Android Dragging and Scaling in FrameLayout -


recently have been trying implement dragging , scaling on picture place in framelayout. want achieve simple: able drag picture around , zoom it. went android developer website , followed guide there.

then following code examples on website wrote mycustomview:

public class mycustomview extends imageview { private static final int invalid_pointer_id = 0xdeadbeef; private scalegesturedetector mscaledetector; private float mscalefactor = 1.f; private float mlasttouchx, mlasttouchy; private int mactivepointerid = invalid_pointer_id; private layoutparams mlayoutparams; private int mposx, mposy;  public mycustomview(context context) {     super(context);      mscaledetector = new scalegesturedetector(context, new customscalelistener());     mlayoutparams = (layoutparams) super.getlayoutparams();     if (mlayoutparams != null) {         mposx = mlayoutparams.leftmargin;         mposy = mlayoutparams.topmargin;     } else {         mlayoutparams = new layoutparams(300, 300);         mlayoutparams.leftmargin = 0;         mlayoutparams.topmargin = 0;     } }  @override public void ondraw(canvas canvas) {     super.ondraw(canvas);      canvas.save();     canvas.scale(mscalefactor, mscalefactor);      canvas.restore(); }  @override public boolean ontouchevent(motionevent ev) {     // let scalegesturedetector inspect events     mscaledetector.ontouchevent(ev);      final int action = motioneventcompat.getactionmasked(ev);      switch (action) {         case motionevent.action_down: {             final int pointerindex = motioneventcompat.getactionindex(ev);              //final float x = motioneventcompat.getx(ev, pointerindex);             //final float y = motioneventcompat.gety(ev, pointerindex);             final float x = ev.getrawx();             final float y = ev.getrawy();              // remember started (for dragging)             mlasttouchx = x;             mlasttouchy = y;             // save id of pointer (for dragging)             mactivepointerid = motioneventcompat.getpointerid(ev, 0);             break;         }          case motionevent.action_move: {             // find index of active pointer , fetch position             final int pointerindex = motioneventcompat.findpointerindex(ev, mactivepointerid);              //final float x = motioneventcompat.getx(ev, pointerindex);             //final float y = motioneventcompat.gety(ev, pointerindex);             final float x = ev.getrawx();             final float y = ev.getrawy();              final float dx = x - mlasttouchx;             final float dy = y - mlasttouchy;              //todo: update location of view             mposx += dx;             mposy += dy;              mlayoutparams.leftmargin += dx;             mlayoutparams.topmargin += dy;             super.setlayoutparams(mlayoutparams);              invalidate();               mlasttouchx = x;             mlasttouchy = y;             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 = motioneventcompat.getactionindex(ev);             final int pointerid = motioneventcompat.getpointerid(ev, pointerindex);              if (pointerid == mactivepointerid) {                 // our active pointer going up. choose new active pointer ,                 // adjust accordingly                 final int newpointerindex = pointerindex == 0 ? 1 : 0;                 //mlasttouchx = motioneventcompat.getx(ev, newpointerindex);                 //mlasttouchy = motioneventcompat.gety(ev, newpointerindex);                 mlasttouchx = ev.getrawx();                 mlasttouchy = ev.getrawy();                 mactivepointerid = motioneventcompat.getpointerid(ev, newpointerindex);             }              break;         }     }     return true; }     private class customscalelistener extends scalegesturedetector.simpleonscalegesturelistener {     @override     public boolean onscale(scalegesturedetector detector) {         mscalefactor *= detector.getscalefactor();          mscalefactor = math.max(0.1f, math.min(mscalefactor, 5.0f));          invalidate();          return true;     } } 

in mainactivity instantiated mycustomview object , attached viewgroup @ background, framelayout. xml file has nothing framelayout there.

    public class mainactivity extends appcompatactivity {     private viewgroup layoutroot;      @override     protected void oncreate(bundle savedinstancestate) {         super.oncreate(savedinstancestate);         setcontentview(r.layout.activity_main);          layoutroot = (viewgroup) findviewbyid(r.id.view_root);          final mycustomview ivandroid = new mycustomview(this);         ivandroid.setimageresource(r.mipmap.ic_launcher);         ivandroid.setlayoutparams(new framelayout.layoutparams(300, 300));         layoutroot.addview(ivandroid);     } } 

and here comes problem troubles me: android developer website uses obtain coordinates of finger touches picture:

final float x = motioneventcompat.getx(ev, pointerindex); final float y = motioneventcompat.gety(ev, pointerindex); 

but works horribly! picture moves, not follow finger exactly, moves less finger does, , importantly, flashes.

so why can see in mycustomview have commented out line , instead used code:

final float x = ev.getrawx(); final float y = ev.getrawy(); 

while time picture moves smoothly in accordance finger, change introduces new problem. on android developer website dragging , scaling, there design principle says:

in drag (or scroll) operation, app has keep track of original pointer (finger), if additional fingers placed on screen. example, imagine while dragging image around, user places second finger on touch screen , lifts first finger. if app tracking individual pointers, regard second pointer default , move image location.

after started using ev.getrawx() , ev.getrawy(), adding second finger screen gives me problem stated above. motioneventcompat.getx(ev, pointerindex) , motioneventcompat.gety(ev, pointerindex) not.

can me explain why happens? know motioneventcompat.getx(ev, pointerindex) returns coordinate after sort of adjustment, , ev.getrawx() returns absolute coordinate. don't understand how adjustment works (is there formula or graphical explanation it?). want know why using motioneventcompat.getx(...) prevent picture jumping second finger on screen (after first finger has been lifted).

last not least, scaling code doesn't work @ all. if , teach me on appreciated!

this question long, partionate in smaller bits. also, english not native language had difficulties writting answer. comment if part not clear.

can me explain why happens?

getrawx() , ev.getrawy() both give absolute pixel value of event. (for sake of backwards compatibility, when screens track 1 "region" @ time) consider finger first (and only) finger interacting device.

then, came improvements allowed track finger id., motioneventcompat.getx(ev, pointerindex) , motioneventcompat.gety(ev, pointerindex) functions allowed further finesse when creating our ontouch() listeners.

is there formula or graphical explanation it?

basically, need take consideration "screen density" of device. such as:

float screen_density = getresources().getdisplaymetrics().density;  protected void updateframe(framelayout framelayout, int h, int w, int x, int y) {     framelayout.layoutparams params = new framelayout.layoutparams(             (int) ((w * screen_density) + 0.5),             (int) ((h * screen_density) + 0.5)     );     params.leftmargin = (int) ((x * screen_density) + 0.5);     params.topmargin = (int) ((y * screen_density) + 0.5);     framelayout.setlayoutparams(params); } 

i want know why using motioneventcompat.getx(...) prevent picture jumping second finger on screen (after first finger has been lifted)

if take consideration first "finger" lifted, new one, has different "initial point", , different "history", because of that, can send event in relation movement made, not final position on screen. way wont "jump finger is" move according ammount of "x units" , "y units" traversed.

last not least, scaling code doesn't work @ all. if , teach me on appreciated!

you consuming event (by returning true on ontouch listener), because of that, no other listener can continue reading event, in way can trigger more listeners.

if desire, move both functions (move , resize) inside ontouch. ontouch listener has on 1700 lines of code (because lot of stuff, including programatically creating views , adding listeners that), cant post here, basically:

1 finger = move frame. raw values, , use "updateframe" 2 fingers = resize frame. raw values, , use "updateframe" 3+ fingers = drop first finger, suppose 2 fingers. 

Comments

Popular posts from this blog

Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12:test (default-test) on project.Error occurred in starting fork -

windows - Debug iNetMgr.exe unhandle exception System.Management.Automation.CmdletInvocationException -

configurationsection - activeMq-5.13.3 setup configurations for wildfly 10.0.0 -