您现在的位置是:亿华云 > IT科技类资讯
Android高手进阶之ViewDragHelper使用详解以及拖动上下滑卡片实现
亿华云2025-10-09 12:56:41【IT科技类资讯】5人已围观
简介前言正好项目中有个页面底部拖动上下滑的UI;今天我们就来讲解下ViewDragHelper;这几天项目比较忙,文章更新会慢,各位老铁可以看历史记录;一、viewDragHleper详解ViewDrag
前言
正好项目中有个页面底部拖动上下滑的手进使用实现UI;
今天我们就来讲解下ViewDragHelper;
这几天项目比较忙,文章更新会慢,阶之及拖各位老铁可以看历史记录;
一、详解下滑viewDragHleper详解
ViewDragHelper是动上针对 ViewGroup 中的拖拽和重新定位 views 操作时提供了一系列非常有用的方法和状态追踪;
1、ViewDragHelper初始化
public class ViewDragTest extends LinearLayout { ViewDragHelper mViewDragHelper; @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); //中间参数表示灵敏度,卡片比如滑动了多少像素才视为触发了滑动.值越大越灵敏. mViewDragHelper = ViewDragHelper.create(this, 1f, new DragCallback()); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //固定写法 int action = MotionEventCompat.getActionMasked(ev); if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mViewDragHelper.cancel(); return false; } return mViewDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { //固定写法 mViewDragHelper.processTouchEvent(event); return true; } @Override public void computeScroll() { //固定写法 //此方法用于自动滚动,比如自动回滚到默认位置. if (mViewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } }2、ViewDragHelper.Callback
//这个类的手进使用实现回调方法,才是ViewDragHelper的重点 private class ViewDragCallback extends ViewDragHelper.Callback{ @Override public boolean tryCaptureView(View child, int pointerId) { //child 表示想要滑动的view //pointerId 表示触摸点的id, 比如多点按压的那个id //返回值表示,是否可以capture,也就是是否可以滑动.可以根据不同的child决定是否可以滑动 return true; } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { //child 表示当前正在移动的view //left 表示当前的view正要移动到左边距为left的地方 //dx 表示和上一次滑动的距离间隔 //返回值就是child要移动的目标位置.可以通过控制返回值,从而控制child只能在ViewGroup的范围中移动. return left; } @Override public int clampViewPositionVertical(View child, int top, int dy) { //child 表示当前正在移动的view //top 表示当前的view正要移动到上边距为top的地方 //dx 表示和上一次滑动的距离间隔 return top; } }重写以上3个方法,可以正常工作了.子View就可以被任意拖动了;
3、控制child的阶之及拖移动范围在父view中
//控制child只能在ViewGroup的云南idc服务商横向中移动 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { final int leftBound = getPaddingLeft(); final int rightBound = getWidth() - mDragView.getWidth(); final int newLeft = Math.min(Math.max(left, leftBound), rightBound); return newLeft; } //控制child只能在ViewGroup的纵向中移动 @Override public int clampViewPositionVertical(View child, int top, int dy) { final int topBound = getPaddingTop(); final int bottomBound = getHeight() - mDragView.getHeight(); final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; }4、开启边界滑动
//开启4个边 mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL); //各个边 public static final int EDGE_LEFT = 1 << 0; public static final int EDGE_RIGHT = 1 << 1; public static final int EDGE_TOP = 1 << 2; public static final int EDGE_BOTTOM = 1 << 3; //当开启边界滑动之后,详解下滑 此方法就会回调 @Override public void onEdgeTouched(int edgeFlags, int pointerId) { //通常开启边界之后, 都需要手动capture view.之后就可以滑动view了. mViewDragHelper.captureChildView(getChildAt(1), pointerId); } @Override public boolean tryCaptureView(View child, int pointerId) { //开启边界之后, 这个方法的返回值可能需要进一步处理.要不然开边界就没啥意思了. return false; }5、释放后的动上回弹效果
有些时候, 当释放的时候, 需要将View回到原来的位置;
//释放的时候, 会回调下面的方法 @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { //调用这个方法,就可以设置releasedChild回弹得位置. mViewDragHelper.settleCapturedViewAt(0, 100);//参数就是x,y的坐标 postInvalidate();//注意一定要调用这个方法,否则没效果. } //以下2个方法最终调用的都是forceSettleCapturedViewAt(). mViewDragHelper.settleCapturedViewAt(0, 100); mViewDragHelper.smoothSlideViewTo(getChildAt(1), 0, 100); //所以...发挥你的想象力,看看有什么妙用!!! //如果你还没有忘记的话...前文应该有说过,涉及到scroll,需要重写view的此方法. //此方法一定要重写,否则没效果 @Override public void computeScroll() { //固定写法 if (mViewDragHelper.continueSettling(true)) { postInvalidate();//注意此处. } } 通过上面2个方法的设置, 当手指释放的时候, View就会自动滑动到指定的位置...(不是一下子就到指定的位置哦,有一个滑动的过程.) 注意:如果需要滑动的View,会消耗touch事件,比如:Button,那么需要重写以下方法. @Override public int getViewHorizontalDragRange(View child) { return child.getMeasuredWidth();//只要返回大于0的值就行 } @Override public int getViewVerticalDragRange(View child) { return child.getMeasuredHeight();//只要返回大于0的值就行 }6、简单api介绍
ViewDragHelper的卡片API
ViewDragHelper create(ViewGroup forParent, Callback cb); 一个静态的创建方法, 参数1:出入的手进使用实现是相应的ViewGroup 参数2:是企商汇一个回掉 shouldInterceptTouchEvent(MotionEvent ev) 处理事件分发的(主要是将ViewGroup的事件分发,委托给ViewDragHelper进行处理) 参数1:MotionEvent ev 主要是阶之及拖ViewGroup的事件 processTouchEvent(MotionEvent event) 处理相应TouchEvent的方法,这里要注意一个问题,详解下滑处理相应的动上TouchEvent的时候要将结果返回为true,消费本次事件!否则将无法使用ViewDragHelper处理相应的卡片拖拽事件!ViewDragHelper.Callback的API
tryCaptureView(View child, int pointerId) 这是一个抽象类,必须去实现,也只有在这个方法返回true的时候下面的方法才会生效; 参数1:捕获的View(也就是你拖动的这个View) onViewDragStateChanged(int state) 当状态改变的时候回调,返回相应的状态(这里有三种状态) STATE_IDLE 闲置状态 STATE_DRAGGING 正在拖动 STATE_SETTLING 放置到某个位置 onViewPositionChanged(View changedView, int left, int top, int dx, int dy) 当你拖动的View位置发生改变的时候回调 参数1:你当前拖动的这个View 参数2:距离左边的距离 参数3:距离右边的距离 参数4:x轴的变化量 参数5:y轴的变化量 onViewCaptured(View capturedChild, int activePointerId) 捕获View的b2b供应网时候调用的方法 参数1:捕获的View(也就是你拖动的这个View) onViewReleased(View releasedChild, float xvel, float yvel) 当View停止拖拽的时候调用的方法,一般在这个方法中重置一些参数,比如回弹什么的 参数1:你拖拽的这个View 参数2:x轴的速率 参数3:y轴的速率 clampViewPositionVertical(View child, int top, int dy) 竖直拖拽的时候回调的方法 参数1:拖拽的View 参数2:距离顶部的距离 参数3:变化量 clampViewPositionHorizontal(View child, int left, int dx) 水平拖拽的时候回调的方法 参数1:拖拽的View 参数2:距离左边的距离 参数3:变化量二、简单的实现demo
下面是简单实现的demo,可以直接复制使用的
1、BottomView的ViewDragHelper实现
public class BottomView extends LinearLayout { private ViewDragHelper mDragHelper; private View view; private int mDragBorder, verticalRange, mDragState, peekHeight, mDragHeight; private final double AUTO_OPEN_SPEED_LIMIT = 800.0; private boolean inflate = false, isExpanded = false, isDragHeightSet = false; private MotionEvent globalEvent; View try_view; public BottomView(Context context) { super(context); } public BottomView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initView(context, attrs); } public BottomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context, attrs); } void initView(Context context, AttributeSet attrs) { peekHeight = 300; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); } @Override protected void onFinishInflate() { super.onFinishInflate(); mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback()); view = getChildAt(0); try_view = findViewById(R.id.ll_try_view); } @Override protected void onLayout(boolean b, int left, int top, int right, int bottom) { verticalRange = getMeasuredHeight() - peekHeight; if (!inflate) { mDragBorder = verticalRange; inflate = true; } view.layout(left, mDragBorder, right, bottom + mDragBorder); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = MotionEventCompat.getActionMasked(ev); if ((action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) && !isDraggingAllowed(ev)) { mDragHelper.cancel(); return false; } return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { if (isDraggingAllowed(event) || isMoving()) { mDragHelper.processTouchEvent(event); return true; } return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { globalEvent = ev; return super.dispatchTouchEvent(ev); } boolean isDraggingAllowed(MotionEvent event) { int[] viewLocations = new int[2]; view.getLocationOnScreen(viewLocations); int upperLimit = viewLocations[1] + (isDragHeightSet ? mDragHeight : peekHeight); int lowerLimit = viewLocations[1]; int y = (int) event.getRawY(); return (y > lowerLimit && y < upperLimit); } boolean isMoving() { return (mDragState == ViewDragHelper.STATE_DRAGGING || mDragState == ViewDragHelper.STATE_SETTLING); } class DragHelperCallback extends ViewDragHelper.Callback { @Override public boolean tryCaptureView(View child, int pointerId) { return true; } @Override public int clampViewPositionVertical(View child, int top, int dy) { return top; } @Override public void onViewDragStateChanged(int state) { super.onViewDragStateChanged(state); mDragState = state; } @Override public int getViewVerticalDragRange(View child) { return verticalRange; } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); boolean settleToOpen = false; if (yvel > AUTO_OPEN_SPEED_LIMIT && xvel < yvel) { settleToOpen = true; } else if (yvel < -AUTO_OPEN_SPEED_LIMIT && xvel > yvel) { settleToOpen = false; } else if (mDragBorder > (2 * verticalRange / 3)) { settleToOpen = true; } else if (mDragBorder < (verticalRange / 3)) { settleToOpen = false; } final int settleDestY = settleToOpen ? verticalRange : 0; isExpanded = settleToOpen ? false : true; if (mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), settleDestY)) { ViewCompat.postInvalidateOnAnimation(BottomView.this); } } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); mDragBorder = top < 0 ? 0 : top > verticalRange ? verticalRange : top; float offset = 1 - ((float) mDragBorder / verticalRange); // if (listener != null) listener.onDrag(offset); requestLayout(); } } @Override public void computeScroll() { super.computeScroll(); if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } public boolean expandOnTouchView() { if (isDraggingAllowed(globalEvent) && mDragHelper.smoothSlideViewTo(view, 0, 0)) { isExpanded = true; ViewCompat.postInvalidateOnAnimation(BottomView.this); return true; } return false; } public boolean expandView() { if (mDragHelper.smoothSlideViewTo(view, 0, 0)) { isExpanded = true; ViewCompat.postInvalidateOnAnimation(BottomView.this); return true; } return false; } public boolean collapseOnTouchView() { if (isDraggingAllowed(globalEvent) && mDragHelper.smoothSlideViewTo(view, 0, verticalRange)) { isExpanded = false; ViewCompat.postInvalidateOnAnimation(BottomView.this); return true; } return false; } public boolean collapseView() { if (mDragHelper.smoothSlideViewTo(view, 0, verticalRange)) { isExpanded = false; ViewCompat.postInvalidateOnAnimation(BottomView.this); return true; } return false; } public boolean isViewExpanded() { return isExpanded; } public void setPeekHeight(int peekHeight) { this.peekHeight = peekHeight; requestLayout(); } public void dragHeight(int mDragHeight) { isDragHeightSet = true; this.mDragHeight = mDragHeight; } }2、布局文件
总结
面对不懂的知识点,不要害怕,勇敢面对;一起加油
本文转载自微信公众号「Android开发编程」
很赞哦!(192)
相关文章
- 并非一个好米任何人都会给你一个好的价格。那你该如何用以有的好米卖出最理想的价格呢?
- 域名不仅仅是一个简单的网站。对于有长远眼光的公司来说,在运营网站之前确定一个优秀的域名对有长远眼光的公司来说是非常重要的。这对今后的市场营销、产品营销和企业品牌建设都具有十分重要的意义。优秀的域名是企业在市场竞争中获得持久优势的利器。
- 3、不明先知,根据相关征兆预测可能发生的事件,以便提前做好准备,赶紧注册相关域名。;不差钱域名;buchaqian抢先注册,就是这种敏感类型。预言是最敏感的状态。其次,你应该有眼力。所谓眼力,就是善于从社会上时不时出现的各种热点事件中获取与事件相关的域名资源。眼力的前提是对域名领域的熟悉和丰富的知识。
- 以上的就是为大家介绍的关于域名的详解
- 为什么大家都选优质域名?到底存在着什么好处?
- 只要我们做的是从目前的市场情况选择域名,从简单易记,从个性特征上,我们就可以找到一个好域名进行注册。域名注册进行域名记录和解析以及绑定网站后,客户可以通过URL登录您的网站。
- 主流搜索引擎显示的相关搜索项越多,越能积极反映该域名的市场价值。同时,被评估域名的搜索引擎显示结果不佳可能是由于以下两个原因:
- CNAME:对应解析的记录值为域名地址
- 便宜域名使用如何?小白可以买到便宜域名吗?
- 互联网中的地址是数字的IP地址,域名解析的作用主要就是为了便于记忆。
站长推荐
4.选择顶级的域名注册服务商
为什么说注册域名注意细节?哪些我们不能忽视?
用户邮箱的静态密码可能已被钓鱼和同一密码泄露。在没有收到安全警报的情况下,用户在适当的时间内不能更改密码。在此期间,攻击者可以随意输入帐户。启用辅助身份验证后,如果攻击者无法获取移动电话动态密码,他将无法进行身份验证。这样,除非用户的电子邮件密码和手机同时被盗,否则攻击者很难破解用户的邮箱。
5. 四种状态过后,域名管理机构释放域名给公众注册。
在更换域名后,并不是就万事大吉了,我们需要将旧域名做301重定向到新域名上,转移旧域名的权重到新域名上。
主流搜索引擎显示的相关搜索项越多,越能积极反映该域名的市场价值。同时,被评估域名的搜索引擎显示结果不佳可能是由于以下两个原因:
3、商标域名一经注册,就可以作为域名裁决过程中的主要信息之一。这可以大大增加公司被抢注的相关域名胜诉的机会。
域名资源有限,好域名更是有限,但机会随时都有,这取决于我们能否抓住机会。一般观点认为,国内域名注册太深,建议优先考虑外国注册人。外国注册人相对诚实,但价格差别很大,从几美元到几十美元不等。域名投资者应抓住机遇,尽早注册国外域名。