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

 更新时间:2022年04月18日 16:27:23   作者:Chen-Zehao  
这篇文章主要为大家详细介绍了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);
    }

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Android组件实现列表选择框功能

    Android组件实现列表选择框功能

    android提供的列表选择框(Spinner)相当于web端用户注册时的选择下拉框,比如注册候选择省份城市等。这篇文章主要介绍了Android组件实现列表选择框功能,需要的朋友可以参考下
    2017-02-02
  • Android ViewPager实现左右滑动的实例

    Android ViewPager实现左右滑动的实例

    这篇文章主要介绍了Android ViewPager实现左右滑动的实例的相关资料,这里提供实现代码实现左右滑动的功能,希望能帮助到大家,需要的朋友可以参考下
    2017-08-08
  • Android实现手写板功能

    Android实现手写板功能

    这篇文章主要为大家详细介绍了Android实现手写板功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • Android开发简易音乐播放器

    Android开发简易音乐播放器

    这篇文章主要为大家详细介绍了Android开发简易音乐播放器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • Android开发之经典游戏贪吃蛇

    Android开发之经典游戏贪吃蛇

    贪吃蛇是一款足够经典的游戏。它的经典,在于用户操作的简单,在于技术实现的简介,在于他的经久不衰。下面来看下如何在Android开发这款经典游戏。
    2016-07-07
  • Android应用接入微信分享的实例代码

    Android应用接入微信分享的实例代码

    本篇文章主要介绍了Android应用接入微信分享的实例代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-07-07
  • Android 简单服务定位器模式实现

    Android 简单服务定位器模式实现

    这篇文章主要介绍了Android 简单服务定位器模式实现,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下
    2021-03-03
  • Android巧用Fragment解耦onActivityResult详解

    Android巧用Fragment解耦onActivityResult详解

    这篇文章主要给大家介绍了关于Android巧用Fragment解耦onActivityResult的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-08-08
  • Android自定义控件实现随手指移动的小球

    Android自定义控件实现随手指移动的小球

    这篇文章主要为大家详细介绍了Android自定义控件实现随手指移动的小球,随着手指触摸移动而移动的效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Android右滑返回上一个界面的实现方法

    Android右滑返回上一个界面的实现方法

    这篇文章主要介绍了Android右滑返回上一个界面的实现方法的相关资料,希望通过本文能帮助到大家,让大家实现这样的功能,需要的朋友可以参考下
    2017-10-10

最新评论