private ImmutableViewportMetrics getValidViewportMetrics( ImmutableViewportMetrics viewportMetrics) { /* First, we adjust the zoom factor so that we can make no overscrolled area visible. */ float zoomFactor = viewportMetrics.zoomFactor; RectF pageRect = viewportMetrics.getPageRect(); RectF viewport = viewportMetrics.getViewport(); float focusX = viewport.width() / 2.0f; float focusY = viewport.height() / 2.0f; float minZoomFactor = 0.0f; float maxZoomFactor = MAX_ZOOM; ZoomConstraints constraints = mTarget.getZoomConstraints(); if (constraints.getMinZoom() > 0) minZoomFactor = constraints.getMinZoom(); if (constraints.getMaxZoom() > 0) maxZoomFactor = constraints.getMaxZoom(); if (!constraints.getAllowZoom()) { // If allowZoom is false, clamp to the default zoom level. maxZoomFactor = minZoomFactor = constraints.getDefaultZoom(); } // Ensure minZoomFactor keeps the page at least as big as the viewport. if (pageRect.width() > 0) { float scaleFactor = viewport.width() / pageRect.width(); minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); if (viewport.width() > pageRect.width()) focusX = 0.0f; } if (pageRect.height() > 0) { float scaleFactor = viewport.height() / pageRect.height(); minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); if (viewport.height() > pageRect.height()) focusY = 0.0f; } maxZoomFactor = Math.max(maxZoomFactor, minZoomFactor); if (zoomFactor < minZoomFactor) { // if one (or both) of the page dimensions is smaller than the viewport, // zoom using the top/left as the focus on that axis. this prevents the // scenario where, if both dimensions are smaller than the viewport, but // by different scale factors, we end up scrolled to the end on one axis // after applying the scale PointF center = new PointF(focusX, focusY); viewportMetrics = viewportMetrics.scaleTo(minZoomFactor, center); } else if (zoomFactor > maxZoomFactor) { PointF center = new PointF(viewport.width() / 2.0f, viewport.height() / 2.0f); viewportMetrics = viewportMetrics.scaleTo(maxZoomFactor, center); } /* Now we pan to the right origin. */ viewportMetrics = viewportMetrics.clamp(); return viewportMetrics; }
/** * Zoom to a specified rect IN CSS PIXELS. * * <p>While we usually use device pixels, @zoomToRect must be specified in CSS pixels. */ private boolean animatedZoomTo(RectF zoomToRect) { setState(PanZoomState.ANIMATED_ZOOM); final float startZoom = getMetrics().zoomFactor; RectF viewport = getMetrics().getViewport(); // 1. adjust the aspect ratio of zoomToRect to match that of the current viewport, // enlarging as necessary (if it gets too big, it will get shrunk in the next step). // while enlarging make sure we enlarge equally on both sides to keep the target rect // centered. float targetRatio = viewport.width() / viewport.height(); float rectRatio = zoomToRect.width() / zoomToRect.height(); if (FloatUtils.fuzzyEquals(targetRatio, rectRatio)) { // all good, do nothing } else if (targetRatio < rectRatio) { // need to increase zoomToRect height float newHeight = zoomToRect.width() / targetRatio; zoomToRect.top -= (newHeight - zoomToRect.height()) / 2; zoomToRect.bottom = zoomToRect.top + newHeight; } else { // targetRatio > rectRatio) { // need to increase zoomToRect width float newWidth = targetRatio * zoomToRect.height(); zoomToRect.left -= (newWidth - zoomToRect.width()) / 2; zoomToRect.right = zoomToRect.left + newWidth; } float finalZoom = viewport.width() / zoomToRect.width(); ImmutableViewportMetrics finalMetrics = getMetrics(); finalMetrics = finalMetrics.setViewportOrigin( zoomToRect.left * finalMetrics.zoomFactor, zoomToRect.top * finalMetrics.zoomFactor); finalMetrics = finalMetrics.scaleTo(finalZoom, new PointF(0.0f, 0.0f)); // 2. now run getValidViewportMetrics on it, so that the target viewport is // clamped down to prevent overscroll, over-zoom, and other bad conditions. finalMetrics = getValidViewportMetrics(finalMetrics); bounce(finalMetrics); return true; }
/** * Scales the viewport, keeping the given focus point in the same place before and after the scale * operation. You must hold the monitor while calling this. */ private void scaleWithFocus(float zoomFactor, PointF focus) { ImmutableViewportMetrics viewportMetrics = getMetrics(); viewportMetrics = viewportMetrics.scaleTo(zoomFactor, focus); mTarget.setViewportMetrics(viewportMetrics); }