Пинч-масштабирование с помощью ScrollView: обновление поведения прокрутки при изменении размера дочернего элемента

У меня есть пользовательский класс, который я расширил из ScrollView . То, что я хочу сделать, – реализовать масштабирование с этим классом, с помощью которого я хочу масштабировать ScrollView и прокручивать увеличенный контент по мере необходимости. В принципе, мне нужно то же поведение, что и класс iOS для UIScrollView (это SHAME, что Android не предоставляет встроенной функции масштабирования, такой как iOS, и мы должны писать все с нуля). У меня есть ScaleGestureListener внутри моего класса, и я обновляю размер макета дочернего представления внутри ScrollView , когда я получаю onScale события: я onScale ширину и высоту макета с коэффициентом масштабирования, который я получаю от ScaleGestureListener . Меня беспокоит, что ScrollView не обновляет свои полосы прокрутки в соответствии с новыми измерениями своего ребенка. Например, если ширина дочернего элемента становится больше, чем у ScrollView , она не будет показывать горизонтальную полосу прокрутки. Я вызываю invalidate и requestLayout как в представлении прокрутки, так и в своем дочернем requestLayout , но они не requestLayout .

Ниже приводится код:

 public class PinchZoomScrollView extends ScrollView{ private ScaleGestureDetector mScaleDetector;// = new ScaleGestureDetector(context, new ScaleListener()); private float mScaleFactor = 1.0f; private float referenceTextSize; private static final float MIN_ZOOM_AMOUNT = 1.0f; private static final float MAX_ZOOM_AMOUNT = 2.0f; private float refChildWidth; private float refChildHeight; private boolean shownForTheFirstTime = true; //private View content; private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { private WeakReference<PinchZoomScrollView> containerRef; public ScaleListener(PinchZoomScrollView container) { super(); containerRef = new WeakReference<PinchZoomScrollView>(container); } @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); // Don't let the object get too small or too large. mScaleFactor = Math.max(MIN_ZOOM_AMOUNT, Math.min(mScaleFactor, MAX_ZOOM_AMOUNT)); //Get Child object View content = containerRef.get().getChildAt(0); if(shownForTheFirstTime) { shownForTheFirstTime = false; refChildWidth = content.getWidth(); refChildHeight = content.getHeight(); } LayoutParams params = new LayoutParams(Math.round(mScaleFactor*refChildWidth), Math.round(mScaleFactor*refChildHeight)); content.setLayoutParams(params); System.out.println("this.width="+containerRef.get().getWidth()); System.out.println("content.width="+Math.round(mScaleFactor*refChildWidth)); content.invalidate(); content.requestLayout(); containerRef.get().updateViewLayout(content, params); containerRef.get().invalidate(); containerRef.get().requestLayout(); return true; } } public PinchZoomScrollView(Context context) { super(context); mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this)); } public PinchZoomScrollView(Context context, AttributeSet attrs) { super(context,attrs); mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this)); } public PinchZoomScrollView(Context context, AttributeSet attrs, int defStyle) { super(context,attrs,defStyle); mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this)); } @Override public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); mScaleDetector.onTouchEvent(ev); return true; } } 

Короче говоря, мне нужен ScrollView для обновления своих полос прокрутки в соответствии с обновленными размерами его ребенка, каждый раз, когда пользователь делает снимок увеличения и уменьшения.

заранее спасибо

Вот решение:

  1. Вместо того, чтобы расширять ScrollView, я написал TwoDimensionScrollView, расширяющий FrameLayout, который представляет собой комбинацию ScrollView и HorizontalScrollView, так что он может отображать как горизонтальные, так и вертикальные полосы прокрутки, даже если ребенок масштабируется. Также я не использовал детектор жестов. Вместо этого я реализую свое собственное масштабирование.

  2. Чтобы обновить полосы прокрутки, перепишите следующие функции:

     @Override protected int computeVerticalScrollRange() { final int count = getChildCount(); final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop(); if (count == 0) { return contentHeight; } @Override protected int computeVerticalScrollOffset() { int result = Math.max(0, super.computeVerticalScrollOffset()); return result - getScrollVerticalMin(); } @Override protected int computeHorizontalScrollRange() { final int count = getChildCount(); final int contentWidth = getWidth() - getPaddingLeft() - getPaddingRight(); if (count == 0) { return contentWidth; } int scrollRange = getChildAt(0).getRight(); final int scrollX = getScrollX(); final int overscrollRight = Math.max(0, scrollRange - contentWidth); if (scrollX < 0) { scrollRange -= scrollX; } else if (scrollX > overscrollRight) { scrollRange += scrollX - overscrollRight; } return (int) (scrollRange * mScaleFactor + getScrollHorizontalMin()); } @Override protected int computeHorizontalScrollOffset() { int result = Math.max(0, super.computeHorizontalScrollOffset()); return result - getScrollHorizontalMin(); } @Override protected int computeVerticalScrollExtent() { return getHeight() + getScrollVerticalMin(); } @Override protected int computeHorizontalScrollExtent() { return getWidth() + getScrollHorizontalMin(); } private int getScrollHorizontalMin() { if (this.getChildCount() < 1) return 0; View child = this.getChildAt(0); return (int) ((1 - mScaleFactor) * child.getPivotX()); } private int getScrollVerticalMin() { if (this.getChildCount() < 1) return 0; View child = this.getChildAt(0); return (int) ((1 - mScaleFactor) * child.getPivotY()); } private int getScrollVerticalRange() { int scrollRange = 0; if (getChildCount() > 0) { View child = getChildAt(0); scrollRange = (int) Math.max(0, child.getHeight() * mScaleFactor - (getHeight() - getPaddingBottom() - getPaddingTop()) + getScrollVerticalMin()); } return scrollRange; } private int getScrollHorizontalRange() { int scrollRange = 0; if (getChildCount() > 0) { View child = getChildAt(0); scrollRange = (int) Math.max(0, child.getWidth() * mScaleFactor - (getWidth() - getPaddingRight() - getPaddingLeft()) + getScrollHorizontalMin()); } return scrollRange; } 
  3. Вам также нужно найти весь код, вызывающий SpringBack () и fling (), а затем установить значение min для x и y, используя вышеуказанные функции getScrollHorizontalMin () и getScrollVerticalMin () вместо 0