java - How can I place limits on my canvas's translation matrix? -
so i'm extending custom surfaceview
, attempting make have pinch-zoom , scrolling capability.
how scroll:
@override public boolean onscroll(motionevent e1, motionevent e2, float distancex, float distancey) { // subtract scrolled amount matrix.posttranslate(-distancex, -distancey); rebound(); invalidate(); // handled return true; }
how zoom:
@override public boolean onscale(scalegesturedetector detector) { if (detector.isinprogress()) { // scale float factor = detector.getscalefactor(); // don't let object small or large. if (factor * scale > 1f) { factor = 1f / scale; } else if (factor * scale < minscale) { factor = minscale / scale; } // store local scale scale *= factor; // scale matrix.prescale(factor, factor, detector.getfocusx(), detector.getfocusy()); rebound(); invalidate(); } return true; }
(for reference use code ondraw
:)
@override protected void ondraw(canvas canvas) { super.ondraw(canvas); canvas.save(); canvas.setmatrix(matrix); // [...] stuff drawn matrix settings canvas.restore(); // [...] stuff drawn without matrix settings, overlay }
currently both of these methods functioning well. zooming stopped @ minimum (between 0f , 1f) , maximum (currently 1f) scale value correctly.
global fields used:
draww
,drawh
=float
, size in pixels of canvas data @ scale = 1.parentw
,parenth
=float
, size in pixels of visible view.matrix
=android.graphics.matrix
.
the problem "rebound()
" (maybe needs better name, heh) method i'm attempting implement automatically force contents stay in view.
i've tried various methods try calculate bounds should (within rectangle (0, 0, parentw, parenth)) , translate matrix "back" when goes far.
here's have, doesn't work, instead pushing farther in right lot. feel problem math, not idea. can please come simpler or cleaner code translates matrix edge if far and/or fix problems attempt @ implementation? if checks used make appear in top left
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); }
a previous version wrote before matrix field (i used canvas.scale()
canvas.translate()
in ondraw
) worked:
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; } }
i'm using new way can correctly scale centered on detector.getfocusx()
, detector.getfocusy()
.
update: changed method now. works somewhat, still bounding y direction way off-center , wrong after changing zoom levels. made "prescale" , "posttranslate" (as understand it) should applying scale translate , not mixing them.
final update: works now. here's working rebound
method comments:
public void rebound() { // make rectangle representing our current canvas looks rectf currentbounds = new rectf(0, 0, draww, drawh); matrix.maprect(currentbounds); // make rectangle representing scroll bounds rectf areabounds = new rectf((float) getleft(), (float) gettop(), (float) parentw + (float) getleft(), (float) parenth + (float) gettop()); // difference between current rectangle , rectangle want pointf diff = new pointf(0f, 0f); // x-direction if (currentbounds.width() > areabounds.width()) { // allow scrolling if amount of content wide @ scale if (currentbounds.left > areabounds.left) { // stop scrolling far left diff.x = (areabounds.left - currentbounds.left); } if (currentbounds.right < areabounds.right) { // stop scrolling far right diff.x = (areabounds.right - currentbounds.right); } } else { // negate scrolling diff.x = (areabounds.left - currentbounds.left); } // y-direction if (currentbounds.height() > areabounds.height()) { // allow scrolling if amount of content tall @ scale if (currentbounds.top > areabounds.top) { // stop scrolling far above diff.y = (areabounds.top - currentbounds.top); } if (currentbounds.bottom < areabounds.bottom) { // stop scrolling far below diff.y = (areabounds.bottom - currentbounds.bottom); } } else { // negate scrolling diff.y = (areabounds.top - currentbounds.top); } // translate matrix.posttranslate(diff.x, diff.y); }
it negates scrolling don't want translating bounds. negates scrolling if content small, forcing content in top-left.
i implemented roll own pinch zoom. suspect problem y-axis being off centre may down view not being full screen size, or might not changing co-ordinates match scale.
my implementation calculates scale factor, applies with: canvas.scale( pinchzoomscale, pinchzoomscale ); calculates physical size of screen in pixels, converts metres, , applies scaling factor objects drawn offset correctly.
this implementation relies on knowing should @ centre of screen , locking on it, whatever zoom level.
Comments
Post a Comment