一、使用Scroller
实现:
Scroller mScroller= new Scroller(mContext);
private void smoothScrollTo(int destX, int destY) {
int scrollX = getScrollX();
int deltaX = destX - scrollX;
//1000ms内滑向destX,效果就是慢慢滑动
mScroller.startScroll(scrollX, 0, deltaX, 0, 1000);
invalidate();
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
其中,
Scroller#startScroll:
public void startScroll(int startX, int startY, int dx, int dy, int duration)
startX:滑动的起点的横坐标。
startY:滑动的起点的纵坐标。
dx:要滑动的水平距离。
dy:要滑动的竖直距离。
duration:滑动时间。
注意:这里的滑动是指View内容的滑动而非View本身位置的改变。
Scroller#computeScrollOffset:
这个方法会根据时间的流逝来计算出当前的scrollX和scrollY的值。大概计算过程就是根据时间流逝的百分比来算出scrollX和scrollY改变的百分比并计算出当前的值。
工作过程:
在smoothScrollTo方法中invalidate方法会导致View重绘,在View的draw方法中又会去调用computeScroll方法,computeScroll方法在View中是一个空实现,需要覆写。computeScroll方法会向Scroller获取当前的scrollX和scrollY,然后通过scrollTo方法实现滑动,接着又调用postInvalidate方法来进行第二次重绘,这一次重绘的过程和第一次重绘一样,还是会导致scrollTo方法滑动到新的位置,如此反复,直到整个滑动过程结束。
例子:
//布局
<?xml version="1.0" encoding="utf-8"?>
<com.tomorrow.androidtest1.MyScrollerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/main_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</com.tomorrow.androidtest1.MyScrollerView>
//代码
//自定义布局,继承RelativeLayout
public class MyScrollerView extends RelativeLayout {
private Scroller mScroller;
public MyScrollerView(Context context) {
super(context);
init(context);
}
public MyScrollerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mScroller = new Scroller(context);
}
public void smoothScrollTo(int startX, int startY, int destX, int destY, int duration) {
int deltaX = destX - startX;
int deltaY = destY - startY;
mScroller.startScroll(startX, startY, deltaX, deltaY, duration);
invalidate();
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
}
//调用
mMain_layout = (MyScrollerView)findViewById(R.id.main_layout);
mMain_layout.smoothScrollTo(-50, -50, -500, -500, 5000);
二、使用动画
动画本身就是一种渐近的过程,因此通过它来实现的滑动天然就具有弹性效果。
使用方法及工作原理将在"动画"小节介绍。
例子:
直接作用于View,使用ObjectAnimator:
ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();
不直接作用于View,使用ValueAnimator跟scrollTo,模仿Scroller跟scrollTo:
final int startX = 0;
final int deltaX = 100;
ValueAnimator animator = ValueAnimator.ofInt(0, 1).setDuration(1000);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
float fraction = animator.getAnimatedFraction();
mButton1.scrollTo(startX + (int)(deltaX * fraction), 0);
}
});
animator.start();
三、使用延时策略
通过发送一系列延时消息从而达到一种渐近式的效果。
1.使用Handler或View的postDelayed方法,通过它来延时发送一个消息,然后在消息中来进行View的滑动,如果接连不断地发送这种延时消息,那么就可以实现弹性滑动的效果。
例子:
private static final int MESSAGE_SCROLL_TO = 1;
private static final int FRAME_COUNT = 30;
private static final int DELAYED_TIME = 33;
private int mCount = 0;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch() {
case MESSAGE_SCROLL_TO:
mCount++;
if(mCount <= FRAME_COUNT) {
float fraction = mCount / (float)FRAME_COUNT;
int scrollX = (int)(fraction * 100);
mButton1.scrollTo(scrollX, 0);
mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO, DELAYED_TIME);
}
break;
default:
break;
}
}
};
2.使用线程sleep方法,通过在while循环中不断地滑动View和sleep,就可以实现弹性滑动的效果。