Android怎么自定义View实现竖向滑动回弹效果
发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要介绍"Android怎么自定义View实现竖向滑动回弹效果",在日常操作中,相信很多人在Android怎么自定义View实现竖向滑动回弹效果问题上存在疑惑,小编查阅了各式资料,整理出简单好
千家信息网最后更新 2026年11月03日Android怎么自定义View实现竖向滑动回弹效果
这篇文章主要介绍"Android怎么自定义View实现竖向滑动回弹效果",在日常操作中,相信很多人在Android怎么自定义View实现竖向滑动回弹效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答"Android怎么自定义View实现竖向滑动回弹效果"的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
一、关键代码
public class UniversalBounceView extends FrameLayout implements IPull { private static final String TAG = "UniversalBounceView"; //default. private static final int SCROLL_DURATION = 200; private static final float SCROLL_FRACTION = 0.4f; private static final int VIEW_TYPE_NORMAL = 0; private static final int VIEW_TYPE_ABSLISTVIEW = 1; private static final int VIEW_TYPE_SCROLLVIEW = 2; private static float VIEW_SCROLL_MAX = 720; private int viewHeight; private AbsListView alv; private OnBounceStateListener onBounceStateListener; private View child; private Scroller scroller; private boolean pullEnabled = true; private boolean pullPaused; private int touchSlop = 8; private int mPointerId; private float downY, lastDownY, tmpY; private int lastPointerIndex; private float moveDiffY; private boolean isNotJustInClickMode; private int moveDelta; private int viewType = VIEW_TYPE_NORMAL; public UniversalBounceView(Context context) { super(context); init(context); } public UniversalBounceView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public UniversalBounceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { scroller = new Scroller(context, new CustomDecInterpolator()); touchSlop = (int) (ViewConfiguration.get(context).getScaledTouchSlop() * 1.5); } class CustomDecInterpolator extends DecelerateInterpolator { public CustomDecInterpolator() { super(); } public CustomDecInterpolator(float factor) { super(factor); } public CustomDecInterpolator(Context context, AttributeSet attrs) { super(context, attrs); } @Override public float getInterpolation(float input) { return (float) Math.pow(input, 6.0 / 12); } } private void checkCld() { int cnt = getChildCount(); if (1 <= cnt) { child = getChildAt(0); } else if (0 == cnt) { pullEnabled = false; child = new View(getContext()); } else { throw new ArrayIndexOutOfBoundsException("child count can not be less than 0."); } } @Override protected void onFinishInflate() { checkCld(); super.onFinishInflate(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); viewHeight = h; VIEW_SCROLL_MAX = h * 1 / 3; } private boolean isTouch = true; public void setTouch(boolean isTouch) { this.isTouch = isTouch; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!isTouch) { return true; } else { try { if (isPullEnable()) { if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { if (Math.abs(ev.getY() - tmpY) < touchSlop) { return super.dispatchTouchEvent(ev); } else { tmpY = Integer.MIN_VALUE; } } return takeEvent(ev); } } catch (IllegalArgumentException | IllegalStateException e) { e.printStackTrace(); } if (getVisibility() != View.VISIBLE) { return true; } return super.dispatchTouchEvent(ev); } } private boolean takeEvent(MotionEvent ev) { int action = ev.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: mPointerId = ev.getPointerId(0); downY = ev.getY(); tmpY = downY; scroller.setFinalY(scroller.getCurrY()); setScrollY(scroller.getCurrY()); scroller.abortAnimation(); pullPaused = true; isNotJustInClickMode = false; moveDelta = 0; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: pullPaused = false; smoothScrollTo(0); if (isNotJustInClickMode) { ev.setAction(MotionEvent.ACTION_CANCEL); } postDelayed(new Runnable() { @Override public void run() { if (getScrollY() == 0 && onBounceStateListener != null) { onBounceStateListener.overBounce(); } } }, 200); break; case MotionEvent.ACTION_MOVE: lastPointerIndex = ev.findPointerIndex(mPointerId); lastDownY = ev.getY(lastPointerIndex); moveDiffY = Math.round((lastDownY - downY) * getScrollFraction()); downY = lastDownY; boolean canStart = isCanPullStart(); boolean canEnd = isCanPullEnd(); int scroll = getScrollY(); float total = scroll - moveDiffY; if (canScrollInternal(scroll, canStart, canEnd)) { handleInternal(); break; } if (Math.abs(scroll) > VIEW_SCROLL_MAX) { return true; } if ((canStart && total < 0) || (canEnd && total > 0)) { if (moveDelta < touchSlop) { moveDelta += Math.abs(moveDiffY); } else { isNotJustInClickMode = true; } if (onBounceStateListener != null) { onBounceStateListener.onBounce(); } scrollBy(0, (int) -moveDiffY); return true; }// else if ((total > 0 && canStart) || (total < 0 && canEnd)) {// if (moveDelta < touchSlop) {// moveDelta += Math.abs(moveDiffY);// } else {// isNotJustInClickMode = true;// }// scrollBy(0, -scroll);// return true;// } break; case MotionEvent.ACTION_POINTER_UP: handlePointerUp(ev, 1); break; default: break; } return super.dispatchTouchEvent(ev); } private boolean canScrollInternal(int scroll, boolean canStart, boolean canEnd) { boolean result = false; if ((child instanceof RecyclerView) || (child instanceof AbsListView) || child instanceof ScrollView) { viewType = VIEW_TYPE_ABSLISTVIEW; result = canStart && canEnd; } else if (child instanceof ScrollView || child instanceof NestedScrollView) { viewType = VIEW_TYPE_SCROLLVIEW; } else { return false; } if (result) { isNotJustInClickMode = true; if (moveDelta < touchSlop) { moveDelta += Math.abs(moveDiffY); return true; } return false; } if (((scroll == 0 && canStart && moveDiffY < 0) || (scroll == 0 && canEnd && moveDiffY > 0) || (!canStart && !canEnd))) { return true; } if (moveDelta < touchSlop) { moveDelta += Math.abs(moveDiffY); return true; } else { isNotJustInClickMode = true; } return false; } private void handleInternal() { } private void handlePointerUp(MotionEvent event, int type) { int pointerIndexLeave = event.getActionIndex(); int pointerIdLeave = event.getPointerId(pointerIndexLeave); if (mPointerId == pointerIdLeave) { int reIndex = pointerIndexLeave == 0 ? 1 : 0; mPointerId = event.getPointerId(reIndex); // 调整触摸位置,防止出现跳动 downY = event.getY(reIndex); } } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } private void smoothScrollTo(int value) { int scroll = getScrollY(); scroller.startScroll(0, scroll, 0, value - scroll, SCROLL_DURATION); postInvalidate(); } @Override public void computeScroll() { super.computeScroll(); if (!pullPaused && scroller.computeScrollOffset()) { scrollTo(scroller.getCurrX(), scroller.getCurrY()); postInvalidate(); } } private float getScrollFraction() { float ratio = Math.abs(getScrollY()) / VIEW_SCROLL_MAX; ratio = ratio < 1 ? ratio : 1; float fraction = (float) (-2 * Math.cos((ratio + 1) * Math.PI) / 5.0f) + 0.1f; return fraction < 0.10f ? 0.10f : fraction; } @Override public boolean isPullEnable() { return pullEnabled; } @Override public boolean isCanPullStart() { if (child instanceof RecyclerView) { RecyclerView recyclerView = (RecyclerView) child; return !recyclerView.canScrollVertically(-1); } if (child instanceof AbsListView) { AbsListView lv = (AbsListView) child; return !lv.canScrollVertically(-1); } if (child instanceof RelativeLayout || child instanceof FrameLayout || child instanceof LinearLayout || child instanceof WebView || child instanceof View) { return child.getScrollY() == 0; } return false; } @Override public boolean isCanPullEnd() { if (child instanceof RecyclerView) { RecyclerView recyclerView = (RecyclerView) child; return !recyclerView.canScrollVertically(1); } if (child instanceof AbsListView) { AbsListView lv = (AbsListView) child; int first = lv.getFirstVisiblePosition(); int last = lv.getLastVisiblePosition(); View view = lv.getChildAt(last - first); if (null == view) { return false; } else { return (lv.getCount() - 1 == last) && (view.getBottom() <= lv.getHeight()); } } if (child instanceof ScrollView) { View v = ((ScrollView) child).getChildAt(0); if (null == v) { return true; } else { return child.getScrollY() >= v.getHeight() - child.getHeight(); } } if (child instanceof NestedScrollView) { View v = ((NestedScrollView) child).getChildAt(0); if (null == v) { return true; } else { return child.getScrollY() >= v.getHeight() - child.getHeight(); } } if (child instanceof WebView) { return (((WebView) child).getContentHeight() * ((WebView) child).getScale()) - (((WebView) child).getHeight() + ((WebView) child).getScrollY()) <= 10; } if (child instanceof RelativeLayout || child instanceof FrameLayout || child instanceof LinearLayout || child instanceof View) { return (child.getScrollY() == 0); } return false; } /** * 通过addView实现效果回弹效果 * * @param replaceChildView 需要替换的View */ public void replaceAddChildView(View replaceChildView) { if (replaceChildView != null) { removeAllViews(); child = replaceChildView; addView(replaceChildView); } } public void setPullEnabled(boolean enable) { pullEnabled = enable; } public interface OnBounceStateListener { public void onBounce(); public void overBounce(); } public void setOnBounceStateListener(OnBounceStateListener onBounceStateListener) { this.onBounceStateListener = onBounceStateListener; } @Override public boolean dispatchKeyEvent(KeyEvent event) { try { return super.dispatchKeyEvent(event); } catch (IllegalArgumentException | IllegalStateException e) { e.printStackTrace(); } return false; } @Override public void dispatchWindowFocusChanged(boolean hasFocus) { try { super.dispatchWindowFocusChanged(hasFocus); } catch (IllegalArgumentException | IllegalStateException e) { e.printStackTrace(); } } }二、注意要点
滑动结束的时候要防止动画抖动
private void handlePointerUp(MotionEvent event, int type) { int pointerIndexLeave = event.getActionIndex(); int pointerIdLeave = event.getPointerId(pointerIndexLeave); if (mPointerId == pointerIdLeave) { int reIndex = pointerIndexLeave == 0 ? 1 : 0; mPointerId = event.getPointerId(reIndex); // 调整触摸位置,防止出现跳动 downY = event.getY(reIndex); } }到此,关于"Android怎么自定义View实现竖向滑动回弹效果"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!
效果
学习
位置
更多
帮助
调整
实用
接下来
代码
关键
动画
文章
方法
时候
理论
知识
篇文章
网站
要点
资料
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
科技与互联网
互联网与科技金融数据
深圳互联网科技公司
北京互联网科技公司
互联网的科技创新
猪兼强互联网科技
互联网科技公司电话
互联网科技公司官网
鑫亿盛互联网科技
极限挑战互联网科技
深圳艾佳互联网科技
天扬科技互联网广告
广州慧亚互联网科技怎么样
互联网还是高科技吗
世界互联网科技公司市值排名
消费医药和互联网科技
米聚互联网科技南京有限公司排面
长沙互联网科技
北京互联网科技类企业分布
同城互联网络科技
互联网科技名片
互联网金融 科技金融
上海万想互联网科技
互联网系的金融科技公司
现代互联网科技股
互联网5g黑科技有哪些
互联网保险科技创业
中国互联网有什么科技
互联网科技股为什么大涨
上海乐五互联网科技有限公司