pts[0] = point.x; pts[1] = point.y; invertedMatrix.mapPoints(pts); point.x = pts[0]; point.y = pts[1]; ObjectRelation.addRelation(this, point); } /** Map rectangle from model to screen coordinates **/ public void mapRect(RectF rect){ matrix.mapRect(rect); ObjectRelation.addRelation(this, rect); } /** Map rectangle from screen to model coordinates **/ public void unmapRect(RectF rect){ matrix.invert(invertedMatrix); invertedMatrix.mapRect(rect); ObjectRelation.addRelation(this, rect); } /** Setup view rect and content size * @param updateMatrix **/ public void setViewRect(float newContentWidth, float newContentHeight, RectF newDisplayRect){ contentWidth = newContentWidth; contentHeight = newContentHeight; if(displayRect!=null){ matrix.postTranslate((newDisplayRect.width()-displayRect.width())/2, (newDisplayRect.height()-displayRect.height())/2); } displayRect = newDisplayRect; // calculate zoom bounds maxScale = 2.0f * density; minScale = Math.min(displayRect.width()/contentWidth, displayRect.height()/contentHeight); adjustScale(); adjustPan(); listener.setPositionAndScaleMatrix(matrix); ObjectRelation.addRelation(this, newDisplayRect); } public boolean onMultiTouchEvent(MotionEvent rawEvent) { ObjectRelation.addRelation(this, rawEvent); if(mode == MODE_ANIMATION){ return false; } MotionEventWrapper event = MotionEventWrapper.create(rawEvent); if(!initialized){ matrix.set(listener.getPositionAndScaleMatrix()); initialized = true; } int action = event.getAction(); boolean handled = true; if(action == MotionEvent.ACTION_DOWN){ handled = doActionDown(event); }else if (action == MotionEventWrapper.ACTION_POINTER_DOWN){ handled = doActionPointerDown(event); }else if(action == MotionEvent.ACTION_UP || action == MotionEventWrapper.ACTION_POINTER_UP){ handled = doActionUp(event); }else if(action == MotionEvent.ACTION_CANCEL ){ handled = doActionCancel(event); }else if(action == MotionEvent.ACTION_MOVE){ handled = doActionMove(event); } listener.setPositionAndScaleMatrix(matrix); return handled; } private boolean doActionDown(MotionEventWrapper event){ if (!scroller.isFinished()) { scroller.abortAnimation(); setControllerMode(MODE_DRAG_START); } else { setControllerMode(MODE_INIT); } if (mode == MODE_INIT) { privateHandler.sendEmptyMessageDelayed(MSG_SWITCH_TO_SHORTPRESS, ViewConfiguration.getTapTimeout()); } velocityTracker = VelocityTracker.obtain(); savedMatrix.set(matrix); touchStartPoint.set(event.getX(), event.getY()); touchStartTime = event.getEventTime(); ObjectRelation.addRelation(this, event); return true; } private boolean doActionPointerDown(MotionEventWrapper event){ zoomBase = distance(event); //Log.d(TAG, "oldDist=" + zoomBase); if (zoomBase > 10f) { if (!scroller.isFinished()) { scroller.abortAnimation(); } savedMatrix.set(matrix); float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); zoomCenter.set(x / 2, y / 2); setControllerMode( MODE_ZOOM ); } ObjectRelation.addRelation(this, event); return true; } private boolean doActionMove(MotionEventWrapper event){ ObjectRelation.addRelation(this, event); if (mode == MODE_NONE || mode == MODE_LONGPRESS_START) { // no dragging during scroll zoom animation or while long press is not released return false; } else if (mode == MODE_ZOOM) { float newDist = distance(event); if (newDist > 10f) { matrix.set(savedMatrix); float scale = newDist / zoomBase; matrix.getValues(matrixValues); float currentScale = matrixValues[Matrix.MSCALE_X]; // limit zoom if (scale * currentScale > maxScale) { scale = maxScale / currentScale; } else if (scale * currentScale < minScale) { scale = minScale / currentScale; } matrix.postScale(scale, scale, zoomCenter.x, zoomCenter.y); adjustPan(); } return true; } velocityTracker.addMovement(event.getEvent()); if (mode != MODE_DRAG) { int deltaX = (int) (touchStartPoint.x - event.getX()); int deltaY = (int) (touchStartPoint.y - event.getY()); if ((deltaX * deltaX + deltaY * deltaY) < touchSlopSquare) { return false; } if (mode == MODE_SHORTPRESS_MODE || mode == MODE_SHORTPRESS_START) { privateHandler.removeMessages(MSG_SWITCH_TO_LONGPRESS); } else if (mode == MODE_INIT) { privateHandler.removeMessages(MSG_SWITCH_TO_SHORTPRESS); } setControllerMode(MODE_DRAG); } matrix.set(savedMatrix); float dx = event.getX() - touchStartPoint.x; float dy = event.getY() - touchStartPoint.y; matrix.postTranslate(dx, dy); adjustPan(); return true; } private boolean doActionUp(MotionEventWrapper event){ ObjectRelation.addRelation(this, event); switch (mode) { case MODE_INIT: // tap case MODE_SHORTPRESS_START: case MODE_SHORTPRESS_MODE: privateHandler.removeMessages(MSG_SWITCH_TO_SHORTPRESS); privateHandler.removeMessages(MSG_SWITCH_TO_LONGPRESS); if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } setControllerMode(MODE_NONE); performClick(); return true; case MODE_LONGPRESS_START: // do nothing break; case MODE_DRAG: case MODE_DRAG_START: // if the user waits a while w/o moving before the // up, we don't want to do a fling if ((event.getEventTime() - touchStartTime) <= MIN_FLING_TIME) { velocityTracker.addMovement(event.getEvent()); velocityTracker.computeCurrentVelocity(1000); matrix.getValues(matrixValues); float currentY = matrixValues[Matrix.MTRANS_Y]; float currentX = matrixValues[Matrix.MTRANS_X]; float currentScale = matrixValues[Matrix.MSCALE_X]; float currentHeight = contentHeight * currentScale; float currentWidth = contentWidth * currentScale; int vx = (int) -velocityTracker.getXVelocity() / 2; int vy = (int) -velocityTracker.getYVelocity() / 2; int maxX = (int) Math.max(currentWidth - displayRect.width(), 0); int maxY = (int) Math.max(currentHeight - displayRect.height(), 0); scroller.fling((int) -currentX, (int) -currentY, vx, vy, 0, maxX, 0, maxY); privateHandler.sendEmptyMessage(MSG_PROCESS_FLING); break; } break; case MODE_ZOOM: // ??? case MODE_NONE: // do nothing } if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } setControllerMode(MODE_NONE); return true; } private boolean doActionCancel(MotionEventWrapper event){ privateHandler.removeMessages(MSG_SWITCH_TO_SHORTPRESS); privateHandler.removeMessages(MSG_SWITCH_TO_LONGPRESS); setControllerMode( MODE_NONE ); ObjectRelation.addRelation(this, event); return true; } /*package*/ void setControllerMode(int newMode){ boolean fireUpdate = mode != newMode; mode = newMode; if(fireUpdate){ listener.onTouchModeChanged(newMode); } } public int getControllerMode(){ return mode; } /** adjust map position to prevent zoom to outside of map **/ private void adjustScale() { matrix.getValues(matrixValues); float currentScale = matrixValues[Matrix.MSCALE_X]; if(currentScale<minScale){ matrix.setScale(minScale, minScale); } } /** adjust map position to prevent pan to outside of map **/ private void adjustPan(){ matrix.getValues(matrixValues); float currentY = matrixValues[Matrix.MTRANS_Y]; float currentX = matrixValues[Matrix.MTRANS_X]; float currentScale = matrixValues[Matrix.MSCALE_X]; float currentHeight = contentHeight * currentScale; float currentWidth = contentWidth * currentScale; float newX = currentX; float newY = currentY; RectF drawingRect = new RectF(newX, newY, newX + currentWidth, newY + currentHeight); float diffUp = Math.min(displayRect.bottom - drawingRect.bottom, displayRect.top - drawingRect.top); float diffDown = Math.max(displayRect.bottom - drawingRect.bottom, displayRect.top - drawingRect.top); float diffLeft = Math.min(displayRect.left - drawingRect.left, displayRect.right - drawingRect.right); float diffRight = Math.max(displayRect.left - drawingRect.left, displayRect.right - drawingRect.right); float dx=0, dy=0; if (diffUp > 0) { dy += diffUp; } if (diffDown < 0) { dy += diffDown; } if (diffLeft > 0) { dx += diffLeft; } if (diffRight < 0) { dx += diffRight; } if(currentWidth<displayRect.width()){ dx = -currentX + (displayRect.width() - currentWidth)/2; } if(currentHeight<displayRect.height()){ dy = -currentY + (displayRect.height() - currentHeight)/2; } matrix.postTranslate(dx, dy); } /** Determine the distance between the first two fingers */ private float distance(MotionEventWrapper event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); ObjectRelation.addRelation(this, event); return FloatMath.sqrt(x * x + y * y); } boolean computeScroll() { boolean more = scroller.computeScrollOffset(); if (more) { float x = scroller.getCurrX(); float y = scroller.getCurrY(); matrix.getValues(matrixValues); float currentY = -matrixValues[Matrix.MTRANS_Y]; float currentX = -matrixValues[Matrix.MTRANS_X]; float dx = currentX - x; float dy = currentY - y; matrix.postTranslate(dx, dy); adjustPan(); listener.setPositionAndScaleMatrix(matrix); } return more; } boolean computeAnimation(){ if(mode == MODE_ANIMATION){ animationInterpolator.next(); if(animationInterpolator.hasScale()){ float scale = animationInterpolator.getScale() / getScale(); matrix.postScale(scale, scale); } if(animationInterpolator.hasScroll()){ PointF newCenter = animationInterpolator.getPoint(); mapPoint(newCenter); float dx = newCenter.x - displayRect.width()/2; float dy = newCenter.y - displayRect.height()/2; matrix.postTranslate(-dx, -dy); } adjustScale(); adjustPan(); listener.setPositionAndScaleMatrix(matrix); return animationInterpolator.more(); } return false; } public PointF getScreenTouchPoint(){ return new PointF(touchStartPoint.x, touchStartPoint.y); } /** Return last touch point in model coordinates **/ public PointF getTouchPoint(){ PointF p = new PointF(); p.set(touchStartPoint); unmapPoint(p); //Log.w(TAG,"point=" + touchStartPoint.x + "," + touchStartPoint.y); return p; } public float getScale(){ matrix.getValues(matrixValues); return matrixValues[Matrix.MSCALE_X]; } public float getTouchRadius(){ return touchSlopSquare; } public float getPositionAndScale(PointF position) { matrix.getValues(matrixValues); float scale = matrixValues[Matrix.MSCALE_X]; if(position!=null){ position.set(-matrixValues[Matrix.MTRANS_X]/scale, -matrixValues[Matrix.MTRANS_Y]/scale); } ObjectRelation.addRelation(this, position); return scale; } public void setPositionAndScale(PointF position, float scale) { matrix.setScale(scale, scale); matrix.postTranslate(-position.x * scale, -position.y * scale); adjustScale(); adjustPan(); listener.setPositionAndScaleMatrix(matrix); ObjectRelation.addRelation(this, position); } public float getMaxScale() { return maxScale; } public float getMinScale() { return minScale; } public void performLongClick() { listener.onPerformLongClick(getTouchPoint()); } public void performClick() { listener.onPerformClick(getTouchPoint()); }