在我们的日常开发工作中,经常会遇到需要实现流畅的滚动效果的需求,例如ListView、RecycleView、ViewPager等控件,这些控件的内部都是利用了VelocityTracker来实现的流畅的滑动效果。因此,我们在开发前需要了解VelocityTracker的概念、使用方法和原理。
一、VelocityTracker 简介
VelocityTracker是Android提供的一个计算速度的工具类。我们可以通过VelocityTracker获取当前触摸事件的速度,然后利用该速度来实现我们所需的各种滑动效果。如果用一个图来表示它的工作原理的话,我们可以大致描述如下:
如上图所示,当我们手指在屏幕上拖动的时候,屏幕所显示的内容会随着手指的滑动而移动。此时,当手指短暂的离开屏幕并且再次接触屏幕的时候,我们就可以通过VelocityTracker计算出滑动速度,然后利用这个速度来实现惯性滑动动画、弹性滑动等效果。
二、VelocityTracker 的使用
接下来我们来介绍一下VelocityTracker的使用。首先,在滑动的控件中获取VelocityTracker对象,并在手指按钮屏幕的触摸事件中,对应的MotionEvent类型事件中传递事件,获取当前的速度,相关的代码如下:
```
public class MyScrollView extends ScrollView {
private VelocityTracker mVelocityTracker;
private float mLastY;
public MyScrollView(Context context) {
this(context, null);
}
public MyScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mVelocityTracker = VelocityTracker.obtain();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
mVelocityTracker.addMovement(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float deltaY = ev.getY() - mLastY;
mLastY = ev.getY();
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000);
int yVelocity = (int) mVelocityTracker.getYVelocity();
Log.d(TAG, "onTouchEvent yVelocity: " + yVelocity);
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
break;
}
return super.onTouchEvent(ev);
}
}
```
上述代码是自定义一个MyScrollView控件,我们可以通过scrollBy方法来实现滑动效果,需要注意的是getYVelocity方法计算出的速度单位是“每秒移动的像素数”,因此我们需要乘以1000,将速度的单位转化为“像素/秒”,便于控制滑动速度。之后我们就可以利用这个计算出的速度来实现各种滑动效果。
三、VelocityTracker在ListView中的应用
我们以ListView为例子来说明一下,如何利用VelocityTracker实现流畅的滑动效果,并对ListView进行优化。
1. 滑动速度优化
ListView在绑定大数据量的时候,滑动卡顿是一个非常让人头痛的问题,我们可以利用VelocityTracker来控制滑动速度,从而实现流畅的滑动效果。相关的代码如下:
```
@Override
public boolean onTouchEvent(MotionEvent ev) {
mVelocityTracker.addMovement(ev);
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float deltaY = ev.getY() - mLastY;
mLastY = ev.getY();
if (Math.abs(deltaY) > 0) {
//因为快速滑动时deltaY非常大,需要乘上一个系数来调节
int speedY = (int) (deltaY * mSpeedRatio);
int curPos = mTimeListContainer.getScrollY();
//滑动到顶部或底部时不进行滑动
if ((speedY < 0 && curPos == mMinY) || (speedY > 0 && curPos == mMaxY)) {
return super.onTouchEvent(ev);
}
if (curPos + speedY < mMinY) {
speedY = mMinY - curPos;
} else if (curPos + speedY > mMaxY) {
speedY = mMaxY - curPos;
}
mTimeListContainer.scrollBy(0, speedY);
}
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
int yVelocity = (int) mVelocityTracker.getYVelocity();
if (Math.abs(yVelocity) > mMinVelocity) {
fling(-yVelocity);
}
recycleVelocityTracker();
break;
case MotionEvent.ACTION_CANCEL:
recycleVelocityTracker();
break;
}
return super.onTouchEvent(ev);
}
```
在代码中,我们对快速滑动时速度进行了调节,利用scrollBy方法来控制ListView的滑动,并对速度进行了计算,当滑出屏幕时,利用fling方法来处理剩余的滑动,从而实现了流畅的滑动效果。
2. 列表项重复利用优化
ListView就是为了展示大量数据而生的,但是它也存在大量的无用浪费,其中一部分是Scrollview的原因导致的,另外一部分是ListView重复创建和重复回收ItemView引起的。因此,我们需要进行最大程度的优化,可以通过ListView的setRecycleViewPool方法来设置ItemView的缓存池,从而优化我们的列表项效率。
```
private RecyclerView.RecycledViewPool mRecycleViewPool = new RecyclerView.RecycledViewPool();
...
public MyAdapter(Context context, List
mContext = context;
mDataList = dataList;
mInflater = LayoutInflater.from(context);
mRecycleViewPool.setMaxRecycledViews(0, 30);
}
...
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mRecycleViewPool.getRecycledView(viewType);
if (view == null) {
view = mInflater.inflate(R.layout.list_item, null);
}
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.mTextView.setText(mDataList.get(position));
}
@Override
public int getItemCount() {
return mDataList.size();
}
@Override
public int getItemViewType(int position) {
return 0;
}
@Override
public void onViewRecycled(MyViewHolder holder) {
super.onViewRecycled(holder);
mRecycleViewPool.putRecycledView(holder.itemView);
}
```
以上代码中,我们在RecyclerView.RecycledViewPool中设置了最大的缓存数量,当当前的ItemView不在使用的时候,系统会将其回收到缓存池中,当需要新的ItemView的时候,进行重复利用,并减少View的创建和销毁所带来的开销。
四、总结
通过对VelocityTracker的使用,大家应该对其有了更深刻的理解了。在实现流畅的滚动效果时,我们需要考虑多方面的因素,例如滑动速率调节、惯性滑动、弹性滑动等方面的问题。因此,我们在使用VelocityTracker的时候需要结合具体的业务需求,进行综合考虑,从而实现更流畅的、更优质的滑动效果。