项目中用到了大神写的Android 带你从源码的角度解析Scroller的滚动实现原理,记录一下,加强记忆
网址:http://blog.csdn.net/xiaanming/article/details/20934541
滑动:
1.基本
scrollTo,滚动的是控件的内容
/**
* 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
* @param y the y position to scroll to
*/
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();
}
}
}
/**
* The offset, in pixels, by which the content of this view is scrolled
* horizontally.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "scrolling")
protected int mScrollX;
2.Scroller
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设置一些初始参数
2.
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
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;
}
每次调用时,计算此时离滑动开始的时间,计算这段时间里应该滑动的距离,赋值给mCurrX和MCurrY,若时间到了,就赋值mFinished为true,下次再调用时,直接返回false.
3.View中方法,此方法在每次绘制view的时候调用,所以可以这边调用invalidate(),一直循环
</pre><pre name="code" class="java"> @Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
mParentView.scrollTo(scroller.getCurrX(), 0);
invalidate();
}
super.computeScroll();
}
Touch滑动:
onTouch和onTouchEvent
dispatchTouchEvent中调用onTouch,
onTouch返回true,将不执行onTouchEvent
MotionEvent cancelEvent = MotionEvent.obtain(event);
cancelEvent
.setAction(MotionEvent.ACTION_CANCEL
| (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
v.onTouchEvent(cancelEvent);
可以结束接下来的一系列动作,解决点击事件和滑动的冲突,在滑动的时候屏蔽点击事件
view跟随手势滑动,在move事件中先判断是否符合符合滑动,符合的设置标志位为ture,接下来的一系列滑动都根据手指滑动距离,来让view滑动,知道up事件发生,重置标志位为false。