扩展HorizontalScrollView实现整个屏幕滚动
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; }
效果:
第一页
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架