2011-07-21 14 views
8

Tak więc rozszerzam niestandardowy SurfaceView i próbuję zrobić to z funkcją powiększania i przewijania.Jak mogę umieścić limity na macierzy tłumaczenia?

Jak przewijać:

@Override 
    public boolean onScroll(MotionEvent e1, MotionEvent e2, 
      float distanceX, float distanceY) { 

     // subtract scrolled amount 
     matrix.postTranslate(-distanceX, -distanceY); 

     rebound(); 

     invalidate(); 

     // handled 
     return true; 
    } 

Jak powiększyć:

@Override 
    public boolean onScale(ScaleGestureDetector detector) { 
     if (detector.isInProgress()) { 
      // get scale 
      float factor = detector.getScaleFactor(); 

      // Don't let the object get too small or too large. 
      if (factor * scale > 1f) { 
       factor = 1f/scale; 
      } else if (factor * scale < minScale) { 
       factor = minScale/scale; 
      } 

      // store local scale 
      scale *= factor; 

      // do the scale 
      matrix.preScale(factor, factor, detector.getFocusX(), detector.getFocusY()); 

      rebound(); 

      invalidate(); 
     } 

     return true; 
    } 

(dla odniesienia używam tego kodu do onDraw :)

@Override 
protected void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 

    canvas.save(); 
    canvas.setMatrix(matrix); 
    // [...] stuff drawn with matrix settings 
    canvas.restore(); 
    // [...] stuff drawn without matrix settings, as an overlay 
} 

Obecnie obie te metody są działa dobrze. Powiększenie jest zatrzymywane na minimalnej (od 0f do 1f) i maksymalnej (obecnie zawsze 1 f) wartości skali.

globalne pola wykorzystywane:

  • drawW, drawH = float, rozmiar w pikselach danych plandeki na skali = 1.
  • parentW, parentH = float wielkość w pikselach widzialnym świetle .
  • matrix = android.graphics.Matrix.

Problemem jest „rebound()” (być może potrzebuje lepszej nazwy, heh) metoda Mam przystąpieniem do wdrożenia, który automatycznie wymusić zawartość do pozostania w widoku.

Próbowałem różnych metod, aby spróbować obliczyć, gdzie powinny być granice (w obrębie prostokąta (0, 0, nadrzędnyW, nadrzędnyH)) i tłumaczyć macierz "wstecz", gdy idzie zbyt daleko.

Oto, co mam obecnie, co z pewnością nie działa, zamiast tego popycha go dalej w prawo. Czuję, że problemem jest moja matematyka, a nie mój pomysł. Czy ktoś może zaproponować prostszy lub bardziej przejrzysty kod, który przekształca matrycę na krawędź, jeśli jest zbyt daleko i/lub rozwiązuje problemy związane z moją próbą wdrożenia? if sprawdza używane, aby pojawić się w lewym górnym

public void rebound() { 
    // bounds 
    RectF currentBounds = new RectF(0, 0, drawW, drawH); 
    matrix.mapRect(currentBounds); 
    RectF parentBounds = new RectF(0, 0, parentW, parentH/2); 

    PointF diff = new PointF(0, 0); 

    if (currentBounds.left > parentBounds.left) { 
     diff.x += (parentBounds.left - currentBounds.left); 
    } 
    if (currentBounds.top > parentBounds.top) { 
     diff.y += (parentBounds.top - currentBounds.top); 
    } 
    if (currentBounds.width() > parentBounds.width()) { 
     if (currentBounds.right < parentBounds.right) { 
      diff.x += (parentBounds.right - currentBounds.right); 
     } 
     if (currentBounds.bottom < parentBounds.bottom) { 
      diff.y += (parentBounds.bottom - currentBounds.bottom); 
     } 
    } 

    matrix.postTranslate(diff.x, diff.y); 
} 

poprzedniej wersji, że napisałem wcześniej matryca była pola (tylko używane canvas.scale() następnie canvas.translate() w onDraw), który pracował:

public void rebound() { 
    // bounds 
    int boundTop = 0; 
    int boundLeft = 0; 
    int boundRight = (int)(-scale * drawW + parentW); 
    int boundBottom = (int)(-scale * drawH + parentH); 

    if (boundLeft >= boundRight) { 
     mScrollX = Math.min(boundLeft, Math.max(boundRight, mScrollX)); 
    } else { 
     mScrollX = 0; 
    } 
    if (boundTop >= boundBottom) { 
     mScrollY = Math.min(boundTop, Math.max(boundBottom, mScrollY)); 
    } else { 
     mScrollY = 0; 
    } 
} 

Używam nowego sposobu, aby prawidłowo skalować na środku: detector.getFocusX(), detector.getFocusY().

AKTUALIZACJA: Zmieniłem metodę na taką, jaka jest teraz. Działa tylko w pewien sposób, nadal ograniczając kierunek w kierunku Y poza centrum i jest nieprawidłowy po zmianie poziomów powiększenia. Zrobiłem też "preScale" i "postTranslate", aby (jak rozumiem) powinno zawsze być stosowanie skali, a następnie translate i nie mieszanie ich.

FINAŁOWA AKTUALIZACJA: Działa teraz.Oto metoda pracy rebound z komentarzem:

public void rebound() { 
    // make a rectangle representing what our current canvas looks like 
    RectF currentBounds = new RectF(0, 0, drawW, drawH); 
    matrix.mapRect(currentBounds); 
    // make a rectangle representing the scroll bounds 
    RectF areaBounds = new RectF((float) getLeft(), 
            (float) getTop(), 
            (float) parentW + (float) getLeft(), 
            (float) parentH + (float) getTop()); 

    // the difference between the current rectangle and the rectangle we want 
    PointF diff = new PointF(0f, 0f); 

    // x-direction 
    if (currentBounds.width() > areaBounds.width()) { 
     // allow scrolling only if the amount of content is too wide at this scale 
     if (currentBounds.left > areaBounds.left) { 
      // stop from scrolling too far left 
      diff.x = (areaBounds.left - currentBounds.left); 
     } 
     if (currentBounds.right < areaBounds.right) { 
      // stop from scrolling too far right 
      diff.x = (areaBounds.right - currentBounds.right); 
     } 
    } else { 
     // negate any scrolling 
     diff.x = (areaBounds.left - currentBounds.left); 
    } 

    // y-direction 
    if (currentBounds.height() > areaBounds.height()) { 
     // allow scrolling only if the amount of content is too tall at this scale 
     if (currentBounds.top > areaBounds.top) { 
      // stop from scrolling too far above 
      diff.y = (areaBounds.top - currentBounds.top); 
     } 
     if (currentBounds.bottom < areaBounds.bottom) { 
      // stop from scrolling too far below 
      diff.y = (areaBounds.bottom - currentBounds.bottom); 
     } 
    } else { 
     // negate any scrolling 
     diff.y = (areaBounds.top - currentBounds.top); 
    } 

    // translate 
    matrix.postTranslate(diff.x, diff.y); 
} 

neguje jakąkolwiek przewijanie, że nie chcę, tłumacząc je z powrotem do granic. Całkowicie neguje przewijanie, jeśli zawartość jest zbyt mała, zmuszając zawartość do umieszczenia w lewym górnym rogu.

Odpowiedz

0

Zaimplementowałem coś dokładnie takiego, aby rzucić własną szczyptą, aby powiększyć. Podejrzewam, że problem z nieosiową osią Y może sprowadzać się do tego, że widok nie jest pełnym rozmiarem ekranu lub nie można zmieniać współrzędnych, aby dopasować skalę.

Moja implementacja oblicza współczynnik skali, stosuje się go do: canvas.scale (pinchZoomScale, pinchZoomScale); Następnie oblicza fizyczny rozmiar ekranu w pikselach, konwertuje go na metry, a następnie stosuje współczynnik skalowania, aby wszystkie rysowane obiekty były poprawnie przesunięte.

Ta implementacja polega na tym, aby zawsze wiedzieć, co powinno znajdować się na środku ekranu i do niego się przyłączyć, niezależnie od poziomu powiększenia.

+0

Masz rację! Widok nie jest całym rozmiarem ekranu. Czy przekształcenia macierzy odnoszą się do ekranu zamiast widoku obszaru roboczego? Zakładałem, że to ma sens, aby być względnym względem płótna, a nie ekranu ... – Ribose

+0

Macierz jest względna względem płótna, ale zakotwicza się w pozycji 0,0, więc podwojenie skali i narysowanie czegoś do 5,5 spowoduje spraw, aby wyglądało na 2.5,2.5 Zauważyłem, że w niektórych przypadkach rozmiar widoku może się mylić i spróbować użyć rozmiaru ekranu jako wskazówki, a nie rzeczywistego widoku, który manipulujesz. Przygotowanie skalowania do płótna wymagało wiele prób i błędów, aby dobrze się bawić przy skalowaniu pozycji obiektów na płótnie. – ScouseChris

+1

Dziękujemy! Dzięki wielu próbom i błędom, udało mi się w końcu to zrobić. – Ribose

Powiązane problemy