View
的scrollTo()
、scrollBy()
使用介绍scrollTo()
和scrollBy()
实践,自定义滑动效果Scroller
滑动辅助类,startScroll()
和computeScrollOffset()
使用介绍Scroller
实践,使用示例scrollTo()
、scrollBy()
方法是View
中的,因此任何的View都可以通过这两种方法进行移动。首先要明白的是,scrollTo、scrollBy滑动的是View中的内容(而且还是整体滑动),而不是View本身。我们的滑动控件如SrollView可以限定宽、高大小,以及在布局中的位置,但是滑动控件中的内容(或者里面的childView)可以是无限长、宽的,我们调用View的scrollTo、scrollBy方法,相当于是移动滑动控件中的画布Canvas
,然后进行重绘,屏幕上也就显示相应的内容。scrollTo(100,0)
表示将View中的内容移动到距离内容初始显示位置的x=100,y=0
的地方,效果如下图:scrollTo(0,100)
效果如下图:scrollTo(100,100)
效果如下图:scrollBy(0,100)
表示将View
中的内容移动到原内容基础上x=x + 0,y=y + 100
的地方,所以scrollBy是在原来的基础上移动的,效果如下图:scrollBy()
的源码中其实也非常简单调用的就是scrollTo()
,只不过是加上了当前的mScrollX
或者 mScrollY
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
onTouchEvent()
方法中处理滑动事件,在MotionEvent.ACTION_MOVE
时调用scrollTo(int x,int y)
进行滑动,在调用scrollTo(int x,int y)
前,我们先要计算出两个参数值,即水平和垂直方向需要滑动的距离,如下:public class ScrollActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scroll);
}
private int mLastX;
private int mLastY;
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
int x = (int) event.getX();
int action = event.getAction();
View decorView = getWindow().getDecorView();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastY = y;
mLastX = x;
break;
case MotionEvent.ACTION_MOVE:
int dy = mLastY - y;//本次手势滑动了多大距离
int dx = mLastX - x;
int oldScrollY = decorView.getScrollY();//先计算之前已经偏移了多少距离
int oldScrollX = decorView.getScrollX();
int scrollY = oldScrollY + dy;//本次需要偏移的距离=之前已经偏移的距离+本次手势滑动了多大距离
int scrollX = oldScrollX + dx;
decorView.scrollTo(scrollX, scrollY);
mLastY = y;
mLastX = x;
break;
}
return true;
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:padding="10dp"
android:text="scroll..."
android:textSize="24sp"
android:textStyle="bold" />
LinearLayout>
scrollTo()
、scrollBy()
是瞬间完成的,当我们的手指在屏幕上移动时,内容会跟着手指滑动,但是当我们手指一抬起时,滑动就会停止,如果我们想要有一种惯性的滚动过程效果和回弹效果,此时就需要使用Scroller
辅助类。Scroller
本身不会去移动View
,它只是一个移动计算辅助类,用于跟踪控件滑动的轨迹,只相当于一个滚动轨迹记录工具,最终还是通过View
的scrollTo
、scrollBy
方法完成View
的移动的。Scroller
类之前,先了解其重要的两个方法:startScroll()
,开始一个动画控制,由(startX , startY)
在duration
时间内前进(dx,dy)
个单位,即到达偏移坐标为(startX+dx , startY+dy)
处。 public void startScroll(int startX, int startY, int dx, int dy) {
startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
computeScrollOffset()
,滑动过程中,根据当前已经消逝的时间计算当前偏移的坐标点,保存在mCurrX
和mCurrY
值中。 public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
...
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
Scroller
类中最重要的两个方法就是startScroll()
和computeScrollOffset()
,但是Scroller
类只是一个滑动计算辅助类,它的 startScroll()
和computeScrollOffset()
方法中也只是对一些轨迹参数进行设置和计算,真正需要进行滑动还是得通过View
的scrollTo()
、scrollBy()
方法 。为此,View
中提供了computeScroll()
方法来控制这个滑动流程。computeScroll()
方法会在绘制子视图的时候进行调用。其源码如下:是一个空方法。public void computeScroll() {}
Scroller
类的基本使用流程可以总结如下:Scroller
类的startScroll()
开始一个滑动动画控制,里面进行了一些轨迹参数的设置和计算;startScroll()
的后面调用invalidate()
;引起视图的重绘操作,从而触发ViewGroup
中的computeScroll()
被调用;computeScroll()
方法中,先调用Scroller
类中的computeScrollOffset()
方法,里面根据当前消耗时间进行轨迹坐标的计算,然后取得计算出的当前滑动的偏移坐标,调用View
的scrollTo()
方法进行滑动控制,最后也需要调用invalidate()
;进行重绘。public class ScrollLinearLayout extends LinearLayout {
public ScrollLinearLayout(Context context) {
super(context);
init();
}
public ScrollLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ScrollLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private int mLastX;
private int mLastY;
private Scroller mScroller;
private void init() {
mScroller = new Scroller(getContext());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
int x = (int) event.getX();
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mLastY = y;
mLastX = x;
break;
case MotionEvent.ACTION_MOVE:
int dy = mLastY - y;//本次手势滑动了多大距离
int dx = mLastX - x;
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, 0);//第一步
invalidate();
mLastY = y;
mLastX = x;
break;
default:
}
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {//第二步
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//第三步
invalidate();
}
}
}
public class ScrollActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scroll);
}
}
<com.example.mtest.ScrollLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:gravity="center"
android:padding="10dp"
android:text="scroll..."
android:textSize="24sp"
android:textStyle="bold" />
com.example.mtest.ScrollLinearLayout>
Scroller
类能够帮助我们实现高级的滑动功能,如手指抬起后的惯性滑动功能。使用流程为,首先通过Scroller
类的startScroll()+invalidate()
触发View
的computeScroll()
,在computeScroll()
中让Scroller
类去计算最新的坐标信息,拿到最新的坐标偏移信息后还是要调用View
的scrollTo
来实现滑动。可以看到,使用Scroller
的整个流程比较简单,关键的是控制滑动的一些逻辑计算。Android
后面推出了OverScroller
类,OverScrolle
r在整体功能上和Scroller
类似,使用也相同。OverScroller
类可以完全代替Scroller
,相比Scroller
,OverScroller
主要是增加了对滑动到边界的一些控制,如增加一些回弹效果等,功能更加强大。本文参考:
Android Scroller详解
Android源码角度分析View的scrollBy()和scrollTo()的参数正负问题
ListView滑动删除实现之二——scrollTo、scrollBy详解