import java.lang.reflect.Field; import android.content.Context; import android.util.AttributeSet; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import android.widget.HorizontalScrollView; import android.widget.OverScroller; import android.widget.Scroller; public class HorizontalScrollViewEx extends HorizontalScrollView { static final int ANIMATED_SCROLL_GAP = 250; private long mLastScroll; private Field mScrollerField; ScrollerEx scrollerEx = null; public HorizontalScrollViewEx(Context context) { this(context, null); } public HorizontalScrollViewEx(Context context, AttributeSet attrs) { super(context, attrs); this.setSmoothScrollingEnabled(false); initScroller(); } public HorizontalScrollViewEx(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.setSmoothScrollingEnabled(false); initScroller(); } private void initScroller() { try { mScrollerField = HorizontalScrollView.class.getDeclaredField("mScroller"); mScrollerField.setAccessible(true); String type = mScrollerField.getType().getSimpleName(); if ("OverScroller".equals(type)) { scrollerEx = new ScrollerEx() { private OverScroller mScroller = null; public void startScroll(int startX, int startY, int dx, int dy, int duration) { mScroller.startScroll(startX, startY, dx, dy, duration); } public boolean isFinished() { return mScroller.isFinished(); } public Object getScroller() { return mScroller; } public void create(Context context, Interpolator interpolator) { mScroller = new OverScroller(context, interpolator); } public void abortAnimation() { if (mScroller != null) { mScroller.abortAnimation(); } } }; } else { scrollerEx = new ScrollerEx() { private Scroller mScroller = null; public void startScroll(int startX, int startY, int dx, int dy, int duration) { mScroller.startScroll(startX, startY, dx, dy, duration); } public boolean isFinished() { return mScroller.isFinished(); } public Object getScroller() { return mScroller; } public void create(Context context, Interpolator interpolator) { mScroller = new Scroller(context, interpolator); } public void abortAnimation() { if (mScroller != null) { mScroller.abortAnimation(); } } }; } } catch (Exception ex) { } }public final void smoothScrollBy(int dx, int dy, int addDuration) { float tension = 0f; scrollerEx.abortAnimation(); Interpolator ip = new OvershootInterpolator(tension); scrollerEx.create(getContext(), ip); try { mScrollerField.set(this, scrollerEx.getScroller()); } catch (Exception e) { } long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll; if (duration > ANIMATED_SCROLL_GAP) { scrollerEx.startScroll(getScrollX(), getScrollY(), dx, dy, addDuration); awakenScrollBars(); // awakenScrollBars(mScroller.getDuration()); invalidate(); } else { if (!scrollerEx.isFinished()) { scrollerEx.abortAnimation(); } scrollBy(dx, dy); } mLastScroll = AnimationUtils.currentAnimationTimeMillis(); } public final void smoothScrollTo(int x, int y, int duration) { smoothScrollBy(x - getScrollX(), y - getScrollY(), duration); } private interface ScrollerEx { void create(Context context, Interpolator interpolator); Object getScroller(); void abortAnimation(); void startScroll(int startX, int startY, int dx, int dy, int duration); boolean isFinished(); } }
核心部分是利用反射的方法取得父类的 mScroller对象,然后再使用 mScroller控制滚动。
然后在Activity里重写dispatchTouchEvent方法,使用GestureDetector控制滚动距离。 下面是部分代码
private HorizontalScrollViewEx hsvMain; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); hsvMain = (HorizontalScrollViewEx) findViewById(R.id.hsvMain); } // 监听屏幕动作事件 GestureDetector = gestureDetector = new GestureDetector(new OnGestureListener() { private int LEFT_DISTANCE = -150; private int RIGHT_DISTANCE = 150; // 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发 public boolean onDown(MotionEvent e) { return true; } // 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, // 多个ACTION_MOVE, 1个ACTION_UP触发 // e1:第1个ACTION_DOWN MotionEvent // e2:最后一个ACTION_MOVE MotionEvent // velocity X:X 轴上的移动速度,像素/秒 // velocity Y:Y 轴上的移动速度,像素/秒 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { float distanceX = e2.getX() - e1.getX(); if (distanceX < LEFT_DISTANCE) { // left setXPosition(true); } else if (distanceX > RIGHT_DISTANCE) { // right setXPosition(false); } hsvMain.smoothScrollTo(xPosition, 0, speed); return true; } // 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发 public void onLongPress(MotionEvent e) { } // 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { hsvMain.scrollBy((int) distanceX, 0); return true; } // 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发 // 注意和onDown()的区别,强调的是没有松开或者拖动的状态 public void onShowPress(MotionEvent e) { } // 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发 public boolean onSingleTapUp(MotionEvent e) { if (e.getY() < offsetOfBottom) { return true; } if (e.getX() > halfOfWidth) { setXPosition(true); } else { setXPosition(false); } hsvMain.scrollTo(xPosition, 0); return true; } }); @Override public boolean dispatchTouchEvent(MotionEvent ev) { boolean result = gestureDetector.onTouchEvent(ev); if (result == false) { final int action = ev.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_UP: hsvMain.smoothScrollTo(xPosition, 0, speed); result = true; } } return true; }
效果:
第一页