2012-09-18 13 views
12

के साथ देखें क्या यह दृश्य देखना संभव है जो क्षैतिज और लंबवत पैन/ड्रैग का समर्थन करता है। उस पर, मैं ज़ूम करने के लिए चुपचाप और ज़ूम करने के लिए दो बार टैप करने में सक्षम होना चाहता हूं। क्या यह दृश्य एंड्रॉइड पर मौजूद है या क्या कोई ऐसा प्रोजेक्ट जानता है जो करता है?क्षैतिज और लंबवत पैन/ड्रैग और पिनच-ज़ूम

इसे और भी कठिन बनाने के लिए, एक अन्य दृश्य (बटन, टेक्स्ट व्यू, वीडियो व्यू, ...) को दृश्य में जोड़ा जाना आवश्यक है। जब पहला/मूल दृश्य ज़ूम इन या ज़ूम किया जाता है, तो सबव्यू (बटन) को माता-पिता के साथ घूमने की आवश्यकता होती है।

मैंने कई समाधानों का प्रयास किया है, लेकिन उनमें से कोई भी विकल्प नहीं है जिसे मैं ढूंढ रहा हूं।

+0

हाय, आप इस प्रश्न के लिए अंतिम समाधान मिला? क्या आप इसे साझा कर सकते हैं? –

+0

यदि आप अपना समाधान साझा कर सकते हैं तो यह अच्छा होगा ... – pawpaw

उत्तर

20

मुझे लगता है कि इसे प्राप्त करने के आप क्या चाहते हैं संभव है, लेकिन वहाँ है, जहाँ तक मैं इसके लिए समाधान में निर्माण के रूप में जानते हैं। आपके प्रश्न के दूसरे भाग से मुझे लगता है कि आप ज़ूम करने योग्य View नहीं चाहते हैं लेकिन ViewGroup जो सभी दृश्यों की सुपर क्लास है जिसमें अन्य दृश्य (उदा। लेआउट) हो सकते हैं।

import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Matrix; 
import android.graphics.Rect; 
import android.view.*; 

public class ZoomableViewGroup extends ViewGroup { 

    private static final int INVALID_POINTER_ID = 1; 
    private int mActivePointerId = INVALID_POINTER_ID; 

    private float mScaleFactor = 1; 
    private ScaleGestureDetector mScaleDetector; 
    private Matrix mScaleMatrix = new Matrix(); 
    private Matrix mScaleMatrixInverse = new Matrix(); 

    private float mPosX; 
    private float mPosY; 
    private Matrix mTranslateMatrix = new Matrix(); 
    private Matrix mTranslateMatrixInverse = new Matrix(); 

    private float mLastTouchX; 
    private float mLastTouchY; 

    private float mFocusY; 

    private float mFocusX; 

    private float[] mInvalidateWorkingArray = new float[6]; 
    private float[] mDispatchTouchEventWorkingArray = new float[2]; 
    private float[] mOnTouchEventWorkingArray = new float[2]; 


    public ZoomableViewGroup(Context context) { 
     super(context); 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
     mTranslateMatrix.setTranslate(0, 0); 
     mScaleMatrix.setScale(1, 1); 
    } 

    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      View child = getChildAt(i); 
      if (child.getVisibility() != GONE) { 
       child.layout(l, t, l+child.getMeasuredWidth(), t + child.getMeasuredHeight()); 
      } 
     } 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      View child = getChildAt(i); 
      if (child.getVisibility() != GONE) { 
       measureChild(child, widthMeasureSpec, heightMeasureSpec); 
      } 
     } 
    } 

    @Override 
    protected void dispatchDraw(Canvas canvas) { 
     canvas.save(); 
     canvas.translate(mPosX, mPosY); 
     canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY); 
     super.dispatchDraw(canvas); 
     canvas.restore(); 
    } 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
     mDispatchTouchEventWorkingArray[0] = ev.getX(); 
     mDispatchTouchEventWorkingArray[1] = ev.getY(); 
     mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray); 
     ev.setLocation(mDispatchTouchEventWorkingArray[0], 
       mDispatchTouchEventWorkingArray[1]); 
     return super.dispatchTouchEvent(ev); 
    } 

    /** 
    * Although the docs say that you shouldn't override this, I decided to do 
    * so because it offers me an easy way to change the invalidated area to my 
    * likening. 
    */ 
    @Override 
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) { 

     mInvalidateWorkingArray[0] = dirty.left; 
     mInvalidateWorkingArray[1] = dirty.top; 
     mInvalidateWorkingArray[2] = dirty.right; 
     mInvalidateWorkingArray[3] = dirty.bottom; 


     mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray); 
     dirty.set(Math.round(mInvalidateWorkingArray[0]), Math.round(mInvalidateWorkingArray[1]), 
       Math.round(mInvalidateWorkingArray[2]), Math.round(mInvalidateWorkingArray[3])); 

     location[0] *= mScaleFactor; 
     location[1] *= mScaleFactor; 
     return super.invalidateChildInParent(location, dirty); 
    } 

    private float[] scaledPointsToScreenPoints(float[] a) { 
     mScaleMatrix.mapPoints(a); 
     mTranslateMatrix.mapPoints(a); 
     return a; 
    } 

    private float[] screenPointsToScaledPoints(float[] a){ 
     mTranslateMatrixInverse.mapPoints(a); 
     mScaleMatrixInverse.mapPoints(a); 
     return a; 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     mOnTouchEventWorkingArray[0] = ev.getX(); 
     mOnTouchEventWorkingArray[1] = ev.getY(); 

     mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray); 

     ev.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]); 
     mScaleDetector.onTouchEvent(ev); 

     final int action = ev.getAction(); 
     switch (action & MotionEvent.ACTION_MASK) { 
      case MotionEvent.ACTION_DOWN: { 
       final float x = ev.getX(); 
       final float y = ev.getY(); 

       mLastTouchX = x; 
       mLastTouchY = y; 

       // Save the ID of this pointer 
       mActivePointerId = ev.getPointerId(0); 
       break; 
      } 

      case MotionEvent.ACTION_MOVE: { 
       // Find the index of the active pointer and fetch its position 
       final int pointerIndex = ev.findPointerIndex(mActivePointerId); 
       final float x = ev.getX(pointerIndex); 
       final float y = ev.getY(pointerIndex); 

       final float dx = x - mLastTouchX; 
       final float dy = y - mLastTouchY; 

       mPosX += dx; 
       mPosY += dy; 
       mTranslateMatrix.preTranslate(dx, dy); 
       mTranslateMatrix.invert(mTranslateMatrixInverse); 

       mLastTouchX = x; 
       mLastTouchY = y; 

       invalidate(); 
       break; 
      } 

      case MotionEvent.ACTION_UP: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 

      case MotionEvent.ACTION_CANCEL: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 

      case MotionEvent.ACTION_POINTER_UP: { 
       // Extract the index of the pointer that left the touch sensor 
       final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
       final int pointerId = ev.getPointerId(pointerIndex); 
       if (pointerId == mActivePointerId) { 
        // This was our active pointer going up. Choose a new 
        // active pointer and adjust accordingly. 
        final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 
        mLastTouchX = ev.getX(newPointerIndex); 
        mLastTouchY = ev.getY(newPointerIndex); 
        mActivePointerId = ev.getPointerId(newPointerIndex); 
       } 
       break; 
      } 
     } 
     return true; 
    } 

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 

     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      mScaleFactor *= detector.getScaleFactor(); 
      if (detector.isInProgress()) { 
       mFocusX = detector.getFocusX(); 
       mFocusY = detector.getFocusY(); 
      } 
      mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); 
      mScaleMatrix.setScale(mScaleFactor, mScaleFactor, 
        mFocusX, mFocusY); 
      mScaleMatrix.invert(mScaleMatrixInverse); 
      invalidate(); 
      requestLayout(); 


      return true; 
     } 
    } 
} 

क्या इस वर्ग, चारों ओर सामग्री खींचने और ज़ूम करने के लिए चुटकी दे रहा है ऐसा करने में सक्षम होना चाहिए, डबल टैप: यहाँ कुछ कोड आप अपने खुद के ViewGroup निर्माण इसमें से अधिकांश this ब्लॉग पोस्ट से आता है से शुरू कर सकता है है ज़ूम करने के लिए अभी संभव नहीं है, लेकिन onTouchEvent विधि में इसे कार्यान्वित करना आसान होना चाहिए।

यदि आपके प्रश्न हैं तो आपके व्यू ग्रुप में बच्चों को लेआउट कैसे करें, मुझे यह video बहुत उपयोगी पाया गया है या यदि आपके पास कोई और प्रश्न है कि एकल विधियां कैसे काम करती हैं या कुछ और टिप्पणियों में पूछने के लिए स्वतंत्र महसूस होती है।

+0

इसमें कुछ त्रुटियां हैं जिनमें आपको व्यू ग्रुप का विस्तार करने की आवश्यकता है और आपने कुछ '}' को याद किया है। – Marche101

+0

@Artjom मुझे आश्चर्य है, क्लिप को ट्रैनफ़्रंट करने के बजाय व्यूग्रुप पर सेटस्केलेक्स/वाई का उपयोग क्यों न करें, स्पर्श बिंदु आदि स्वयं? –

+0

जब दृश्य 1: 1 स्केल तक पहुंचता है तो ज़ूम आउट करना बंद करना संभव है? क्या यह हासिल करना आसान है? – edoardotognoni

0

यह कस्टम दृश्य एंड्रॉयड मानक imageView का एक उपवर्ग (और साथ ही और डबल ज़ूम नल,) है और यह (बहु) स्पर्श पैन और ज़ूम करने के लिए कहते हैं:

https://github.com/sephiroth74/ImageViewZoom

http://blog.sephiroth.it/2011/04/04/imageview-zoom-and-scroll/

यह माइकऑर्टिज़ के टच इमेज व्यू के समान है जिसे आप पहले ही जानते हैं लेकिन कुछ और विशेषताएं जोड़ते हैं।

आप इसे अन्य टेक्स्ट व्यू (ओं) के साथ एक साथ "स्टैक" (एक एंड्रॉइड फ्रेमलाउट या उस तरह कुछ) के अंदर उपयोग कर सकते हैं। (मेरा मतलब है व्यंजनों का ढेर या कार्ड के ढेर की तरह विचारों का एक "ढेर"। दूसरे शब्दों में, जेड अक्ष पर अगले दृश्यों में से एक ढेर दृश्यों का ढेर होता है।)

अपने सभी विचारों को एक साथ चलाना आवश्यक है कि आप एंड्रॉइड जेस्चर (मल्टीटाउच) तंत्र पर नियंत्रण लेते हैं और आवश्यक कोड लिखते हैं। आपके (काफी जटिल) आवश्यकता के लिए कोई उपयोग करने योग्य उपयोग समाधान नहीं है। इस लेख पर एक नज़र डालें:, फिक्स्ड मामूली त्रुटियों के साथ @Artjom जवाब का

http://android-developers.blogspot.it/2010/06/making-sense-of-multitouch.html

13

पोस्ट अर्थात् धनुकोष्ठक, आयात, और ViewGroup विस्तार।

import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Matrix; 
import android.graphics.Rect; 
import android.view.*; 

public class ZoomableViewGroup extends ViewGroup { 

    private static final int INVALID_POINTER_ID = 1; 
    private int mActivePointerId = INVALID_POINTER_ID; 

    private float mScaleFactor = 1; 
    private ScaleGestureDetector mScaleDetector; 
    private Matrix mScaleMatrix = new Matrix(); 
    private Matrix mScaleMatrixInverse = new Matrix(); 

    private float mPosX; 
    private float mPosY; 
    private Matrix mTranslateMatrix = new Matrix(); 
    private Matrix mTranslateMatrixInverse = new Matrix(); 

    private float mLastTouchX; 
    private float mLastTouchY; 

    private float mFocusY; 

    private float mFocusX; 

    private float[] mInvalidateWorkingArray = new float[6]; 
    private float[] mDispatchTouchEventWorkingArray = new float[2]; 
    private float[] mOnTouchEventWorkingArray = new float[2]; 


    public ZoomableViewGroup(Context context) { 
     super(context); 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
     mTranslateMatrix.setTranslate(0, 0); 
     mScaleMatrix.setScale(1, 1); 
    } 

    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      View child = getChildAt(i); 
      if (child.getVisibility() != GONE) { 
       child.layout(l, t, l+child.getMeasuredWidth(), t + child.getMeasuredHeight()); 
      } 
     } 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      View child = getChildAt(i); 
      if (child.getVisibility() != GONE) { 
       measureChild(child, widthMeasureSpec, heightMeasureSpec); 
      } 
     } 
    } 

    @Override 
    protected void dispatchDraw(Canvas canvas) { 
     canvas.save(); 
     canvas.translate(mPosX, mPosY); 
     canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY); 
     super.dispatchDraw(canvas); 
     canvas.restore(); 
    } 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
     mDispatchTouchEventWorkingArray[0] = ev.getX(); 
     mDispatchTouchEventWorkingArray[1] = ev.getY(); 
     mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray); 
     ev.setLocation(mDispatchTouchEventWorkingArray[0], 
       mDispatchTouchEventWorkingArray[1]); 
     return super.dispatchTouchEvent(ev); 
    } 

    /** 
    * Although the docs say that you shouldn't override this, I decided to do 
    * so because it offers me an easy way to change the invalidated area to my 
    * likening. 
    */ 
    @Override 
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) { 

     mInvalidateWorkingArray[0] = dirty.left; 
     mInvalidateWorkingArray[1] = dirty.top; 
     mInvalidateWorkingArray[2] = dirty.right; 
     mInvalidateWorkingArray[3] = dirty.bottom; 


     mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray); 
     dirty.set(Math.round(mInvalidateWorkingArray[0]), Math.round(mInvalidateWorkingArray[1]), 
       Math.round(mInvalidateWorkingArray[2]), Math.round(mInvalidateWorkingArray[3])); 

     location[0] *= mScaleFactor; 
     location[1] *= mScaleFactor; 
     return super.invalidateChildInParent(location, dirty); 
    } 

    private float[] scaledPointsToScreenPoints(float[] a) { 
     mScaleMatrix.mapPoints(a); 
     mTranslateMatrix.mapPoints(a); 
     return a; 
    } 

    private float[] screenPointsToScaledPoints(float[] a){ 
     mTranslateMatrixInverse.mapPoints(a); 
     mScaleMatrixInverse.mapPoints(a); 
     return a; 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     mOnTouchEventWorkingArray[0] = ev.getX(); 
     mOnTouchEventWorkingArray[1] = ev.getY(); 

     mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray); 

     ev.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]); 
     mScaleDetector.onTouchEvent(ev); 

     final int action = ev.getAction(); 
     switch (action & MotionEvent.ACTION_MASK) { 
      case MotionEvent.ACTION_DOWN: { 
       final float x = ev.getX(); 
       final float y = ev.getY(); 

       mLastTouchX = x; 
       mLastTouchY = y; 

       // Save the ID of this pointer 
       mActivePointerId = ev.getPointerId(0); 
       break; 
      } 

      case MotionEvent.ACTION_MOVE: { 
       // Find the index of the active pointer and fetch its position 
       final int pointerIndex = ev.findPointerIndex(mActivePointerId); 
       final float x = ev.getX(pointerIndex); 
       final float y = ev.getY(pointerIndex); 

       final float dx = x - mLastTouchX; 
       final float dy = y - mLastTouchY; 

       mPosX += dx; 
       mPosY += dy; 
       mTranslateMatrix.preTranslate(dx, dy); 
       mTranslateMatrix.invert(mTranslateMatrixInverse); 

       mLastTouchX = x; 
       mLastTouchY = y; 

       invalidate(); 
       break; 
      } 

      case MotionEvent.ACTION_UP: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 

      case MotionEvent.ACTION_CANCEL: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 

      case MotionEvent.ACTION_POINTER_UP: { 
       // Extract the index of the pointer that left the touch sensor 
       final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
       final int pointerId = ev.getPointerId(pointerIndex); 
       if (pointerId == mActivePointerId) { 
        // This was our active pointer going up. Choose a new 
        // active pointer and adjust accordingly. 
        final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 
        mLastTouchX = ev.getX(newPointerIndex); 
        mLastTouchY = ev.getY(newPointerIndex); 
        mActivePointerId = ev.getPointerId(newPointerIndex); 
       } 
       break; 
      } 
     } 
     return true; 
    } 

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 

     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      mScaleFactor *= detector.getScaleFactor(); 
      if (detector.isInProgress()) { 
       mFocusX = detector.getFocusX(); 
       mFocusY = detector.getFocusY(); 
      } 
      mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); 
      mScaleMatrix.setScale(mScaleFactor, mScaleFactor, 
        mFocusX, mFocusY); 
      mScaleMatrix.invert(mScaleMatrixInverse); 
      invalidate(); 
      requestLayout(); 


      return true; 
     } 
    } 
} 
+0

सही समाधान। सलाम। –

+0

ज़ूम करने के बाद ज़ूम करने योग्य दृश्य समूह में एक बच्चा दृश्य जोड़ता है, बच्चे के स्पर्श किए गए निर्देशांक गलत हो जाते हैं। मेरे द्वारा यह कैसे किया जा सकता है ? –

+1

जो मेरे जैसे समस्या का सामना करने के लिए: विधि
'निजी नाव [] screenPointsToScaledPoints का क्रम बदलने के (फ्लोट [] एक)' के लिए:
'निजी नाव [] screenPointsToScaledPoints (फ्लोट [] एक) { mScaleMatrixInverse। mapPoints (क); mTranslateMatrixInverse.mapPoints (ए); एक वापसी; } ' –

8

दिए गए उत्तरों के आधार पर मैंने पैन और ज़ूम कार्यों को काम करने के लिए इस कोड का उपयोग किया। पहले पिवट-पॉइंट्स के साथ समस्याएं थीं।स्पर्श इवेंट dipatch करने के लिए अपने दृष्टिकोण के लिए Artjom को http://judepereira.com/blog/multi-touch-in-android-translate-scale-and-rotate/ धन्यवाद: onTouch कार्य करने के लिए

public class ZoomableViewGroup extends ViewGroup { 

    // these matrices will be used to move and zoom image 
    private Matrix matrix = new Matrix(); 
    private Matrix matrixInverse = new Matrix(); 
    private Matrix savedMatrix = new Matrix(); 
    // we can be in one of these 3 states 
    private static final int NONE = 0; 
    private static final int DRAG = 1; 
    private static final int ZOOM = 2; 
    private int mode = NONE; 
    // remember some things for zooming 
    private PointF start = new PointF(); 
    private PointF mid = new PointF(); 
    private float oldDist = 1f; 
    private float[] lastEvent = null; 

    private boolean initZoomApplied=false; 

    private float[] mDispatchTouchEventWorkingArray = new float[2]; 
    private float[] mOnTouchEventWorkingArray = new float[2]; 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
     mDispatchTouchEventWorkingArray[0] = ev.getX(); 
     mDispatchTouchEventWorkingArray[1] = ev.getY(); 
     mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray); 
     ev.setLocation(mDispatchTouchEventWorkingArray[0], 
       mDispatchTouchEventWorkingArray[1]); 
     return super.dispatchTouchEvent(ev); 
    } 

    private float[] scaledPointsToScreenPoints(float[] a) { 
     matrix.mapPoints(a); 
     return a; 
    } 

    private float[] screenPointsToScaledPoints(float[] a){ 
     matrixInverse.mapPoints(a); 
     return a; 
    } 

    public ZoomableViewGroup(Context context) { 
     super(context); 
     init(context); 
    } 

    public ZoomableViewGroup(Context context, AttributeSet attrs) { 
     super(context, attrs);  
     init(context); 
    } 

    public ZoomableViewGroup(Context context, AttributeSet attrs, 
      int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
     init(context); 
    } 

    /** 
    * Determine the space between the first two fingers 
    */ 
    private float spacing(MotionEvent event) { 
     float x = event.getX(0) - event.getX(1); 
     float y = event.getY(0) - event.getY(1); 
     return (float)Math.sqrt(x * x + y * y); 
    } 

    /** 
    * Calculate the mid point of the first two fingers 
    */ 
    private void midPoint(PointF point, MotionEvent event) { 
     float x = event.getX(0) + event.getX(1); 
     float y = event.getY(0) + event.getY(1); 
     point.set(x/2, y/2); 
    } 


    private void init(Context context){ 

    } 


    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      View child = getChildAt(i); 
      if (child.getVisibility() != GONE) { 
       child.layout(l, t, l+child.getMeasuredWidth(), t + child.getMeasuredHeight()); 
      } 
     } 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     int widthSize = MeasureSpec.getSize(widthMeasureSpec); 
     int heightSize = MeasureSpec.getSize(heightMeasureSpec); 

     float[] values = new float[9]; 
     matrix.getValues(values); 
     float container_width = values[Matrix.MSCALE_X]*widthSize; 
     float container_height = values[Matrix.MSCALE_Y]*heightSize; 

     //Log.d("zoomToFit", "m width: "+container_width+" m height: "+container_height); 
     //Log.d("zoomToFit", "m x: "+pan_x+" m y: "+pan_y); 

     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      View child = getChildAt(i); 
      if (child.getVisibility() != GONE) { 
       measureChild(child, widthMeasureSpec, heightMeasureSpec); 

       if(i==0 && !initZoomApplied && child.getWidth()>0){ 
        int c_w = child.getWidth(); 
        int c_h = child.getHeight(); 

        //zoomToFit(c_w, c_h, container_width, container_height); 
       } 
      } 
     }   

    } 

    private void zoomToFit(int c_w, int c_h, float container_width, float container_height){ 
     float proportion_firstChild = (float)c_w/(float)c_h; 
     float proportion_container = container_width/container_height; 

     //Log.d("zoomToFit", "firstChildW: "+c_w+" firstChildH: "+c_h); 
     //Log.d("zoomToFit", "proportion-container: "+proportion_container); 
     //Log.d("zoomToFit", "proportion_firstChild: "+proportion_firstChild); 

     if(proportion_container<proportion_firstChild){ 
      float initZoom = container_height/c_h; 
      //Log.d("zoomToFit", "adjust height with initZoom: "+initZoom); 
      matrix.postScale(initZoom, initZoom); 
      matrix.postTranslate(-1*(c_w*initZoom-container_width)/2, 0); 
      matrix.invert(matrixInverse); 
     }else { 
      float initZoom = container_width/c_w; 
      //Log.d("zoomToFit", "adjust width with initZoom: "+initZoom); 
      matrix.postScale(initZoom, initZoom); 
      matrix.postTranslate(0, -1*(c_h*initZoom-container_height)/2); 
      matrix.invert(matrixInverse); 
     } 
     initZoomApplied=true; 
     invalidate(); 
    } 

    @Override 
    protected void dispatchDraw(Canvas canvas) { 
     canvas.save(); 
     canvas.setMatrix(matrix); 
     super.dispatchDraw(canvas); 
     canvas.restore(); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     // handle touch events here 
     mOnTouchEventWorkingArray[0] = event.getX(); 
     mOnTouchEventWorkingArray[1] = event.getY(); 

     mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray); 

     event.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]); 

     switch (event.getAction() & MotionEvent.ACTION_MASK) { 
      case MotionEvent.ACTION_DOWN: 
       savedMatrix.set(matrix); 
       start.set(event.getX(), event.getY()); 
       mode = DRAG; 
       lastEvent = null; 
       break; 
      case MotionEvent.ACTION_POINTER_DOWN: 
       oldDist = spacing(event); 
       if (oldDist > 10f) { 
        savedMatrix.set(matrix); 
        midPoint(mid, event); 
        mode = ZOOM; 
       } 
       lastEvent = new float[4]; 
       lastEvent[0] = event.getX(0); 
       lastEvent[1] = event.getX(1); 
       lastEvent[2] = event.getY(0); 
       lastEvent[3] = event.getY(1); 
       //d = rotation(event); 
       break; 
      case MotionEvent.ACTION_UP: 
      case MotionEvent.ACTION_POINTER_UP: 
       mode = NONE; 
       lastEvent = null; 
       break; 
      case MotionEvent.ACTION_MOVE: 
       if (mode == DRAG) { 
        matrix.set(savedMatrix); 
        float dx = event.getX() - start.x; 
        float dy = event.getY() - start.y; 
        matrix.postTranslate(dx, dy); 
        matrix.invert(matrixInverse); 
       } else if (mode == ZOOM) { 
        float newDist = spacing(event); 
        if (newDist > 10f) { 
         matrix.set(savedMatrix); 
         float scale = (newDist/oldDist); 
         matrix.postScale(scale, scale, mid.x, mid.y); 
         matrix.invert(matrixInverse); 
        } 
       } 
       break; 
     } 

     invalidate(); 
     return true; 
    } 

} 

क्रेडिट्स में जाते हैं।

मैंने एक ज़ूम टॉफिट विधि जोड़ा जो इस बिंदु पर टिप्पणी की गई है क्योंकि ज्यादातर लोगों को इसकी आवश्यकता नहीं है। यह बच्चों को कंटेनर के आकार में फिट करता है और स्केलफैक्टर के लिए पहला बच्चा संदर्भ के रूप में लेता है।

+0

स्केल फैक्टर को सीमित करने के लिए कैसे? मैं चाहता हूं कि ज़ूम 0.75 और 4 के भीतर हो, उदाहरण के लिए। – techtinkerer

+0

आप MAX_ZOOM परिभाषित कर सकते हैं: 'int MAX_ZOOM = 4;' और फिर स्केल (matrix.postScale) पोस्ट करने से पहले इस पंक्ति को जोड़ें: 'float [] values ​​= new float [9]; matrix.getValues ​​(मान); यदि (स्केल * मान [Matrix.MSCALE_X]> = MAX_ZOOM) { \t \t स्केल = MAX_ZOOM/मान [Matrix.MSCALE_X]; } ' – Thomas

+0

अच्छा कोड! ज़ूम बेहतर है (तुरंत शुरू होता है, जो एलेक्स के कोड में मामला नहीं है) और कोई पिवट-पॉइंट समस्या नहीं है। हालांकि, यह डबल-टैप-ड्रैग एक्शन के साथ ज़ूम करने की अनुमति नहीं देता है। – Donkey

0

एलेक्स के कोड जोड़ने ज़ूम इन निम्न परिवर्तन

के बेहतर permormance प्राप्त करने के लिए
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 

    @Override 
    public boolean onScale(ScaleGestureDetector detector) { 
     mScaleFactor *= detector.getScaleFactor(); 
     if (detector.isInProgress()) { 
      mFocusX = detector.getFocusX(); 
      mFocusY = detector.getFocusY(); 
     } 

     mFocusX = (mFocusX + mLastTouchX)/2; // get center of touch 
     mFocusY = (mFocusY + mLastTouchY)/2; // get center of touch 

     mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 2.0f)); 
     mScaleMatrix.setScale(mScaleFactor, mScaleFactor,mFocusX, mFocusY); 
     mScaleMatrix.invert(mScaleMatrixInverse); 
     invalidate(); 
     requestLayout(); 

     return true; 
    } 
} 
6

Thomas जवाब है लगभग सबसे अच्छा (मैं अपने फ़ोन पर एक स्थिति बग है): ज़ूम तुरंत शुरू होता है (जो मामला नहीं है Alex के कोड के साथ), और ज़ूम को सही पिवट-पॉइंट पर बनाया गया है।

हालांकि, Alex के कोड के विपरीत, "डबल-टैप-ड्रैग" इशारा के साथ ज़ूम करना संभव नहीं है (एक अच्छी तरह से ज्ञात इशारा नहीं है बल्कि Google में एक ही उंगली के साथ ज़ूम करने के लिए एक बहुत ही उपयोगी है) क्रोम या Google मानचित्र ऐप्स)। तो यहाँ Thomas के कोड का एक संशोधन यह संभव बनाने के लिए (और subview स्थिति बग फिक्सिंग) है:

public class ZoomableView extends ViewGroup { 

    // States. 
    private static final byte NONE = 0; 
    private static final byte DRAG = 1; 
    private static final byte ZOOM = 2; 

    private byte mode = NONE; 

    // Matrices used to move and zoom image. 
    private Matrix matrix = new Matrix(); 
    private Matrix matrixInverse = new Matrix(); 
    private Matrix savedMatrix = new Matrix(); 

    // Parameters for zooming. 
    private PointF start = new PointF(); 
    private PointF mid = new PointF(); 
    private float oldDist = 1f; 
    private float[] lastEvent = null; 
    private long lastDownTime = 0l; 

    private float[] mDispatchTouchEventWorkingArray = new float[2]; 
    private float[] mOnTouchEventWorkingArray = new float[2]; 


    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
     mDispatchTouchEventWorkingArray[0] = ev.getX(); 
     mDispatchTouchEventWorkingArray[1] = ev.getY(); 
     mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray); 
     ev.setLocation(mDispatchTouchEventWorkingArray[0], mDispatchTouchEventWorkingArray[1]); 
     return super.dispatchTouchEvent(ev); 
    } 

    public ZoomableView(Context context) { 
     super(context); 
     init(context); 
    } 

    public ZoomableView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     init(context); 
    } 

    public ZoomableView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
     init(context); 
    } 


    private void init(Context context) { 

    } 


    /** 
    * Determine the space between the first two fingers 
    */ 
    private float spacing(MotionEvent event) { 
     float x = event.getX(0) - event.getX(1); 
     float y = event.getY(0) - event.getY(1); 
     return (float) Math.sqrt(x * x + y * y); 
    } 

    /** 
    * Calculate the mid point of the first two fingers 
    */ 
    private void midPoint(PointF point, MotionEvent event) { 
     float x = event.getX(0) + event.getX(1); 
     float y = event.getY(0) + event.getY(1); 
     point.set(x/2, y/2); 
    } 

    private float[] scaledPointsToScreenPoints(float[] a) { 
     matrix.mapPoints(a); 
     return a; 
    } 

    private float[] screenPointsToScaledPoints(float[] a) { 
     matrixInverse.mapPoints(a); 
     return a; 
    } 


    @Override 
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      View child = getChildAt(i); 
      if (child.getVisibility() != GONE) { 
       child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); 
      } 
     } 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
      View child = getChildAt(i); 
      if (child.getVisibility() != GONE) { 
       measureChild(child, widthMeasureSpec, heightMeasureSpec); 
      } 
     } 
    } 

    @Override 
    protected void dispatchDraw(Canvas canvas) { 
     float[] values = new float[9]; 
     matrix.getValues(values); 
     canvas.save(); 
     canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]); 
     canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]); 
     super.dispatchDraw(canvas); 
     canvas.restore(); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     // handle touch events here 
     mOnTouchEventWorkingArray[0] = event.getX(); 
     mOnTouchEventWorkingArray[1] = event.getY(); 

     mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray); 

     event.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]); 

     switch (event.getAction() & MotionEvent.ACTION_MASK) { 
      case MotionEvent.ACTION_DOWN: 
       savedMatrix.set(matrix); 
       mode = DRAG; 
       lastEvent = null; 
       long downTime = event.getDownTime(); 
       if (downTime - lastDownTime < 300l) { 
        float density = getResources().getDisplayMetrics().density; 
        if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) < 40.f * density) { 
         savedMatrix.set(matrix); 
         mid.set(event.getX(), event.getY()); 
         mode = ZOOM; 
         lastEvent = new float[4]; 
         lastEvent[0] = lastEvent[1] = event.getX(); 
         lastEvent[2] = lastEvent[3] = event.getY(); 
        } 
        lastDownTime = 0l; 
       } else { 
        lastDownTime = downTime; 
       } 
       start.set(event.getX(), event.getY()); 
       break; 
      case MotionEvent.ACTION_POINTER_DOWN: 
       oldDist = spacing(event); 
       if (oldDist > 10f) { 
        savedMatrix.set(matrix); 
        midPoint(mid, event); 
        mode = ZOOM; 
       } 
       lastEvent = new float[4]; 
       lastEvent[0] = event.getX(0); 
       lastEvent[1] = event.getX(1); 
       lastEvent[2] = event.getY(0); 
       lastEvent[3] = event.getY(1); 
       break; 
      case MotionEvent.ACTION_UP: 
      case MotionEvent.ACTION_POINTER_UP: 
       mode = NONE; 
       lastEvent = null; 
       break; 
      case MotionEvent.ACTION_MOVE: 
       final float density = getResources().getDisplayMetrics().density; 
       if (mode == DRAG) { 
        matrix.set(savedMatrix); 
        float dx = event.getX() - start.x; 
        float dy = event.getY() - start.y; 
        matrix.postTranslate(dx, dy); 
        matrix.invert(matrixInverse); 
        if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) > 20.f * density) { 
         lastDownTime = 0l; 
        } 
       } else if (mode == ZOOM) { 
        if (event.getPointerCount() > 1) { 
         float newDist = spacing(event); 
         if (newDist > 10f * density) { 
          matrix.set(savedMatrix); 
          float scale = (newDist/oldDist); 
          matrix.postScale(scale, scale, mid.x, mid.y); 
          matrix.invert(matrixInverse); 
         } 
        } else { 
         matrix.set(savedMatrix); 
         float scale = event.getY()/start.y; 
         matrix.postScale(scale, scale, mid.x, mid.y); 
         matrix.invert(matrixInverse); 
        } 
       } 
       break; 
     } 

     invalidate(); 
     return true; 
    } 

} 
+0

'onTouchEvent' पर x, y निर्देशांक को कैसे परिवर्तित किया जाए? – isuru

+0

मुझे थॉमस के उत्तर के साथ कोई समस्या थी। मेरे दृश्य के हिट बॉक्स तब नीचे थे जहां वास्तविक दृश्य है (एंड्रॉइड <25 एपीआई के लिए)। मुझे पता चला कि समस्या प्रेषण के साथ थी विधि विधि। चूंकि मैट्रिक्स के बारे में कुछ भी नहीं जानता, इसलिए मैं बहुत निराश था। आप प्रेषण समस्या को हल करें। मैं वास्तव में समझना चाहता हूं कि जादू कैसे काम करता है। मैं मैट्रिक्स और शायद, Rect वस्तुओं के साथ ViewPort तकनीक के बारे में जानना चाहता हूँ। क्या आप इसके लिए सीखने के लिए अच्छे स्रोतों की सलाह दे सकते हैं? मैं वास्तव में कुछ करना पसंद नहीं करता, जब मैं यह नहीं समझ सकता कि यह कैसे काम करता है। कोड के लिए धन्यवाद, और धन्यवाद @ थॉमस भी। – Kotsu

+0

यह सबसे अच्छा समाधान है। – Chandru

0

जो एक ज़ूमिंग में रुचि रखते हैं के लिए/LinearLayout पैनिंग, मैं संस्करण एलेक्स द्वारा पर पोस्ट किए गए संशोधित चीजों को लंबवत रूप से रखें और दृश्यमान दृश्यों में पैनिंग को कैप करें। मैं पीडीएफ रेन्डरर से बिटमैप्स के लिए इसका उपयोग करता हूं। मैंने इसका परीक्षण किया है, लेकिन यदि आप किसी भी बग को देखते हैं तो कृपया पोस्ट करें क्योंकि मैं उनके बारे में भी जानना चाहता हूं!

नोट: मैंने डबल टैपिंग को लागू नहीं करने का विकल्प चुना है, क्योंकि क्विकस्केल काम करता है।

public class ZoomableLinearLayout extends ViewGroup { 

    private static final int INVALID_POINTER_ID = 1; 
    private int mActivePointerId = INVALID_POINTER_ID; 

    private float mScaleFactor = 1; 
    private ScaleGestureDetector mScaleDetector; 
    private Matrix mScaleMatrix = new Matrix(); 
    private Matrix mScaleMatrixInverse = new Matrix(); 

    private float mPosX; 
    private float mPosY; 
    private Matrix mTranslateMatrix = new Matrix(); 
    private Matrix mTranslateMatrixInverse = new Matrix(); 

    private float mLastTouchX; 
    private float mLastTouchY; 

    private float mFocusY; 
    private float mFocusX; 

    private int mCanvasWidth; 
    private int mCanvasHeight; 

    private float[] mInvalidateWorkingArray = new float[6]; 
    private float[] mDispatchTouchEventWorkingArray = new float[2]; 
    private float[] mOnTouchEventWorkingArray = new float[2]; 

    private boolean mIsScaling; 

    public ZoomableLinearLayout(Context context) { 
     super(context); 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
     mTranslateMatrix.setTranslate(0, 0); 
     mScaleMatrix.setScale(1, 1); 
    } 

    public ZoomableLinearLayout(Context context, AttributeSet attributeSet) { 
     super(context, attributeSet); 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
     mTranslateMatrix.setTranslate(0, 0); 
     mScaleMatrix.setScale(1, 1); 
    } 

    @Override 
    protected void onLayout(boolean changed, int l, int t, int r, int b) { 
     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
     View child = getChildAt(i); 
     if (child.getVisibility() != GONE) { 
      child.layout(l, t, l+child.getMeasuredWidth(), t += child.getMeasuredHeight()); 
     } 
     } 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 

     int height = 0; 
     int width = 0; 
     int childCount = getChildCount(); 
     for (int i = 0; i < childCount; i++) { 
     View child = getChildAt(i); 
     if (child.getVisibility() != GONE) { 
      measureChild(child, widthMeasureSpec, heightMeasureSpec); 
      height += child.getMeasuredHeight(); 
      width = Math.max(width, child.getMeasuredWidth()); 
     } 
     } 
     mCanvasWidth = width; 
     mCanvasHeight = height; 
    } 

    @Override 
    protected void dispatchDraw(Canvas canvas) { 
     canvas.save(); 
     canvas.translate(mPosX, mPosY); 
     canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY); 
     super.dispatchDraw(canvas); 
     canvas.restore(); 
    } 

    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
     mDispatchTouchEventWorkingArray[0] = ev.getX(); 
     mDispatchTouchEventWorkingArray[1] = ev.getY(); 
     mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray); 
     ev.setLocation(mDispatchTouchEventWorkingArray[0], 
      mDispatchTouchEventWorkingArray[1]); 
     return super.dispatchTouchEvent(ev); 
    } 

    /** 
    * Although the docs say that you shouldn't override this, I decided to do 
    * so because it offers me an easy way to change the invalidated area to my 
    * likening. 
    */ 
    @Override 
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) { 

     mInvalidateWorkingArray[0] = dirty.left; 
     mInvalidateWorkingArray[1] = dirty.top; 
     mInvalidateWorkingArray[2] = dirty.right; 
     mInvalidateWorkingArray[3] = dirty.bottom; 

     mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray); 
     dirty.set(Math.round(mInvalidateWorkingArray[0]), Math.round(mInvalidateWorkingArray[1]), 
      Math.round(mInvalidateWorkingArray[2]), Math.round(mInvalidateWorkingArray[3])); 

     location[0] *= mScaleFactor; 
     location[1] *= mScaleFactor; 
     return super.invalidateChildInParent(location, dirty); 
    } 

    private float[] scaledPointsToScreenPoints(float[] a) { 
     mScaleMatrix.mapPoints(a); 
     mTranslateMatrix.mapPoints(a); 
     return a; 
    } 

    private float[] screenPointsToScaledPoints(float[] a){ 
     mTranslateMatrixInverse.mapPoints(a); 
     mScaleMatrixInverse.mapPoints(a); 
     return a; 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     mOnTouchEventWorkingArray[0] = ev.getX(); 
     mOnTouchEventWorkingArray[1] = ev.getY(); 

     mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray); 

     ev.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]); 
     mScaleDetector.onTouchEvent(ev); 

     final int action = ev.getAction(); 
     switch (action & MotionEvent.ACTION_MASK) { 
     case MotionEvent.ACTION_DOWN: { 
      final float x = ev.getX(); 
      final float y = ev.getY(); 

      mLastTouchX = x; 
      mLastTouchY = y; 

      // Save the ID of this pointer 
      mActivePointerId = ev.getPointerId(0); 
      break; 
     } 

     case MotionEvent.ACTION_MOVE: { 
      // Find the index of the active pointer and fetch its position 
      final int pointerIndex = ev.findPointerIndex(mActivePointerId); 
      final float x = ev.getX(pointerIndex); 
      final float y = ev.getY(pointerIndex); 

      if (mIsScaling && ev.getPointerCount() == 1) { 
       // Don't move during a QuickScale. 
       mLastTouchX = x; 
       mLastTouchY = y; 

       break; 
      } 

      float dx = x - mLastTouchX; 
      float dy = y - mLastTouchY; 

      float[] topLeft = {0f, 0f}; 
      float[] bottomRight = {getWidth(), getHeight()}; 
      /* 
      * Corners of the view in screen coordinates, so dx/dy should not be allowed to 
      * push these beyond the canvas bounds. 
      */ 
      float[] scaledTopLeft = screenPointsToScaledPoints(topLeft); 
      float[] scaledBottomRight = screenPointsToScaledPoints(bottomRight); 

      dx = Math.min(Math.max(dx, scaledBottomRight[0] - mCanvasWidth), scaledTopLeft[0]); 
      dy = Math.min(Math.max(dy, scaledBottomRight[1] - mCanvasHeight), scaledTopLeft[1]); 

      mPosX += dx; 
      mPosY += dy; 

      mTranslateMatrix.preTranslate(dx, dy); 
      mTranslateMatrix.invert(mTranslateMatrixInverse); 

      mLastTouchX = x; 
      mLastTouchY = y; 

      invalidate(); 
      break; 
     } 

     case MotionEvent.ACTION_UP: { 
      mActivePointerId = INVALID_POINTER_ID; 
      break; 
     } 

     case MotionEvent.ACTION_CANCEL: { 
      mActivePointerId = INVALID_POINTER_ID; 
      break; 
     } 

     case MotionEvent.ACTION_POINTER_UP: { 
      // Extract the index of the pointer that left the touch sensor 
      final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
      final int pointerId = ev.getPointerId(pointerIndex); 
      if (pointerId == mActivePointerId) { 
       // This was our active pointer going up. Choose a new 
       // active pointer and adjust accordingly. 
       final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 
       mLastTouchX = ev.getX(newPointerIndex); 
       mLastTouchY = ev.getY(newPointerIndex); 
       mActivePointerId = ev.getPointerId(newPointerIndex); 
      } 
      break; 
     } 
     } 
     return true; 
    } 

    private float getMaxScale() { 
     return 2f; 
    } 

    private float getMinScale() { 
     return 1f; 
    } 

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
     @Override 
     public boolean onScaleBegin(ScaleGestureDetector detector) { 
     mIsScaling = true; 

     mFocusX = detector.getFocusX(); 
     mFocusY = detector.getFocusY(); 

     float[] foci = {mFocusX, mFocusY}; 
     float[] scaledFoci = screenPointsToScaledPoints(foci); 

     mFocusX = scaledFoci[0]; 
     mFocusY = scaledFoci[1]; 

     return true; 
     } 

     @Override 
     public void onScaleEnd(ScaleGestureDetector detector) { 
     mIsScaling = false; 
     } 

     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
     mScaleFactor *= detector.getScaleFactor(); 
     mScaleFactor = Math.max(getMinScale(), Math.min(mScaleFactor, getMaxScale())); 
     mScaleMatrix.setScale(mScaleFactor, mScaleFactor, mFocusX, mFocusY); 
     mScaleMatrix.invert(mScaleMatrixInverse); 
     invalidate(); 

     return true; 
     } 
    } 

} 
0

मैं यहां पोस्ट किए गए कोड के कुछ संशोधित संस्करणों का उपयोग कर रहा हूं। यह ज़ूमलाउट स्क्रॉल और स्केल के लिए एंड्रॉइड जेस्चर पहचानकर्ताओं का उपयोग करता है। ज़ूमिंग या पैनिंग करते समय यह पिवट और सीमाओं को भी सुरक्षित रखता है।

https://github.com/maxtower/ZoomLayout/blob/master/app/src/main/java/com/maxtower/testzoomlayout/ZoomLayout.java

सीमाओं पैन संरक्षित करने के लिए:

if (contentSize != null) 
    { 
     float[] values = new float[9]; 
     matrix.getValues(values); 
     float totX = values[Matrix.MTRANS_X] + distanceX; 
     float totY = values[Matrix.MTRANS_Y] + distanceY; 
     float sx = values[Matrix.MSCALE_X]; 

     Rect viewableRect = new Rect(); 
     ZoomLayout.this.getDrawingRect(viewableRect); 
     float offscreenWidth = contentSize.width() - (viewableRect.right - viewableRect.left); 
     float offscreenHeight = contentSize.height() - (viewableRect.bottom - viewableRect.top); 
     float maxDx = (contentSize.width() - (contentSize.width()/sx)) * sx; 
     float maxDy = (contentSize.height() - (contentSize.height()/sx)) * sx; 
     if (totX > 0 && distanceX > 0) 
     { 
      distanceX = 0; 
     } 
     if (totY > 0 && distanceY > 0) 
     { 
      distanceY = 0; 
     } 

     if(totX*-1 > offscreenWidth+maxDx && distanceX < 0) 
     { 
      distanceX = 0; 
     } 
     if(totY*-1 > offscreenHeight+maxDy && distanceY < 0) 
     { 
      distanceY = 0; 
     } 

    } 
संबंधित मुद्दे