最近研究了一下qq中listview 滑动出现删除按钮的操作,找到了一个SwipeMenuListView的类库,于是认真的研读了一下代码,确实有很多可学习的知识,其中有一个地方用到了ScrollerCompat这个类,之前确实没有接触过,所以认知学习了一下,这个类,主要是用来支持自动滑动的,其中用到了几个重要的方法:
1、public void startScroll (int startX, int startY, int dx, int dy, int duration)
这个方法其实网上有一堆的详细介绍,但是我发现对几个参数的介绍分歧很大,当时我看的时候确实很困惑,所以仔细研究了一下源码,确定了正确的答案,下面分享给大家,先看一下源码:
/**
* Start scrolling by providing a starting point and the distance to travel.
* 翻译:通过设置一个起始点和移动距离来启动一个滚动
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
*翻译:startX代表水平方向滚动的偏移值,以像素为单位。正值表明滚动将向左滚动,也就是x轴起始位置
* @param startY Starting vertical scroll offset in pixels. Positive numbers
* will scroll the content up.
* 翻译:startY代表垂直方向滚动的偏移值,以像素为单位。正值表明滚动将向上滚动
* @param dx Horizontal distance to travel. Positive numbers will scroll the
* content to the left.
* 翻译:dx 代表水平方向的滑动距离,正值表明向左滑动
* @param dy Vertical distance to travel. Positive numbers will scroll the
* content up.
* 翻译:dy 代表垂直方向的滑动距离,正值表明向上滑动
* @param duration Duration of the scroll in milliseconds.
* 翻译:整个滑动过程所经历的滑动时间,单位毫秒
*/
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;
}
首先,这个方法我需要强调一下,网上有些人说dx,dy代表终点位置,我想现在我把源代码展示出来了,大家看一下mFinalX = startX + dx;也应该能够看明白是一个偏移量,也就是移动距离。需要提醒一下 ,我发现SwipeMenuListView的作者也理解错了,将dx理解成终点了,大家用的时候可以注意一下,但是不影响使用。
另外,此处值得正负和轴的方向正好相反,也就是说,如果想向右滑动,则需要设定为
startScroll(负数, 0, 负数, 0, int duration);startX和dx(或startY和dy)正负号一定要统一。
最后 duration代表的是整个移动过程的总时间,默认250ms,不是每一帧移动的时间间隔,网上有的朋友解释的不正确。
2、 public boolean computeScrollOffset()这个方法要明确一点是,是用来判断整个滚动过程是否完全走完,如果走完的返回false,否则返回true,网上有的解释正好相反,大家看一下下面的源代码应该会一目了然。
/**
* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished. loc will be altered to provide the
* new location.
*/
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
float x = timePassed * mDurationReciprocal;
if (mInterpolator == null)
x = viscousFluid(x);
else
x = mInterpolator.getInterpolation(x);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);
float distanceCoef = 1.f;
float velocityCoef = 0.f;
if (index < NB_SAMPLES) {
final float t_inf = (float) index / NB_SAMPLES;
final float t_sup = (float) (index + 1) / NB_SAMPLES;
final float d_inf = SPLINE_POSITION[index];
final float d_sup = SPLINE_POSITION[index + 1];
velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
distanceCoef = d_inf + (t - t_inf) * velocityCoef;
}
mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
3、public void scrollTo(int x, int y)让试图滚动到指定位置
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* 滚动到的X位置
* @param y the y position to scroll to
* 滚动到的Y位置
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
4、public void computeScroll() 这个方法没有源码,是一个空方法,需要自己来重写。在试图重绘的时候回调用这个方法,就是onDraw()里面调用。
下面给大家简单介绍一下应用:
分一下三步:
第一步:创建ScrollerCompat对象
mScroller= ScrollerCompat.create(getContext(),mInterpolator);//mInterpolator可以省略就是动画效果
第二步:重写computeScroll()
computeScroll(){
if (mScroller.computeScrollOffset()) { //此处一定要进行判断,否则后面调用 postInvalidate();会陷入死循环,无限刷新,导致死机
// 产生了动画效果,根据当前值 每次滚动一点
scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //让试图移动到指定位置
postInvalidate(); //不可省略,让onDraw继续调用本方法,类似递归
}
}
第三步:手动启动滚动
startScroll (0, 0, 200, 0, 350);
postInvalidate();//此处需要手动调用刷新,否则不会看到移动效果