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.
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
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
Dziękujemy! Dzięki wielu próbom i błędom, udało mi się w końcu to zrobić. – Ribose