Android怎么自定义View实现竖向滑动回弹效果
发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,这篇文章主要介绍"Android怎么自定义View实现竖向滑动回弹效果",在日常操作中,相信很多人在Android怎么自定义View实现竖向滑动回弹效果问题上存在疑惑,小编查阅了各式资料,整理出简单好
千家信息网最后更新 2025年02月01日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安全错误
数据库的锁怎样保障安全
网易版服务器有电脑通用的吗
怎么登录我的世界神奇宝贝服务器
军地网络安全排查协议
财政部门 网络安全
刺激战场游戏服务器分布
94年第一个网络安全的是
计算机网络安全危害数据分析
墙砖贴图软件开发
如何知道数据库总记录
f5服务器负载
服务器代码安全方面书籍推荐
网络安全观是
富士康电子网络技术
mc不是正版可以开私人服务器吗
网络安全防护情况怎么写
软件开发送快递
独嗅网络技术有限公司
全程软件开发机构
5G网络技术如何
软件开发封板什么意思
国家汉语方言数据库网站
江门数字软件开发平均价格
稳定性好的网络安全审计系统
连接数据库提示无监听
重新装了系统数据库不在了
浙旅控股网络技术员
网络安全也是我们公司
数据库的索引内存
手机网络安全狗
5G网络技术如何