Android实现橡皮筋回弹和平移缩放效果

Android实现橡皮筋回弹和平移缩放效果

本文实例为大家分享了Android实现橡皮筋回弹和平移缩放的具体代码,供大家参考,具体内容如下

前言

由于最近在做一个view的平移缩放功能以及橡皮筋效果,不过网上查到的大多数都是分开实现的,所以我这里把这两种功能整合到了一起

代码实现

这里我写把效果分开来写,最后再合并

平移、缩放

mLayout.java

import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.FrameLayout; import android.widget.Scroller; /**  * Created by ChenZehao  * on 2019/8/4  */ public class mLayout extends FrameLayout{     // 属性变量     private float translationX; // 移动X     private float translationY; // 移动Y     private float scale = 1; // 伸缩比例     // 移动过程中临时变量     private float actionX;     private float actionY;     private float spacing;     private int moveType; // 0=未选择,1=拖动,2=缩放     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) {         super.onTouchEvent(event);         switch (event.getAction() & MotionEvent.ACTION_MASK) {             case MotionEvent.ACTION_DOWN:                 moveType = 1;                 actionX = event.getRawX();                 actionY = event.getRawY();                 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 + event.getRawX() - actionX;                     translationY = translationY + event.getRawY() - actionY;                     System.out.println();                     setTranslationX(translationX);                     setTranslationY(translationY);                     actionX = event.getRawX();                     actionY = event.getRawY();                 }                 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;     }     // 触碰两点间距离     private float getSpacing(MotionEvent event) {         //通过三角函数得到两点间的距离         float x = event.getX(0) - event.getX(1);         float y = event.getY(0) - event.getY(1);         return (float) Math.sqrt(x * x + y * y);     } }

橡皮筋回弹

import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.FrameLayout; import android.widget.Scroller; /**  * Created by ChenZehao  * on 2019/8/4  */ public class mLayout extends FrameLayout{     //系数可自己更改     private static final float DEFAULT_FATOR = 0.4f;     /**      * 阻尼因子      */     private float mFator = DEFAULT_FATOR;     private Scroller mScroller;     /**      * 记录上一次触摸事件      */     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) {         super.onTouchEvent(event);         switch (event.getAction()) {             case MotionEvent.ACTION_DOWN:                 mLastMotionEvent = MotionEvent.obtain(event);                 break;             case MotionEvent.ACTION_MOVE:                 int dx = (int) (event.getRawX() - mLastMotionEvent.getRawX());                 int dy = (int) (event.getRawY() - mLastMotionEvent.getRawY());                 //如果不想对四个方向增加阻尼效果,直接删除即可                 //向上平移                 if ((Math.abs(dx) < Math.abs(dy)) && dy < 0){                     smoothScrollBy(0, -(int) (dy * mFator));                 }                 //向下平移                 else if (Math.abs(dx) < Math.abs(dy) && dy > 0) {                     smoothScrollBy(0, -(int) (dy * mFator));                 }                 //向左平移                 else if (Math.abs(dx) > Math.abs(dy) && dx < 0){                     smoothScrollBy(-(int) (dx * mFator), 0);                 }                 //向右平移                 else if (Math.abs(dx) > Math.abs(dy) && dx > 0){                     smoothScrollBy(-(int) (dx * mFator), 0);                 }                 mLastMotionEvent = MotionEvent.obtain(event);                 break;             case MotionEvent.ACTION_UP:             case MotionEvent.ACTION_CANCEL:                 smoothScrollTo(0, 0);                 break;         }         return true;     }     private void smoothScrollBy(int dx, int dy) {         mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);         invalidate();     }     private void smoothScrollTo(int fx, int fy) {         int dx = fx - mScroller.getFinalX();         int dy = fx - mScroller.getFinalY();         smoothScrollBy(dx, dy);     }     @Override     public void computeScroll() {         if (mScroller.computeScrollOffset()) {             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());             postInvalidate();         }         super.computeScroll();     } }

平移、缩放、阻尼效果合并

import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.FrameLayout; import android.widget.Scroller; /**  * Created by ChenZehao  * on 2019/8/4  */ public class mLayout extends FrameLayout{     private float scale = 1; // 伸缩比例     // 移动过程中临时变量     private float actionX;     private float actionY;     private float spacing;     private int moveType; // 0=未选择,1=拖动,2=缩放     private float firstX;     private float firstY;     //系数可自己更改     private static final float DEFAULT_FATOR = 0.4f;     /**      * 阻尼因子      */     private float mFator = DEFAULT_FATOR;     private Scroller mScroller;     /**      * 记录上一次触摸事件      */     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) {         super.onTouchEvent(event);         switch (event.getAction() & MotionEvent.ACTION_MASK) {             case MotionEvent.ACTION_DOWN:                 mLastMotionEvent = MotionEvent.obtain(event);                 moveType = 1;                 actionX = event.getRawX();                 actionY = event.getRawY();                 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) (event.getRawX() - mLastMotionEvent.getRawX());                     int dy = (int) (event.getRawY() - mLastMotionEvent.getRawY());                     //如果不想对四个方向增加阻尼效果,直接删除即可                     //向上平移                     if ((Math.abs(dx) < Math.abs(dy)) && dy < 0){                         smoothScrollBy(0, -(int) (dy * mFator));                     }                     //向下平移                     else if (Math.abs(dx) < Math.abs(dy) && dy > 0) {                         smoothScrollBy(0, -(int) (dy * mFator));                     }                     //向左平移                     else if (Math.abs(dx) > Math.abs(dy) && dx < 0){                         smoothScrollBy(-(int) (dx * mFator), 0);                     }                     //向右平移                     else if (Math.abs(dx) > Math.abs(dy) && dx > 0){                         smoothScrollBy(-(int) (dx * mFator), 0);                     }                     mLastMotionEvent = MotionEvent.obtain(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) {         mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);         invalidate();     }     private void smoothScrollTo(int fx, int fy) {         int dx = fx - mScroller.getFinalX();         int dy = fx - mScroller.getFinalY();         smoothScrollBy(dx, dy);     }     @Override     public void computeScroll() {         if (mScroller.computeScrollOffset()) {             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());             postInvalidate();         }         super.computeScroll();     }     // 触碰两点间距离     private float getSpacing(MotionEvent event) {         //通过三角函数得到两点间的距离         float x = event.getX(0) - event.getX(1);         float y = event.getY(0) - event.getY(1);         return (float) Math.sqrt(x * x + y * y);     } } 使用方法

在xml文件中添加mLayout布局,便可对mLayout里面的控件和布局进行平移、缩放、阻尼效果的操作

功能扩展——在布局中添加button

如果我们在mLayout布局中添加button,那么会出现获取焦点冲突的问题,导致触摸到按钮时无法进行平移等操作,因此我们需要重写button的dispatchTouchEvent函数,因此要创建一个类mButton来继承Button

点击时事件被button获取,因此要将事件通过dispatchTouchEvent回传给父view,再调用父view的onInterceptTouchEvent函数对拦截到的事件进行处理。

代码如下:

mButton.java

import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; public class mButton extends android.support.v7.widget.AppCompatButton {     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 (ev.getAction()) {             case MotionEvent.ACTION_DOWN:                 getParent().requestDisallowInterceptTouchEvent(false);                 break;             case MotionEvent.ACTION_MOVE:                 break;             case MotionEvent.ACTION_UP:             case MotionEvent.ACTION_POINTER_UP:                 break;         }         return super.dispatchTouchEvent(ev);     } }

mLayout.java

import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.FrameLayout; import android.widget.Scroller; /**  * Created by ChenZehao  * on 2019/8/4  */ public class mLayout extends FrameLayout{     private float scale = 1; // 伸缩比例     // 移动过程中临时变量     private float actionX;     private float actionY;     private float spacing;     private int moveType; // 0=未选择,1=拖动,2=缩放     private float firstX;     private float firstY;     //系数可自己更改     private static final float DEFAULT_FATOR = 0.4f;     /**      * 阻尼因子      */     private float mFator = DEFAULT_FATOR;     private Scroller mScroller;     /**      * 记录上一次触摸事件      */     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) {         super.onTouchEvent(event);         switch (event.getAction() & MotionEvent.ACTION_MASK) {             case MotionEvent.ACTION_DOWN:                 mLastMotionEvent = MotionEvent.obtain(event);                 moveType = 1;                 actionX = event.getRawX();                 actionY = event.getRawY();                 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) (event.getRawX() - mLastMotionEvent.getRawX());                     int dy = (int) (event.getRawY() - mLastMotionEvent.getRawY());                     //如果不想对四个方向增加阻尼效果,直接删除即可                     //向上平移                     if ((Math.abs(dx) < Math.abs(dy)) && dy < 0){                         smoothScrollBy(0, -(int) (dy * mFator));                     }                     //向下平移                     else if (Math.abs(dx) < Math.abs(dy) && dy > 0) {                         smoothScrollBy(0, -(int) (dy * mFator));                     }                     //向左平移                     else if (Math.abs(dx) > Math.abs(dy) && dx < 0){                         smoothScrollBy(-(int) (dx * mFator), 0);                     }                     //向右平移                     else if (Math.abs(dx) > Math.abs(dy) && dx > 0){                         smoothScrollBy(-(int) (dx * mFator), 0);                     }                     mLastMotionEvent = MotionEvent.obtain(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;     }     //拦截子button的事件     @Override     public boolean onInterceptTouchEvent(MotionEvent event) {         switch (event.getAction() & MotionEvent.ACTION_MASK){             case MotionEvent.ACTION_DOWN:                 mLastMotionEvent = MotionEvent.obtain(event);                 moveType = 1;                 actionX = event.getRawX();                 actionY = event.getRawY();                 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) (event.getRawX() - mLastMotionEvent.getRawX());                     int dy = (int) (event.getRawY() - mLastMotionEvent.getRawY());                     //如果不想对四个方向增加阻尼效果,直接删除即可                     //向上平移                     if ((Math.abs(dx) < Math.abs(dy)) && dy < 0){                         smoothScrollBy(0, -(int) (dy * mFator));                     }                     //向下平移                     else if (Math.abs(dx) < Math.abs(dy) && dy > 0) {                         smoothScrollBy(0, -(int) (dy * mFator));                     }                     //向左平移                     else if (Math.abs(dx) > Math.abs(dy) && dx < 0){                         smoothScrollBy(-(int) (dx * mFator), 0);                     }                     //向右平移                     else if (Math.abs(dx) > Math.abs(dy) && dx > 0){                         smoothScrollBy(-(int) (dx * mFator), 0);                     }                     mLastMotionEvent = MotionEvent.obtain(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 != event.getRawX() || firstY != event.getRawY())                     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 super.onInterceptTouchEvent(event);     }     private void smoothScrollBy(int dx, int dy) {         mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);         invalidate();     }     private void smoothScrollTo(int fx, int fy) {         int dx = fx - mScroller.getFinalX();         int dy = fx - mScroller.getFinalY();         smoothScrollBy(dx, dy);     }     @Override     public void computeScroll() {         if (mScroller.computeScrollOffset()) {             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());             postInvalidate();         }         super.computeScroll();     }     // 触碰两点间距离     private float getSpacing(MotionEvent event) {         //通过三角函数得到两点间的距离         float x = event.getX(0) - event.getX(1);         float y = event.getY(0) - event.getY(1);         return (float) Math.sqrt(x * x + y * y);     } }

推荐阅读