转载请标明出处: http://blog.csdn.net/airsaid/article/details/53207851
本文出自:周游的博客
前言
在上一篇(Android 从0开始自定义控件之View的滑动(二))中,用 scrollTo() 和 scrollBy() 方法实现了 View 的滑动,但是实现的效果非常的生硬,用户体验很差。
这一篇继续在原有基础上,扩展下 View 的弹性滑动。下面详细介绍下实现 View 弹性滑动的几种方式。
Scroller
我们可以使用Scroller这个类来实现 View 内容的弹性滑动,使用起来也很简单,只需三步:
- 第一步:创建 Scroller 对象。
Scroller mScroller = new Scroller(context);
- 第二步:调用 startScroll() 方法。
mScroller.startScroll(getScrollX(), getScrollY() , -50, -50);
invalidate();
- 第三步:重写 View 的 computeScroll() 方法,实现具体滑动逻辑。
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
下面以一个简单的例子演示下Scroller这个类的用法:
首先自定义一个LinearLayout,并在构造中创建了Scroller对象,提供了一个startScroll()方法,并且重写了computeScroll()方法:
/**
* 作者:周游
* 时间:2016/11/13
* 博客:http://blog.csdn.net/airsaid
*/
public class ScrollerLinearLayout extends LinearLayout{
private final Scroller mScroller;
public ScrollerLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
public void startScroll(){
mScroller.startScroll(getScrollX(), getScrollY() , -50, -50);
invalidate();
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
}
在布局中用该自定义LinearLayout包裹了一个Button:
<?xml version="1.0" encoding="utf-8"?>
<com.airsaid.viewdemo.widget.ScrollerLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.airsaid.viewdemo.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="start"
android:text="start"/>
</com.airsaid.viewdemo.widget.ScrollerLinearLayout>
在代码中调用了其startScroll()方法:
public class MainActivity extends AppCompatActivity {
private ScrollerLinearLayout mLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLayout = (ScrollerLinearLayout) findViewById(R.id.layout);
}
public void start(View v){
mLayout.startScroll();
}
}
运行结果:
通过上面的例子,简单实现了用Scroller实现了View的弹性滑动。但是这一切都是怎么一个过程呢?首先来看看Scroller的startScroll()方法:
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}
通过查看startScroll()方法的源码可以发现,其内部只是做了赋值的操作,并没有调用其他的方法,那么这个弹性滑动的效果又是从哪里来的呢?
答案就在于,我们在调用startScroll()方法后又调用了invalidate()方法使View进行了重绘。那么这时就会走View的draw方法,在View的draw方法中又会去调用View的computeScroll()方法,而该方法在View中是一个空实现。这就解释了我们为什么需要复写computeScroll()方法。
在computeScroll()方法中,我们调用了computeScrollOffset()方法进行判断,该方法内部会根据时间的流逝来计算出scrollX和scrollY改变的百分比并计算出当前的值。这个方法的返回值也很重要,返回true表示滑动还未结束,false表示滑动已经结束。所以在这里,我们进行了判断,当其返回true时,就调用scrollTo()方法使View滑动,并调用invalidate()重绘,只要滑动没有完成就继续递归下去。
到这里Scroller的工作原理就很清晰了,Scroller并不能单独完成View的弹性滑动,而是要配合View的computeScroll()方法,不断的通过invalidate()方法重绘View,计算Scroller的X和Y值,并通过scrollTo()方法一点点的移动,连在一起,就形成了弹性滑动。
动画
使用动画使View实现滑动效果,其本身就具有弹性滑动的效果。所以只要掌握了动画的使用,那么就可以轻松实现出View的弹性滑动效果。这里就不细说了,大家可以看下我之前写的关于动画的两篇文章:Android 动画系列之属性(Property)动画详解、Android 动画系列之补间(Tween)动画详解。
延时策略
除了上面的几种实现方式之外,我们还可以使用延时策略。延时策略就是通过发送一系列的延时消息从而达到一种渐进式的效果。我们可以使用Handler的postDelayed方法,也可以使用其他的延迟消息方法。这里我们就用Handler来实现一个简单的弹性滑动效果:
rivate int mStartX = 0;
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
if(mStartX > -100){
mLayout.scrollTo(mStartX, 0);
mStartX --;
mHandler.sendEmptyMessageDelayed(0, 5);
}
break;
}
}
};
点击按钮时,发送延时消息:
mHandler.sendEmptyMessageDelayed(0, 5);
运行结果:
总结
通过了解以上几种实现弹性滑动的方法,知道了可以用Scroller实现View内的弹性滑动,使用动画可以实现View的弹性滑动。
在这几种方式中,其实更重要的是实现思想。只要掌握了其思想,那么就可以灵活运用起来,实现更多复杂的效果。
参考
1,《Android开发艺术探索》
2, http://blog.csdn.net/sinyu890807/article/details/48719871