@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (panEnabled && readySent && vTranslate != null && e1 != null && e2 != null && (Math.abs(e1.getX() - e2.getX()) > 50 || Math.abs(e1.getY() - e2.getY()) > 50) && (Math.abs(velocityX) > 500 || Math.abs(velocityY) > 500) && !isZooming) { PointF vTranslateEnd = new PointF(vTranslate.x + (velocityX * 0.25f), vTranslate.y + (velocityY * 0.25f)); float sCenterXEnd = ((getWidth()/2) - vTranslateEnd.x)/scale; float sCenterYEnd = ((getHeight()/2) - vTranslateEnd.y)/scale; new AnimationBuilder(new PointF(sCenterXEnd, sCenterYEnd)).withEasing(EASE_OUT_QUAD).withPanLimited(false).withOrigin(ORIGIN_FLING).start(); return true; } return super.onFling(e1, e2, velocityX, velocityY); }
/** * Returns the source point at the center of the view. * @return the source coordinates current at the center of the view. */ @Nullable public final PointF getCenter() { int mX = getWidth()/2; int mY = getHeight()/2; return viewToSourceCoord(mX, mY); }
/** * Find the area of the source file that is currently visible on screen, taking into account the * current scale, translation, orientation and clipped region. This is a convenience method; see * {@link #viewToFileRect(Rect, Rect)}. * @param fRect rectangle instance to which the result will be written. Re-use for efficiency. */ public void visibleFileRect(Rect fRect) { if (vTranslate == null || !readySent) { return; } fRect.set(0, 0, getWidth(), getHeight()); viewToFileRect(fRect, fRect); }
/** * Calculate how much further the image can be panned in each direction. The results are set on * the supplied {@link RectF} and expressed as screen pixels. For example, if the image cannot be * panned any further towards the left, the value of {@link RectF#left} will be set to 0. * @param vTarget target object for results. Re-use for efficiency. */ public final void getPanRemaining(RectF vTarget) { if (!isReady()) { return; } float scaleWidth = scale * sWidth(); float scaleHeight = scale * sHeight(); if (panLimit == PAN_LIMIT_CENTER) { vTarget.top = Math.max(0, -(vTranslate.y - (getHeight() / 2))); vTarget.left = Math.max(0, -(vTranslate.x - (getWidth() / 2))); vTarget.bottom = Math.max(0, vTranslate.y - ((getHeight() / 2) - scaleHeight)); vTarget.right = Math.max(0, vTranslate.x - ((getWidth() / 2) - scaleWidth)); } else if (panLimit == PAN_LIMIT_OUTSIDE) { vTarget.top = Math.max(0, -(vTranslate.y - getHeight())); vTarget.left = Math.max(0, -(vTranslate.x - getWidth())); vTarget.bottom = Math.max(0, vTranslate.y + scaleHeight); vTarget.right = Math.max(0, vTranslate.x + scaleWidth); } else { vTarget.top = Math.max(0, -vTranslate.y); vTarget.left = Math.max(0, -vTranslate.x); vTarget.bottom = Math.max(0, (scaleHeight + vTranslate.y) - getHeight()); vTarget.right = Math.max(0, (scaleWidth + vTranslate.x) - getWidth()); } }
/** * Sets scale and translate ready for the next draw. */ private void preDraw() { if (getWidth() == 0 || getHeight() == 0 || sWidth <= 0 || sHeight <= 0) { return; } // If waiting to translate to new center position, set translate now if (sPendingCenter != null && pendingScale != null) { scale = pendingScale; if (vTranslate == null) { vTranslate = new PointF(); } vTranslate.x = (getWidth()/2) - (scale * sPendingCenter.x); vTranslate.y = (getHeight()/2) - (scale * sPendingCenter.y); sPendingCenter = null; pendingScale = null; fitToBounds(true); refreshRequiredTiles(true); } // On first display of base image set up position, and in other cases make sure scale is correct. fitToBounds(false); }
/** * Given a requested source center and scale, calculate what the actual center will have to be to keep the image in * pan limits, keeping the requested center as near to the middle of the screen as allowed. */ @NonNull private PointF limitedSCenter(float sCenterX, float sCenterY, float scale, @NonNull PointF sTarget) { PointF vTranslate = vTranslateForSCenter(sCenterX, sCenterY, scale); int vxCenter = getPaddingLeft() + (getWidth() - getPaddingRight() - getPaddingLeft())/2; int vyCenter = getPaddingTop() + (getHeight() - getPaddingBottom() - getPaddingTop())/2; float sx = (vxCenter - vTranslate.x)/scale; float sy = (vyCenter - vTranslate.y)/scale; sTarget.set(sx, sy); return sTarget; }
/** * Returns the minimum allowed scale. */ private float minScale() { int vPadding = getPaddingBottom() + getPaddingTop(); int hPadding = getPaddingLeft() + getPaddingRight(); if (minimumScaleType == SCALE_TYPE_CENTER_CROP || minimumScaleType == SCALE_TYPE_START) { return Math.max((getWidth() - hPadding) / (float) sWidth(), (getHeight() - vPadding) / (float) sHeight()); } else if (minimumScaleType == SCALE_TYPE_CUSTOM && minScale > 0) { return minScale; } else { return Math.min((getWidth() - hPadding) / (float) sWidth(), (getHeight() - vPadding) / (float) sHeight()); } }
vTranslate.y = Math.max(vTranslate.y, getHeight()/2 - scaleHeight); } else if (center) { vTranslate.x = Math.max(vTranslate.x, getWidth() - scaleWidth); vTranslate.y = Math.max(vTranslate.y, getHeight() - scaleHeight); } else { vTranslate.x = Math.max(vTranslate.x, -scaleWidth); if (panLimit == PAN_LIMIT_CENTER && isReady()) { maxTx = Math.max(0, getWidth()/2); maxTy = Math.max(0, getHeight()/2); } else if (center) { maxTx = Math.max(0, (getWidth() - scaleWidth) * xPaddingRatio); maxTy = Math.max(0, (getHeight() - scaleHeight) * yPaddingRatio); } else { maxTx = Math.max(0, getWidth()); maxTy = Math.max(0, getHeight());
/** * Get the translation required to place a given source coordinate at the center of the screen, with the center * adjusted for asymmetric padding. Accepts the desired scale as an argument, so this is independent of current * translate and scale. The result is fitted to bounds, putting the image point as near to the screen center as permitted. */ @NonNull private PointF vTranslateForSCenter(float sCenterX, float sCenterY, float scale) { int vxCenter = getPaddingLeft() + (getWidth() - getPaddingRight() - getPaddingLeft())/2; int vyCenter = getPaddingTop() + (getHeight() - getPaddingBottom() - getPaddingTop())/2; if (satTemp == null) { satTemp = new ScaleAndTranslate(0, new PointF(0, 0)); } satTemp.scale = scale; satTemp.vTranslate.set(vxCenter - (sCenterX * scale), vyCenter - (sCenterY * scale)); fitToBounds(true, satTemp); return satTemp.vTranslate; }
/** * Determine whether tile is visible. */ private boolean tileVisible(Tile tile) { float sVisLeft = viewToSourceX(0), sVisRight = viewToSourceX(getWidth()), sVisTop = viewToSourceY(0), sVisBottom = viewToSourceY(getHeight()); return !(sVisLeft > tile.sRect.right || tile.sRect.left > sVisRight || sVisTop > tile.sRect.bottom || tile.sRect.top > sVisBottom); }
subTileWidth = sTileWidth/sampleSize; while (subTileHeight + yTiles + 1 > maxTileDimensions.y || (subTileHeight > getHeight() * 1.25 && sampleSize < fullImageSampleSize)) { yTiles += 1; sTileHeight = sHeight()/yTiles;
vTranslate.x = vCenterEndX - vLeftNow; vTranslate.y = vCenterEndY - vTopNow; if ((previousScale * sHeight() < getHeight() && scale * sHeight() >= getHeight()) || (previousScale * sWidth() < getWidth() && scale * sWidth() >= getWidth())) { fitToBounds(true); vCenterStart.set(vCenterEndX, vCenterEndY); vTranslate.y = (getHeight()/2) - (scale * sRequestedCenter.y); } else { vTranslate.y = (getHeight()/2) - (scale * (sHeight()/2)); vTranslate.x = vCenterStart.x - vLeftNow; vTranslate.y = vCenterStart.y - vTopNow; if ((previousScale * sHeight() < getHeight() && scale * sHeight() >= getHeight()) || (previousScale * sWidth() < getWidth() && scale * sWidth() >= getWidth())) { fitToBounds(true); vCenterStart.set(sourceToViewCoord(quickScaleSCenter)); vTranslate.y = (getHeight()/2) - (scale * sRequestedCenter.y); } else { vTranslate.y = (getHeight()/2) - (scale * (sHeight()/2));
/** * Check whether view and image dimensions are known and either a preview, full size image or * base layer tiles are loaded. First time, send ready event to listener. The next draw will * display an image. */ private boolean checkReady() { boolean ready = getWidth() > 0 && getHeight() > 0 && sWidth > 0 && sHeight > 0 && (bitmap != null || isBaseLayerReady()); if (!readySent && ready) { preDraw(); readySent = true; onReady(); if (onImageEventListener != null) { onImageEventListener.onReady(); } } return ready; }
int vyCenter = getPaddingTop() + (getHeight() - getPaddingBottom() - getPaddingTop())/2; float targetScale = limitedScale(this.targetScale); PointF targetSCenter = panLimited ? limitedSCenter(this.targetSCenter.x, this.targetSCenter.y, targetScale, new PointF()) : this.targetSCenter;
/** * Enable or disable pan gesture detection. Disabling pan causes the image to be centered. Pan * can still be changed from code. * @param panEnabled true to enable panning, false to disable. */ public final void setPanEnabled(boolean panEnabled) { this.panEnabled = panEnabled; if (!panEnabled && vTranslate != null) { vTranslate.x = (getWidth()/2) - (scale * (sWidth()/2)); vTranslate.y = (getHeight()/2) - (scale * (sHeight()/2)); if (isReady()) { refreshRequiredTiles(true); invalidate(); } } }
this.sOrientation = sOrientation; checkReady(); if (!checkImageLoaded() && maxTileWidth > 0 && maxTileWidth != TILE_SIZE_AUTO && maxTileHeight > 0 && maxTileHeight != TILE_SIZE_AUTO && getWidth() > 0 && getHeight() > 0) { initialiseBaseLayer(new Point(maxTileWidth, maxTileHeight));
int imageHeight = mImageView.getSHeight(); int viewWidth = mImageView.getWidth(); int viewHeight = mImageView.getHeight();
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (panEnabled && readySent && vTranslate != null && e1 != null && e2 != null && (Math.abs(e1.getX() - e2.getX()) > 50 || Math.abs(e1.getY() - e2.getY()) > 50) && (Math.abs(velocityX) > 500 || Math.abs(velocityY) > 500) && !isZooming) { PointF vTranslateEnd = new PointF(vTranslate.x + (velocityX * 0.25f), vTranslate.y + (velocityY * 0.25f)); float sCenterXEnd = ((getWidth()/2) - vTranslateEnd.x)/scale; float sCenterYEnd = ((getHeight()/2) - vTranslateEnd.y)/scale; new AnimationBuilder(new PointF(sCenterXEnd, sCenterYEnd)).withEasing(EASE_OUT_QUAD).withPanLimited(false).withOrigin(ORIGIN_FLING).start(); return true; } return super.onFling(e1, e2, velocityX, velocityY); }
/** * Returns the source point at the center of the view. */ public final PointF getCenter() { int mX = getWidth()/2; int mY = getHeight()/2; return viewToSourceCoord(mX, mY); }