PanZoom implements View.OnTouchListener { protected void zoom(final MotionEvent motionEvent) { if (zoom == Zoom.NONE) { return; } final RectF oldFingersRect = getFingersRect(); final RectF newFingersRect = fingerDistance(motionEvent); setFingersRect(newFingersRect); if(oldFingersRect == null || RectFUtils.areIdentical(oldFingersRect, newFingersRect)) { return; } RectF newRect = new RectF(); float scaleX = 1; float scaleY = 1; switch (zoom) { case STRETCH_HORIZONTAL: scaleX = oldFingersRect.width() / getFingersRect().width(); if (!isValidScale(scaleX)) { return; } break; case STRETCH_VERTICAL: scaleY = oldFingersRect.height() / getFingersRect().height(); if (!isValidScale(scaleY)) { return; } break; case STRETCH_BOTH: scaleX = oldFingersRect.width() / getFingersRect().width(); scaleY = oldFingersRect.height() / getFingersRect().height(); if (!isValidScale(scaleX) || !isValidScale(scaleY)) { return; } break; case SCALE: float sc1 = (float) Math.hypot(oldFingersRect.height(), oldFingersRect.width()); float sc2 = (float) Math.hypot(getFingersRect().height(), getFingersRect().width()); float sc = sc1 / sc2; scaleX = sc; scaleY = sc; if (!isValidScale(scaleX) || !isValidScale(scaleY)) { return; } break; } if (EnumSet.of( Zoom.STRETCH_HORIZONTAL, Zoom.STRETCH_BOTH, Zoom.SCALE).contains(zoom)) { calculateZoom(newRect, scaleX, true); adjustDomainBoundary(newRect.left, newRect.right, BoundaryMode.FIXED); } if (EnumSet.of( Zoom.STRETCH_VERTICAL, Zoom.STRETCH_BOTH, Zoom.SCALE).contains(zoom)) { calculateZoom(newRect, scaleY, false); adjustRangeBoundary(newRect.top, newRect.bottom, BoundaryMode.FIXED); } plot.redraw(); } protected  PanZoom(@NonNull XYPlot plot, Pan pan, Zoom zoom); protected  PanZoom(@NonNull XYPlot plot, Pan pan, Zoom zoom, ZoomLimit limit); State getState(); void setState(@NonNull State state); static PanZoom attach(@NonNull XYPlot plot); static PanZoom attach(@NonNull XYPlot plot, @NonNull Pan pan, @NonNull Zoom zoom); static PanZoom attach(@NonNull XYPlot plot, @NonNull Pan pan, @NonNull Zoom zoom, @NonNull ZoomLimit limit); boolean isEnabled(); void setEnabled(boolean enabled); @Override boolean onTouch(final View view, final MotionEvent event); Pan getPan(); void setPan(Pan pan); Zoom getZoom(); void setZoom(Zoom zoom); ZoomLimit getZoomLimit(); void setZoomLimit(ZoomLimit zoomLimit); View.OnTouchListener getDelegate(); void setDelegate(View.OnTouchListener delegate); void reset();  }
@Test public void testZoom() { xyPlot = spy(new InstrumentedXYPlot(getContext())); xyPlot.setDomainBoundaries(0, 100, BoundaryMode.FIXED); xyPlot.setRangeBoundaries(0, 100, BoundaryMode.FIXED); xyPlot.redraw(); PanZoom panZoom = spy(new PanZoom(xyPlot, PanZoom.Pan.BOTH, PanZoom.Zoom.SCALE)); xyPlot.getOuterLimits().set(0, 100, 0, 100); panZoom.setFingersRect(new RectF(0, 0, 20, 20)); InOrder inOrder = inOrder(xyPlot); inOrder.verify(xyPlot).setDomainBoundaries(0, 100, BoundaryMode.FIXED); panZoom.zoom(TestUtils.newPointerDownEvent(0, 0, 40, 40)); inOrder.verify(xyPlot).setDomainBoundaries(25f, 75f, BoundaryMode.FIXED); inOrder.verify(xyPlot).setRangeBoundaries(25f, 75f, BoundaryMode.FIXED); inOrder.verify(xyPlot).redraw(); panZoom.zoom(TestUtils.newPointerDownEvent(0, 0, 80, 80)); inOrder.verify(xyPlot).setDomainBoundaries(37.5f, 62.5f, BoundaryMode.FIXED); inOrder.verify(xyPlot).setRangeBoundaries(37.5f, 62.5f, BoundaryMode.FIXED); inOrder.verify(xyPlot).redraw(); panZoom.zoom(TestUtils.newPointerDownEvent(0, 0, 20, 20)); inOrder.verify(xyPlot).setDomainBoundaries(0f, 100f, BoundaryMode.FIXED); inOrder.verify(xyPlot).setRangeBoundaries(0f, 100f, BoundaryMode.FIXED); inOrder.verify(xyPlot).redraw(); panZoom.zoom(TestUtils.newPointerDownEvent(0, 0, 1, 1)); inOrder.verify(xyPlot).setDomainBoundaries(0f, 100f, BoundaryMode.FIXED); inOrder.verify(xyPlot).setRangeBoundaries(0f, 100f, BoundaryMode.FIXED); inOrder.verify(xyPlot).redraw(); inOrder.verify(xyPlot, never()).redraw(); verify(panZoom, never()).pan(any(MotionEvent.class)); }