/** * Applies state restrictions and notifies {@link OnStateChangeListener} listeners. */ public void updateState() { // Applying zoom patch (needed in case if image size is changed) stateController.applyZoomPatch(state); stateController.applyZoomPatch(prevState); stateController.applyZoomPatch(stateStart); stateController.applyZoomPatch(stateEnd); exitController.applyZoomPatch(); boolean reset = stateController.updateState(state); if (reset) { notifyStateReset(); } else { notifyStateUpdated(); } }
protected void applyState(State state) { float size = origSize * state.getZoom(); float maxSize = origSize * controller.getStateController().getMaxZoom(state); size = Math.max(origSize, Math.min(size, maxSize)); // Bigger text size steps for smoother scaling size = Math.round(size); if (!State.equals(this.size, size)) { this.size = size; super.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); } }
/** * @param out Output movement area rectangle * @param state Current state * @deprecated User {@link #getMovementArea(State, RectF)} instead. */ @SuppressWarnings("unused") // Public API @Deprecated public void getEffectiveMovementArea(RectF out, State state) { getMovementArea(state, out); }
private boolean isZoomedOut() { final State state = controller.getState(); final float minZoom = controller.getStateController().getMinZoom(state); return State.compare(state.getZoom(), minZoom) <= 0; }
isStateChangedDuringTouch = false; stateController.restrictStateBounds( state, prevState, pivotX, pivotY, true, true, false); State restrictedState = stateController.restrictStateBoundsCopy( state, prevState, pivotX, pivotY, true, false, true); animateStateTo(restrictedState, false);
zoom = applyZoomResilience(zoom, prevState.getZoom(), minZoom, maxZoom, extraZoom); newX = applyTranslationResilience(newX, prevState.getX(), tmpRectF.left, tmpRectF.right, extraX); newY = applyTranslationResilience(newY, prevState.getY(), tmpRectF.top, tmpRectF.bottom, extraY);
@Override public void onStateChanged(State state) { // Applying zoom patch (needed in case if image size is changed) toController.getStateController().applyZoomPatch(fromState); toController.getStateController().applyZoomPatch(toState); }
/** * Restricts state's translation and zoom bounds. * * @param state State to be restricted * @param prevState Previous state to calculate overscroll and overzoom (optional) * @param pivotX Pivot's X coordinate * @param pivotY Pivot's Y coordinate * @param allowOverscroll Whether overscroll is allowed * @param allowOverzoom Whether overzoom is allowed * @param restrictRotation Whether rotation should be restricted to a nearest N*90 angle * @return End state to animate changes or null if no changes are required. */ @SuppressWarnings("SameParameterValue") // Using same method params as in restrictStateBounds @Nullable State restrictStateBoundsCopy(State state, State prevState, float pivotX, float pivotY, boolean allowOverscroll, boolean allowOverzoom, boolean restrictRotation) { tmpState.set(state); boolean changed = restrictStateBounds(tmpState, prevState, pivotX, pivotY, allowOverscroll, allowOverzoom, restrictRotation); return changed ? tmpState.copy() : null; }
public GestureController(@NonNull View view) { final Context context = view.getContext(); targetView = view; settings = new Settings(); stateController = new StateController(settings); animationEngine = new LocalAnimationEngine(view); InternalGesturesListener internalListener = new InternalGesturesListener(); gestureDetector = new GestureDetector(context, internalListener); scaleDetector = new ScaleGestureDetectorFixed(context, internalListener); rotateDetector = new RotationGestureDetector(context, internalListener); exitController = new ExitController(view, this); flingScroller = new OverScroller(context); stateScroller = new FloatScroller(); flingBounds = new MovementBounds(settings); final ViewConfiguration configuration = ViewConfiguration.get(context); touchSlop = configuration.getScaledTouchSlop(); minVelocity = configuration.getScaledMinimumFlingVelocity(); maxVelocity = configuration.getScaledMaximumFlingVelocity(); }
/** * Resets to initial state (default position, min zoom level) and notifies * {@link OnStateChangeListener} listeners. * <p> * Should be called when image size is changed. * <p> * See {@link Settings#setImage(int, int)}. */ public void resetState() { stopAllAnimations(); boolean reset = stateController.resetState(state); if (reset) { notifyStateReset(); } else { notifyStateUpdated(); } }
float minZoom = getStateController().getFitZoom(state); float zoomThreshold = minZoom == 0f ? 0f : state.getZoom() / minZoom - 1f;
private void resetImage(boolean animate) { final GestureController controller = imageView.getController(); if (controller.isAnimating()) { return; // Waiting for animation end } if (animate) { final State state = controller.getState().copy(); final PointF pivot = getPivot(); // Restoring initial image zoom and rotation final float minZoom = controller.getStateController().getMinZoom(state); state.zoomTo(minZoom, pivot.x, pivot.y); state.rotateTo(0f, pivot.x, pivot.y); // Animating state changes. Do not forget to make a state's copy prior to any changes. controller.setPivot(pivot.x, pivot.y); controller.animateStateTo(state); } else { // Immediately resetting the state controller.resetState(); } }
public void applyZoomPatch() { // Applying zoom patch (needed in case if image size is changed) initialZoom = controller.getStateController().applyZoomPatch(initialZoom); }
/** * Updates state (or resets state if reset was scheduled, see {@link #resetState(State)}). * * @param state State to be updated * @return {@code true} if state was reset to initial state or {@code false} if state was * updated. */ boolean updateState(State state) { if (isResetRequired) { // Applying initial state state.set(0f, 0f, zoomBounds.set(state).getFitZoom(), 0f); GravityUtils.getImagePosition(state, settings, tmpRect); state.translateTo(tmpRect.left, tmpRect.top); // We can correctly reset state only when we have both image size and viewport size // but there can be a delay before we have all values properly set // (waiting for layout or waiting for image to be loaded) isResetRequired = !settings.hasImageSize() || !settings.hasViewportSize(); return !isResetRequired; } else { // Restricts state's translation and zoom bounds, disallowing overscroll / overzoom. restrictStateBounds(state, state, Float.NaN, Float.NaN, false, false, true); return false; } }
private float scrollBy(@NonNull MotionEvent event, float dx) { if (isSkipViewPager || isViewPagerDisabled) { return dx; } final State state = getState(); getStateController().getMovementArea(state, tmpRectF); float pagerDx = splitPagerScroll(dx, state, tmpRectF); pagerDx = skipPagerMovement(pagerDx, state, tmpRectF); float viewDx = dx - pagerDx; // Applying pager scroll boolean shouldFixViewX = isViewPagerInterceptedScroll && viewPagerX == 0; int actualX = performViewPagerScroll(event, pagerDx); viewPagerX += actualX; if (shouldFixViewX) { // Adding back scroll not handled by ViewPager viewDx += Math.round(pagerDx) - actualX; } // Returning altered scroll left for image return viewDx; }
protected boolean shouldDisallowInterceptTouch(MotionEvent event) { if (exitController.isExitDetected()) { return true; } switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: { // If view can be panned then parent should not intercept touch events. // We should check it on DOWN event since parent may quickly take control over us // in case of a very fast MOVE action. stateController.getMovementArea(state, tmpRectF); final boolean isPannable = State.compare(tmpRectF.width(), 0f) > 0 || State.compare(tmpRectF.height(), 0f) > 0; if (settings.isPanEnabled() && (isPannable || !settings.isRestrictBounds())) { return true; } break; } case MotionEvent.ACTION_POINTER_DOWN: { // If view can be zoomed or rotated then parent should not intercept touch events. return settings.isZoomEnabled() || settings.isRotationEnabled(); } default: } return false; }
private boolean canScroll(float dy) { if (!controller.getSettings().isRestrictBounds()) { return true; } final State state = controller.getState(); controller.getStateController().getMovementArea(state, tmpArea); return (dy > 0f && State.compare(state.getY(), tmpArea.bottom) < 0f) || (dy < 0f && State.compare(state.getY(), tmpArea.top) > 0f); }