这就是我最终为 flingable 视图所做的事情。这肯定比仅仅从视图寻呼机扩展更痛苦(它需要我进行大量的试验和错误),但它给了我想要的东西。您必须想办法使用 View 的 scrollX 以确定要捕捉到的项目以及到达视图的结尾或开头时要执行的操作。
public class FlingableScroller extends View implements GestureDetector.OnGestureListener {
public FlingableScroller(Context context, AttributeSet attrs) {
super(context, attrs);
mGestureDetector = new GestureDetector(context, this);
mScroller = new OverScroller(context);
//This is how you "set current item" You will have to calculate INITIAL_SCROLL_X yourself
setScrollX(INITIAL_SCROLL_X);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
animateFlingIfApplicable();
}
private void animateFlingIfApplicable() {
//We have a fling only if computeScrollOffset is true.
if (mScroller.computeScrollOffset()) {
//Flinging passed the start point so continue the fling at the end
if (mScroller.getFinalX() == 0) {
int velocity = getFlingVelocity();
mScroller.forceFinished(true);
mScroller.fling(MAXIMUM_SCROLL_X, 0, velocity / FRICTION_COEFFICIENT, 0, 0, MAXIMUM_SCROLL_X, 0, 0);
//Flinging passed the end point so continue the fling at the start
} else if (mScroller.getFinalX() == MAXIMUM_SCROLL_X) {
int velocity = getFlingVelocity();
mScroller.forceFinished(true);
mScroller.fling(0, 0, velocity / FRICTION_COEFFICIENT, 0, 0, MAXIMUM_SCROLL_X, 0, 0);
} else if (mScroller.getFinalX() == getScrollX() || mScroller.getCurrVelocity() == 0) {
snapToItem();
mScroller.forceFinished(true);
} else {
scrollTo(mScroller.getCurrX(), 0);
}
}
invalidate();
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//This logic handles the case when you scroll pass the beginning or the end of the scroller
if (getScrollX() > MAXIMUM_SCROLL_X) {
scrollTo(0, 0);
} else if (getScrollX() >= 0) {
scrollBy(distance, 0);
} else {
scrollTo(MAXIMUM_SCROLL_X, 0);
}
invalidate();
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//This is to prevent low velocity "flings"
if (Math.abs(velocityX) < 400 * SCREEN_DENSITY) {
return false;
}
mScroller.forceFinished(true);
//Define friction_coefficient to a value that gives you desirable flinging.
mScroller.fling(getScrollX(), getScrollY(), (int) -velocityX / FRICTION_COEFFICIENT, 0, 0, MAXIMUM_SCROLL_X, 0, 0);
invalidate();
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mScroller.forceFinished(true);
break;
case MotionEvent.ACTION_UP:
snapToItem();
invalidate();
break;
}
return mGestureDetector.onTouchEvent(event);
}
private void snapToItem() {
//The the user lifts up their finger from a scroll or when a fling finishes determine what item to snap to. See the ViewPager source code to emulate the "fake drag"
int scrollByValue = getScrollX() / SOME_VALUE
scrollTo(scrollByValue, 0);
}
}