This example shares the specific code for Android to implement elastic band rebound and pan zoom for your reference. The specific content is as follows
Preface
Since I have been doing a view's translation and scaling function and rubber band effect recently, most of the ones I found online are implemented separately, so I have integrated these two functions together
Code implementation
Here I write the effects separately and finally merge them
Pan, zoom
import ; import ; import ; import ; import ; /** * Created by ChenZehao * on 2019/8/4 */ public class mLayout extends FrameLayout{ // Attribute variables private float translationX; // Mobile X private float translationY; // Mobile Y private float scale = 1; // Scaling ratio // Temporary variables during movement private float actionX; private float actionY; private float spacing; private int moveType; // 0=Not selected, 1=Drag, 2=Scale private float firstX; private float firstY; public mLayout(Context context) { this(context, null); } public mLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public mLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onTouchEvent(MotionEvent event) { (event); switch (() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: moveType = 1; actionX = (); actionY = (); firstX = actionX; firstY = actionY; break; case MotionEvent.ACTION_POINTER_DOWN: moveType = 2; spacing = getSpacing(event); break; case MotionEvent.ACTION_MOVE: if (moveType == 1) { translationX = translationX + () - actionX; translationY = translationY + () - actionY; (); setTranslationX(translationX); setTranslationY(translationY); actionX = (); actionY = (); } else if (moveType == 2) { scale = scale * getSpacing(event) / spacing; if(scale >= 1){ setScaleX(scale); setScaleY(scale); }else{ scale = 1; } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: moveType = 0; break; } return true; } // Touch the distance between two points private float getSpacing(MotionEvent event) { //Get the distance between two points through the trigonometric function float x = (0) - (1); float y = (0) - (1); return (float) (x * x + y * y); } }
Rubber band rebound
import ; import ; import ; import ; import ; /** * Created by ChenZehao * on 2019/8/4 */ public class mLayout extends FrameLayout{ //The coefficient can be changed by yourself private static final float DEFAULT_FATOR = 0.4f; /** * Damping factor */ private float mFator = DEFAULT_FATOR; private Scroller mScroller; /** * Record the last touch event */ private MotionEvent mLastMotionEvent; public mLayout(Context context) { this(context, null); } public mLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public mLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScroller = new Scroller(context); } @Override public boolean onTouchEvent(MotionEvent event) { (event); switch (()) { case MotionEvent.ACTION_DOWN: mLastMotionEvent = (event); break; case MotionEvent.ACTION_MOVE: int dx = (int) (() - ()); int dy = (int) (() - ()); //If you don't want to add damping effect to the four directions, just delete it //Play up if (((dx) < (dy)) && dy < 0){ smoothScrollBy(0, -(int) (dy * mFator)); } //Play down else if ((dx) < (dy) && dy > 0) { smoothScrollBy(0, -(int) (dy * mFator)); } //Play to the left else if ((dx) > (dy) && dx < 0){ smoothScrollBy(-(int) (dx * mFator), 0); } //Play to the right else if ((dx) > (dy) && dx > 0){ smoothScrollBy(-(int) (dx * mFator), 0); } mLastMotionEvent = (event); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: smoothScrollTo(0, 0); break; } return true; } private void smoothScrollBy(int dx, int dy) { ((), (), dx, dy); invalidate(); } private void smoothScrollTo(int fx, int fy) { int dx = fx - (); int dy = fx - (); smoothScrollBy(dx, dy); } @Override public void computeScroll() { if (()) { scrollTo((), ()); postInvalidate(); } (); } }
Translation, zoom, damping effects merge
import ; import ; import ; import ; import ; /** * Created by ChenZehao * on 2019/8/4 */ public class mLayout extends FrameLayout{ private float scale = 1; // Scaling ratio // Temporary variables during movement private float actionX; private float actionY; private float spacing; private int moveType; // 0=Not selected, 1=Drag, 2=Scale private float firstX; private float firstY; //The coefficient can be changed by yourself private static final float DEFAULT_FATOR = 0.4f; /** * Damping factor */ private float mFator = DEFAULT_FATOR; private Scroller mScroller; /** * Record the last touch event */ private MotionEvent mLastMotionEvent; public mLayout(Context context) { this(context, null); } public mLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public mLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScroller = new Scroller(context); } @Override public boolean onTouchEvent(MotionEvent event) { (event); switch (() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mLastMotionEvent = (event); moveType = 1; actionX = (); actionY = (); firstX = actionX; firstY = actionY; break; case MotionEvent.ACTION_POINTER_DOWN: moveType = 2; spacing = getSpacing(event); break; case MotionEvent.ACTION_MOVE: if (moveType == 1) { int dx = (int) (() - ()); int dy = (int) (() - ()); //If you don't want to add damping effect to the four directions, just delete it //Play up if (((dx) < (dy)) && dy < 0){ smoothScrollBy(0, -(int) (dy * mFator)); } //Play down else if ((dx) < (dy) && dy > 0) { smoothScrollBy(0, -(int) (dy * mFator)); } //Play to the left else if ((dx) > (dy) && dx < 0){ smoothScrollBy(-(int) (dx * mFator), 0); } //Play to the right else if ((dx) > (dy) && dx > 0){ smoothScrollBy(-(int) (dx * mFator), 0); } mLastMotionEvent = (event); } else if (moveType == 2) { scale = scale * getSpacing(event) / spacing; if(scale >= 1){ setScaleX(scale); setScaleY(scale); }else{ scale = 1; } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_CANCEL: moveType = 0; if(scale == 1) smoothScrollTo(0, 0); break; } return true; } private void smoothScrollBy(int dx, int dy) { ((), (), dx, dy); invalidate(); } private void smoothScrollTo(int fx, int fy) { int dx = fx - (); int dy = fx - (); smoothScrollBy(dx, dy); } @Override public void computeScroll() { if (()) { scrollTo((), ()); postInvalidate(); } (); } // Touch the distance between two points private float getSpacing(MotionEvent event) { //Get the distance between two points through the trigonometric function float x = (0) - (1); float y = (0) - (1); return (float) (x * x + y * y); } }
How to use
Add mLayout layout to the xml file to pan, scale, and dampen the controls and layouts in mLayout
Functional extension – add buttons to layout
If we add buttons in mLayout layout, we will have the problem of getting focus conflicts, which will lead to the inability to pan and other operations when touching the button. Therefore, we need to rewrite the button's dispatchTouchEvent function, so we need to create a class mButton to inherit the Button
The event is retrieved by the button when clicking, so the event must be returned to the parent view through dispatchTouchEvent, and then the onInterceptTouchEvent function of the parent view is called to process the intercepted event.
The code is as follows:
import ; import ; import ; public class mButton extends . { public mButton(Context context) { super(context); } public mButton(Context context, AttributeSet attrs) { super(context, attrs); } public mButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (()) { case MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(false); break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: break; } return (ev); } }
import ; import ; import ; import ; import ; /** * Created by ChenZehao * on 2019/8/4 */ public class mLayout extends FrameLayout{ private float scale = 1; // Scaling ratio // Temporary variables during movement private float actionX; private float actionY; private float spacing; private int moveType; // 0=Not selected, 1=Drag, 2=Scale private float firstX; private float firstY; //The coefficient can be changed by yourself private static final float DEFAULT_FATOR = 0.4f; /** * Damping factor */ private float mFator = DEFAULT_FATOR; private Scroller mScroller; /** * Record the last touch event */ private MotionEvent mLastMotionEvent; public mLayout(Context context) { this(context, null); } public mLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public mLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScroller = new Scroller(context); } @Override public boolean onTouchEvent(MotionEvent event) { (event); switch (() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mLastMotionEvent = (event); moveType = 1; actionX = (); actionY = (); firstX = actionX; firstY = actionY; break; case MotionEvent.ACTION_POINTER_DOWN: moveType = 2; spacing = getSpacing(event); break; case MotionEvent.ACTION_MOVE: if (moveType == 1) { int dx = (int) (() - ()); int dy = (int) (() - ()); //If you don't want to add damping effect to the four directions, just delete it //Play up if (((dx) < (dy)) && dy < 0){ smoothScrollBy(0, -(int) (dy * mFator)); } //Play down else if ((dx) < (dy) && dy > 0) { smoothScrollBy(0, -(int) (dy * mFator)); } //Play to the left else if ((dx) > (dy) && dx < 0){ smoothScrollBy(-(int) (dx * mFator), 0); } //Play to the right else if ((dx) > (dy) && dx > 0){ smoothScrollBy(-(int) (dx * mFator), 0); } mLastMotionEvent = (event); } else if (moveType == 2) { scale = scale * getSpacing(event) / spacing; if(scale >= 1){ setScaleX(scale); setScaleY(scale); }else{ scale = 1; } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_CANCEL: moveType = 0; if(scale == 1) smoothScrollTo(0, 0); break; } return true; } //Intercepting the event of subbutton @Override public boolean onInterceptTouchEvent(MotionEvent event) { switch (() & MotionEvent.ACTION_MASK){ case MotionEvent.ACTION_DOWN: mLastMotionEvent = (event); moveType = 1; actionX = (); actionY = (); firstX = actionX; firstY = actionY; break; case MotionEvent.ACTION_POINTER_DOWN: moveType = 2; spacing = getSpacing(event); break; case MotionEvent.ACTION_MOVE: if (moveType == 1) { int dx = (int) (() - ()); int dy = (int) (() - ()); //If you don't want to add damping effect to the four directions, just delete it //Play up if (((dx) < (dy)) && dy < 0){ smoothScrollBy(0, -(int) (dy * mFator)); } //Play down else if ((dx) < (dy) && dy > 0) { smoothScrollBy(0, -(int) (dy * mFator)); } //Play to the left else if ((dx) > (dy) && dx < 0){ smoothScrollBy(-(int) (dx * mFator), 0); } //Play to the right else if ((dx) > (dy) && dx > 0){ smoothScrollBy(-(int) (dx * mFator), 0); } mLastMotionEvent = (event); } else if (moveType == 2) { scale = scale * getSpacing(event) / spacing; if(scale >= 1){ setScaleX(scale); setScaleY(scale); }else{ scale = 1; } } break; case MotionEvent.ACTION_UP: moveType = 0; if(scale == 1) smoothScrollTo(0, 0); if(firstX != () || firstY != ()) return true; break; case MotionEvent.ACTION_POINTER_UP: moveType = 0; if(scale == 1) smoothScrollTo(0, 0); break; case MotionEvent.ACTION_CANCEL: moveType = 0; if(scale == 1) smoothScrollTo(0, 0); break; } return (event); } private void smoothScrollBy(int dx, int dy) { ((), (), dx, dy); invalidate(); } private void smoothScrollTo(int fx, int fy) { int dx = fx - (); int dy = fx - (); smoothScrollBy(dx, dy); } @Override public void computeScroll() { if (()) { scrollTo((), ()); postInvalidate(); } (); } // Touch the distance between two points private float getSpacing(MotionEvent event) { //Get the distance between two points through the trigonometric function float x = (0) - (1); float y = (0) - (1); return (float) (x * x + y * y); } }
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.