2012-08-29 25 views
11

Utworzono klasę powiększenia i panoramy dla ImageView.Powiększanie i przesuwanie ImageView Android

Funkcje, które próbuję utworzyć. - Przyciąga jednym dotknięciem palca i porusza się po nim ruch - przybliża i przesuwa palcem po dotknięciu dwoma palcami i poruszeniu

W większości przypadków działa to bardzo dobrze. Występuje drobny błąd, gdy wykonuję następujące czynności: - Rozglądam się jednym palcem (Stan: bez problemu) - Odkładam drugi palec, powiększam i przesuwanie (Stan: bez problemu) - Puszczam mój drugi finger (Status: obraz przeskakuje trochę)

Miałem nadzieję, że ktoś może mi pomóc rozwiązać ten problem.

myślę, że to musi mieć do czynienia z resetowania mLastTouchX i mLastTouchY w „case MotionEvent.ACTION_POINTER_UP”

Wszelkie pomoc byłaby znacznie ceniona!

import android.content.Context; 
import android.graphics.Canvas; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.ScaleGestureDetector; 
import android.widget.ImageView; 

public class MyImageView extends ImageView { 

    private static final int INVALID_POINTER_ID = -1; 

    private float mPosX; 
    private float mPosY; 

    private float mLastTouchX; 
    private float mLastTouchY; 
    private float mLastGestureX; 
    private float mLastGestureY; 
    private int mActivePointerId = INVALID_POINTER_ID; 

    private ScaleGestureDetector mScaleDetector; 
    private float mScaleFactor = 1.f; 

    public MyImageView(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
     mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); 
    } 

    public MyImageView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     // Let the ScaleGestureDetector inspect all events. 
     mScaleDetector.onTouchEvent(ev); 

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

        mLastTouchX = x; 
        mLastTouchY = y; 
        mActivePointerId = ev.getPointerId(0); 
       } 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_1_DOWN: { 
       if (mScaleDetector.isInProgress()) { 
        final float gx = mScaleDetector.getFocusX(); 
        final float gy = mScaleDetector.getFocusY(); 
        mLastGestureX = gx; 
        mLastGestureY = gy; 
       } 
       break; 
      } 
      case MotionEvent.ACTION_MOVE: { 

       // Only move if the ScaleGestureDetector isn't processing a gesture. 
       if (!mScaleDetector.isInProgress()) { 
        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; 

        invalidate(); 

        mLastTouchX = x; 
        mLastTouchY = y; 
       } 
       else{ 
        final float gx = mScaleDetector.getFocusX(); 
        final float gy = mScaleDetector.getFocusY(); 

        final float gdx = gx - mLastGestureX; 
        final float gdy = gy - mLastGestureY; 

        mPosX += gdx; 
        mPosY += gdy; 

        invalidate(); 

        mLastGestureX = gx; 
        mLastGestureY = gy; 
       } 

       break; 
      } 
      case MotionEvent.ACTION_UP: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 
      case MotionEvent.ACTION_CANCEL: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_UP: { 
       final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) 
         >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
       final int pointerId = ev.getPointerId(pointerIndex); 
       if (pointerId == mActivePointerId) { 
        Log.d("DEBUG", "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; 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 

     canvas.save(); 

     canvas.translate(mPosX, mPosY); 

     if (mScaleDetector.isInProgress()) { 
      canvas.scale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY()); 
     } 
     else{ 
      canvas.scale(mScaleFactor, mScaleFactor); 
     } 
     super.onDraw(canvas); 
     canvas.restore(); 
    } 

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      mScaleFactor *= detector.getScaleFactor(); 

      // Don't let the object get too small or too large. 
      mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f)); 

      invalidate(); 
      return true; 
     } 
    } 
} 
+0

hm. Wypróbowałem Twój kod i obserwowałem go z małym obrazkiem, który następnie umieszczam na drugim palcu, a obraz "przeskakuje" do niego. Czy to błąd, z którym się spotykasz? – sandrstar

+0

Tak, ten skok jest tym, co próbuję rozwiązać. – Hank

+0

Wygląda na to, że jest spowodowane użyciem współrzędnych ostrości. Postaram się teraz debugować kod później. – sandrstar

Odpowiedz

16

Wydaje się, że canvas.scale() w „inny” sprawozdanie z metody „OnDraw” wymagała mLastGestureX i mLastGestureY zatrzymać skoków. ja też odświeżyć mLastTouchX i mLastTouchY kiedy wracając do pojedynczego palca panoramowania w przypadku MotionEvent.ACTION_POINTER_UP „”

Oto ostateczny, nie może spełnić każdy powoduje, że nie ogranicza przesuwanie granic obrazu przeszłości, ale to powinno być łatwe do osiągnięcia jest wiele dyskusji na ten temat.

import android.content.Context; 
import android.graphics.Canvas; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.ScaleGestureDetector; 
import android.widget.ImageView; 

public class MyImageView extends ImageView { 

    private static final int INVALID_POINTER_ID = -1; 

    private float mPosX; 
    private float mPosY; 

    private float mLastTouchX; 
    private float mLastTouchY; 
    private float mLastGestureX; 
    private float mLastGestureY; 
    private int mActivePointerId = INVALID_POINTER_ID; 

    private ScaleGestureDetector mScaleDetector; 
    private float mScaleFactor = 1.f; 

    public MyImageView(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
     mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); 
    } 

    public MyImageView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     // Let the ScaleGestureDetector inspect all events. 
     mScaleDetector.onTouchEvent(ev); 

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

        mLastTouchX = x; 
        mLastTouchY = y; 
        mActivePointerId = ev.getPointerId(0); 
       } 
       break; 
      } 
      case MotionEvent.ACTION_POINTER_1_DOWN: { 
       if (mScaleDetector.isInProgress()) { 
        final float gx = mScaleDetector.getFocusX(); 
        final float gy = mScaleDetector.getFocusY(); 
        mLastGestureX = gx; 
        mLastGestureY = gy; 
       } 
       break; 
      } 
      case MotionEvent.ACTION_MOVE: { 

       // Only move if the ScaleGestureDetector isn't processing a gesture. 
       if (!mScaleDetector.isInProgress()) { 
        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; 

        invalidate(); 

        mLastTouchX = x; 
        mLastTouchY = y; 
       } 
       else{ 
        final float gx = mScaleDetector.getFocusX(); 
        final float gy = mScaleDetector.getFocusY(); 

        final float gdx = gx - mLastGestureX; 
        final float gdy = gy - mLastGestureY; 

        mPosX += gdx; 
        mPosY += gdy; 

        invalidate(); 

        mLastGestureX = gx; 
        mLastGestureY = gy; 
       } 

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

       final int pointerIndex = (ev.getAction() & 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); 
       } 
       else{ 
        final int tempPointerIndex = ev.findPointerIndex(mActivePointerId); 
        mLastTouchX = ev.getX(tempPointerIndex); 
        mLastTouchY = ev.getY(tempPointerIndex); 
       } 

       break; 
      } 
     } 

     return true; 
    } 

    @Override 
    public void onDraw(Canvas canvas) { 

     canvas.save(); 

     canvas.translate(mPosX, mPosY); 

     if (mScaleDetector.isInProgress()) { 
      canvas.scale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY()); 
     } 
     else{ 
      canvas.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY); 
     } 
     super.onDraw(canvas); 
     canvas.restore(); 
    } 

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      mScaleFactor *= detector.getScaleFactor(); 

      // Don't let the object get too small or too large. 
      mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f)); 

      invalidate(); 
      return true; 
     } 
    } 
} 
+0

Jak ograniczyć to przed wyprowadzką z ImageView? –

+0

Przeniesiono wszystko do OpenGL, ponieważ był silniejszym graficznym narzędziem odwzorowującym, ale myślę, że jeśli obliczysz granice swojego obrazu przy dowolnym powiększeniu kevel, możesz prawdopodobnie sprawdzić i uniemożliwić panoramowanie. – Hank

+0

To wciąż przeskakuje, gdy drugi palec jest umieszczone na ekranie. –

1

byłem bawiąc się rozwiązanie tego problemu przez ponad tydzień, a to daje mi mnóstwo problemów. Jednak znacznie zmniejszyłem problem. Twoje powyższe rozwiązanie NIE działa dla mnie, ale moje rozwiązanie poniżej jest blisko. Problem polega na tym, że skacze, gdy drugi palec jest wciśnięty lub podniesiony. Odkryłem, że tak się dzieje, ponieważ mPosX i mPosY nie zawsze są tymi obiektami, które reprezentują. Oto co mam na myśli:

Kiedy ACTION_MOVE nazywa i oznaczeniem wchodzi do „innego” oświadczenie (do czynienia z wydarzeniami zoom), mPosX i mPosY są tylko zmienia według zmiany ostrości, niezmiana powiększenia. Oznacza to, że przesuwanie dwoma palcami działa, a powiększanie dwoma palcami działa, ale mPosX i mPosY nie zmieniają się odpowiednio w stosunku do zmieniającego się zoomu.

Próbuję znaleźć sposoby, aby to naprawić, stosując różnicowe zmiany powiększenia (mScaleDetector.getScaleFactor()) i różnicowe zmiany ostrości, ale nie mogę wydawać się działać na tyle dobrze, aby znajdź coś, co działa.

Innym rozwiązaniem jest przeniesienie wszystkich operacji powiększania do OnTouchListener i całkowite pozbycie się ScaleListener. Oznacza to o wiele więcej matematyki, ale z pewnością byłoby rozwiązaniem.

Oto OnDraw:

@Override 
    public void onDraw(Canvas c) { 
     c.save(); 

     if (mScaleDetector.isInProgress()) { 
      c.scale(mScaleFactor, mScaleFactor, mLastGestureX - mPosX, 
        mLastGestureY - mPosY); 
     } else { 
      c.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY); 
     } 

     c.translate(mPosX/mScaleFactor, mPosY/mScaleFactor); 

     // drawing instruction here 

     c.restore(); 
    } 

Oto jak kod reaguje na prasach palców:

@Override 
    public boolean onTouchEvent(MotionEvent ev) { 
     mScaleDetector.onTouchEvent(ev); 

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

       mLastTouchX = x; 
       mLastTouchY = y; 

       mActivePointerId = ev.getPointerId(0); 
      } 
      break; 
     } 

     case MotionEvent.ACTION_POINTER_DOWN: { 
      if (!mScaleDetector.isInProgress()) { 
       final float gx = mScaleDetector.getFocusX(); 
       final float gy = mScaleDetector.getFocusY(); 

       mLastGestureX = gx; 
       mLastGestureY = gy; 
      } 
      break; 
     } 

     case MotionEvent.ACTION_MOVE: { 
      if (!mScaleDetector.isInProgress()) { 
       Log.i("hi", "SD not in progress"); 
       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; 

       invalidate(); 

       mLastTouchX = x; 
       mLastTouchY = y; 
      } else { 
       Log.i("hi", "SD in progress"); 
       final float gx = mScaleDetector.getFocusX(); 
       final float gy = mScaleDetector.getFocusY(); 

       final float gdx = gx - mLastGestureX; 
       final float gdy = gy - mLastGestureY; 

       mPosX += gdx; 
       mPosY += gdy; 

       // SOMETHING NEEDS TO HAPPEN RIGHT HERE. 

       invalidate(); 

       mLastGestureX = gx; 
       mLastGestureY = gy; 
      } 

      break; 
     } 

     case MotionEvent.ACTION_POINTER_UP: { 

      final int pointerIndex = (ev.getAction() & 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); 
      } else { 
       final int tempPointerIndex = ev.findPointerIndex(mActivePointerId); 

       mLastTouchX = ev.getX(tempPointerIndex); 
       mLastTouchY = ev.getY(tempPointerIndex); 
      } 

      break; 
     } 
     } 

     return true; 
    } 

I choć to głównie niepowiązane, oto ScaleListener:

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
     @Override 
     public boolean onScale(ScaleGestureDetector detector) { 
      mScaleFactor *= detector.getScaleFactor(); 

      invalidate(); 
      return true; 
     } 
    } 

Ponownie ten kod NIE działa idealnie, ale jest bardzo blisko. Wyjaśniłem dokładnie powyższy problem i nadal mam problem z jego uruchomieniem. Nie wiem, czy to pojawi się w twoich powiadomieniach, Hank, ale mam nadzieję, że ktoś to zobaczy i mi pomoże.

+0

dziękuję, jest to ulepszenie poprzedniego kodu, ale to wciąż przeskakuje po dotknięciu drugiego palca na ekranie. –

0

Rozwiązanie Hanka działa dla mnie. Dodałem funkcję resetowania, aby kolejne obrazy mogły być wyświetlane normalnie.

public void ResetView() { 
     mScaleFactor = 1.f; 
     mPosX = 0.f; 
     mPosY = 0.f; 
    } 
0

W/o patrząc na kod Zakładam, że robisz obliczenia pozycji na podstawie 2 palców, gdy są 2 palce. Zawsze będziesz miał skok w tej sprawie.

+1

Lol dlaczego miałbyś odpowiedzieć trzy lata po rozwiązaniu problemu? – Hank

+2

Lol dlaczego cię to obchodzi? –

Powiązane problemy