Android Scroller实现弹性滑动效果

来自:网络
时间:2022-08-07
阅读:

本文实例为大家分享了Android Scroller实现弹性滑动的具体代码,供大家参考,具体内容如下

首先看下实现效果,可以看到当我们手指松开时图片会逐渐滑动到初始位置,而不是直接跳变到中心点。

Android Scroller实现弹性滑动效果

代码实现

当手指触摸到view上时即TouchEvent位MotionEvent.ACTION_DOWN时,记录开始的坐标位置,同时由于手指再次按到屏幕上的的时候view还在执行动画,所以当动画还在执行的时候我们需要将动画停止。

if (!mScroller.isFinished()) {
    mScroller.abortAnimation();
                }
mStartX = (int) event.getX();
mStartY = (int) event.getY();

当然后当用户手指在屏幕上面滑动的时候,即event为MotionEvent.ACTION_MOVE时,我们需要将view的位置进行移动,这里我使用的是scrollBy的方式移动view的位置。

int curX = (int) event.getX();
int curY = (int) event.getY();
Log.i(TAG, "onTouchEvent: curX" + curX + "curY" + curY);
int delX = curX - mStartX;
int delY = curY - mStartY;
mStartX = curX;
mStartY = curY;
mViewGroup.scrollBy(-delX, -delY);

为什么使用scrollBy移动位置的时候前面还有个mViewGroup呢,因为我们在使用scrollBy/scrollTo的时候实际上移动的是view中内容,所以当我们想要移动view自身的时候那么就需要得到该view的parent,然后移动parent里面的内容,即我们需要移动的View,同时可以看到我们scrollBy方法中对变化的值取了负数,这个由于View内部计算滑动距离的两个属性的计算方式与我们平常使用的刚好相反。

Android Scroller实现弹性滑动效果

mScrollX用来记录横向滚动的距离:该属性的计算方式为: view左边缘位置减去view内容左边缘位置

Android Scroller实现弹性滑动效果

所以当我们滑动view到右侧的时候,我们需要对取变化距离的负值。

Android Scroller实现弹性滑动效果

mScrollY用来计算纵向滚动的距离:该属性的计算方式为: view上边缘位置减去view内容上边缘的位置

Android Scroller实现弹性滑动效果

紧接着当用的手指抬起的时候,即event为MotionEvent.ACTION_UP,我们需要将view平滑移动到起始位置。

case MotionEvent.ACTION_UP:
                mScroller.startScroll(mViewGroup.getScrollX(), mViewGroup.getScrollY(),
                        -mViewGroup.getScrollX(), -mViewGroup.getScrollY(), 1000);
                invalidate();// 在ui线程中调用
                break;

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            mViewGroup.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();// 在非ui线程中调用
        }
    }

这里我们使用了scroller进行平滑移动,查看startScroll的源码,这个函数其实并没有干什么

Android Scroller实现弹性滑动效果

该函数知识设置了一些参数,并没有移动view的位置。View的移动其实是由下面的invalidate()触发的,因为invalidate()会让view 重绘,重新绘制的时候会调用到view自身的draw()方法,而draw方法又会调用到computeScroll()方法,再computeScroll()方法中,我们首先判断判断当前的移动是否结束,没有结束的话通过getCurrX(),getCurrY()移动到当前动画所在位置,然后再次重新绘制view,然后继续调用draw,继续上面的过程,直到scroller结束即computeScrollOffset()返回false。

完整代码

使用的时候只需要将这个view,放置在xml中,并配置一个图片背景

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Main2Activity">

    <com.example.recyclerviewlearn.CustomizeImageView
        android:layout_centerInParent="true"
        android:background="@drawable/ic_launcher_background"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

下面是自定义ImageView的代码

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.Scroller;

public class CustomizeImageView extends androidx.appcompat.widget.AppCompatImageView {
    private static final String TAG = "CustomizeImageView";

    private ViewGroup mViewGroup;

    private int mStartX = 0;

    private int mStartY = 0;

    private Scroller mScroller = new Scroller(this.getContext());

    public CustomizeImageView(Context context) {
        super(context);
    }

    public CustomizeImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomizeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mViewGroup = (ViewGroup) getParent();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(TAG, "onTouchEvent: ");
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                mStartX = (int) event.getX();
                mStartY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i(TAG, "onTouchEvent: startX" + mStartX + "mStartY" + mStartY);
                int curX = (int) event.getX();
                int curY = (int) event.getY();
                Log.i(TAG, "onTouchEvent: curX" + curX + "curY" + curY);
                int delX = curX - mStartX;
                int delY = curY - mStartY;
                mStartX = curX;
                mStartY = curY;
                Log.i(TAG, "onTouchEvent: ACTION_MOVE");
                mViewGroup.scrollBy(-delX, -delY);
                break;
            case MotionEvent.ACTION_UP:
                mScroller.startScroll(mViewGroup.getScrollX(), mViewGroup.getScrollY(),
                        -mViewGroup.getScrollX(), -mViewGroup.getScrollY(), 1000);
                invalidate();
                break;
            default:
                break;
        }
        return true;
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            mViewGroup.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }
}

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

返回顶部
顶部