Scroller弹性滑动
Android系统的滑动效果往往比较生硬,用户体验和粘着性方面如果要提高的话,弹性滑动是比较好的利器。实现
弹性滑动可以有好几种方式,其中利用Scroller实现弹性滑动是一种不错的方式。
首先,从源码层面看一下为什么Scroller为什么能实现弹性滑动。
/**
* 滚动到目标位置
*
* @param destX
* @param destY
*/
protected void smoothScrollTo(int destX, int destY) {
int scrollX = mScroller.getFinalX()
int scrollY = mScroller.getFinalY()
int dx = destX - scrollX;
int dy = destY - scrollY
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
invalidate();
}
@Override
public void computeScroll() {
//判断mScroller滚动是否完成
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
//必须调用该方法,否则不一定能看到滚动效果
postInvalidate();
}
super.computeScroll();
}
smoothScrollTo(int destX, int destY)方法的两个参数分别是View子空间(内容)滑动的目标横坐标和纵坐标。
mScroller.getFinalX()表示的是运动前的横坐标,所以int dx = destX - mScroller.getFinalX()表示的就是运动的相对
距离。对于纵纵坐标也是如此。
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy),这个方法的参数有必要说明一下。第
一、二个参数分别表示的是View内容的原始坐标,第三、四个参数表示的是运动的相对距离(单位是像素),最后
一个参数表示的运动的时间长度。
invalidate()非常重要,只有调用这个方法才能后面的computeScroll()方法的调用,否则的话不一定会刷新界面,用户就会
看不到滚动效果。
再看computeScroll()方法,正是因为这个方法View才实现了弹性滑动。其中,mScroller.computeScrollOffset()返回的是一个
bool量,代表是否完成滑动,如果完成了滑动则返回true,否则返回false。
当View重绘后会在draw方法中调用computerScroll,而computerScroll又会去向Scroller获取当前的scrollX和scrollY;然后通过
scrollTo方法实现滑动;接着又调用postInvalidate方法来进行第二次重绘,这次重绘的过程和第一次重绘一样,还是会导致computerScroll
方法被调用;然后继续向scroller获取当前的scrollerX和scrollerY,并通过scrollTo方法滑动到新的位置,如是反复,直到整个滑动过程结束。
下面附上一段项目代码,该代码利用了Scroller实现了弹性滑动的LinearLayout布局视图。希望给大家带来帮助。
public class ElasticLinearLayout extends LinearLayout {
private static final String TAG = ElasticLinearLayout.class.getSimpleName();
private Scroller mScroller;
private GestureDetector mGestureDetector;
public ElasticLinearLayout(Context context) {
this(context, null);
}
public ElasticLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setClickable(true);
setLongClickable(true);
mScroller = new Scroller(context);
mGestureDetector = new GestureDetector(context, new GestureDetector.OnGestureListener(){
@Override
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
return true;
}
@Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
/**
*
* @param e1 第1个ACTION_DOWN MotionEvent
* @param e2 最后一个ACTION_MOVE MotionEvent
* @param distanceX 距离上次产生onScroll事件后,X抽移动的距离
* @param distanceY 距离上次产生onScroll事件后,Y抽移动的距离
* @return
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
int disX = (int) ((distanceX - 0.5) / 2);
int disY = (int) ((distanceY - 0.5) / 2);
smoothScrollBy(disX, disY);
return false;
}
@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// TODO Auto-generated method stub
return false;
}
});
}
/**
* 滚动到目标位置
*
* @param destX
* @param destY
*/
protected void smoothScrollTo(int destX, int destY) {
int dx = destX - mScroller.getFinalX();
int dy = destY - mScroller.getFinalY();
smoothScrollBy(dx, dy);
}
/**
* 设置滚动的相对偏移
*
* @param dx
* @param dy
*/
protected void smoothScrollBy(int dx, int dy) {
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
super.computeScroll();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
smoothScrollTo(0, 0);
break;
default:
return mGestureDetector.onTouchEvent(event);
}
return super.onTouchEvent(event);
}
}
上一篇: 详解Vue.js中.native修饰符