Android怎么自定义View实现竖向滑动回弹效果
发表于:2025-11-11 作者:千家信息网编辑
千家信息网最后更新 2025年11月11日,这篇文章主要介绍"Android怎么自定义View实现竖向滑动回弹效果",在日常操作中,相信很多人在Android怎么自定义View实现竖向滑动回弹效果问题上存在疑惑,小编查阅了各式资料,整理出简单好
千家信息网最后更新 2025年11月11日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安全错误
数据库的锁怎样保障安全
计算机网络技术需要c语言吗
传统数据库系统中数据管理
通过网闸内外网数据库同步
服务器集群架构图
泽达软件开发有限公司
网络安全需求分析的提问话术
tbc如何看服务器人数
乐清软件开发工程师
金庸群侠传5数据库在哪
软件开发计划书交付产品
江苏鲲鹏系列服务器生产商
尉氏软件开发商
公司网站 软件开发合同
数据库三种形式的区别
metware数据库
蓝页网络技术
海口软件开发权威机构
网络安全人才和创新基地
合肥有几家软件开发公司吗
三门峡山体调查数据库
北京魔盒网络技术有限公司
湖南鲸东通互联网科技有限公司
沭阳巨型网络技术市场
java数据库监控
常用的数据库集群技术
网络安全产品的书籍
国家网络安全宣传周征文散文
打鱼软件开发购买多钱
怀旧服服务器开不了怎么回事
简述数据库管理系统的定义及功能