ViewDragHelper 自定义ViewGroup实现QQ5.0侧滑效果
来源:互联网 发布:红辣椒电影数据分析 编辑:程序博客网 时间:2024/04/25 13:13
前言:利用ViewDragHelper 实现一个ViewGroup,学习巩固一下ViewDragHelper的相关知识。先看下效果
一、效果分析
1、QQ5.0侧滑效果,从左边滑动的时候,主页内容移动并且缩小,左边内容放大且平移(主要实现这个效果)
二、首先对实例化ViewDragHelper时候必须要传入的ViewDragHelper.Callback抽象类几个方法的了解
1、在拖动的时候这个child是否可以拖动,如果可以拖动则返回true,否则返回false
@Overridepublic boolean tryCaptureView(View child, int pointerId) { return true;}
2、当拖动的view位置发生变化的时候都会回调这里
///位置改变@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {}
3、水平和垂直方向移动的 坐标,如果返回0,则表示不会移动
@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) { return left;}父类默认返回0 ,所以如果不重写的话垂直方向不会有拖动效果
@Overridepublic int clampViewPositionVertical(View child, int top, int dy) { return super.clampViewPositionVertical(child, top, dy);}
4、手指释放
@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) { }
现在开始我们自定义ViewGroup,首先自定义一个类MySlideMeun继承FrameLayout,FrameLayout包含两个LineLayout,第一个作为左边的菜单,第二个作为主页的菜单
我们要利用ViewDragHelper,就肯定要将onTouch事件交给它来处理,包括事件拦截和事件处理。所以现在在构造方法中实例化一个ViewDragHelper,如果不用包含敏感度的构造函数,就会使用默认的敏感度(简单理解也就是手指滑动多远才算有效)
mViewDragHelper = ViewDragHelper.create(this, new ViewDragHelper.Callback() { //如果当前拖拽的view可以被拖动的话,返回true否则false @Override public boolean tryCaptureView(View child, int pointerId) { return true; }然后MySlideMeun交出onTouch事件拦截和处理
@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { return mViewDragHelper.shouldInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) { mViewDragHelper.processTouchEvent(event); return true;}
接着重写水平方向的移动,
@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) { return left;}
目前为止,ViewDragHelper就可拖动MySlideMeun中的所有子View。
现在把需要的一些对象和参数得到一下
拿到两个linelayout,作为我们滑动的菜单
leftMenu左菜单
contentMenu 主菜单
@Overrideprotected void onFinishInflate() { super.onFinishInflate(); try { leftMenu = (LinearLayout) getChildAt(0); contentMenu = (LinearLayout) getChildAt(1); } catch (Exception e) { e.printStackTrace(); }}水平滑动的范围默认设置为控件宽度的0.8
//控制滑动的范围系数private float scope = 0.8f;在MySlideMeun 的onSizeChanged方法中得到它测量的宽度,并且计算出能够滑动的最大范围
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); widthScope = (int) (getMeasuredWidth()*scope);}
下一阶段:上面的步骤完成之后,就可以滑动了,现在要处理滑动的范围问题,和当我们拖动左边菜单的时候,主菜单也需要滑动相应的距离,下面就来解决一下这两个问题。
首先控制主菜单的范围,主菜单left坐标的应该是从0 到 widthScope
//child,在水平方向移动的dx,left,要移动到的left左边,可以在此进行返回控制@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) { //如果当前拖动的是contentMeun,则修正它移动的坐标 left=child==contentMenu?fixHorX(left):left; return left;}
/** * 修正x左边,contentMenu 水平移动的范围 * @param x */public int fixHorX(int x){ x=x<0?0:x; x=x>widthScope?widthScope:x; return x;}
当左边菜单在发生拖动的时候,移动主菜单的位置,这个时候在拖动左边菜单的时候主菜单就会跟着移动
//所有view的位置改变都会回掉这里@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { //如果拖动的是leftMenu,位置还原到0,0,并且把contentMeun的位置移动 if(changedView==leftMenu){ leftMenu.layout(0,0,leftMenu.getWidth(),leftMenu.getHeight()); left =fixHorX(contentMenu.getLeft()+dx);
contentMenu.layout(left,0,left+contentMenu.getWidth(),contentMenu.getHeight());} }
现在为止,基本实现了拖动的功能,接着需要在手指释放的时候,将主菜单打开或者关闭,我们通过判断释放时候主菜单的left坐标,如果大于滑动范围的一半则打开,否则就关闭它,在打开和关闭的方法中,利用ViewDragHelper的实现慢慢滑动到打开和关闭状态(和scroll基本一样用法)
//释放@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) { if(contentMenu.getLeft()>=widthScope/2){ open(); }else{ close(); }}
public void open(){ //contentMenu从当前坐标移动到最右边 mViewDragHelper.smoothSlideViewTo(contentMenu,widthScope,0); postInvalidate(); Log.i(Tag,"widthScope:"+widthScope);}public void close(){ mViewDragHelper.smoothSlideViewTo(contentMenu,0,0); postInvalidate();}
@Overridepublic void computeScroll() { super.computeScroll(); if(mViewDragHelper.continueSettling(true)){ postInvalidate(); }}
侧滑菜单基本就已经完成了,然后根据每次主菜单left坐标的变化值,对它和左边菜单进行属性动画,计算left坐标除以滑动返回,算出现在已经滑动的百分比,也就是从0.0到1.0的值,根据这个值再利用float估值器FloatEvaluator,就可以很好控制属性动画
@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { //如果拖动的是leftMenu,位置还原到0,0,并且把contentMeun的位置移动 if(changedView==leftMenu){ leftMenu.layout(0,0,leftMenu.getWidth(),leftMenu.getHeight()); left =fixHorX(contentMenu.getLeft()+dx); }else{ left=fixHorX(left); } currentLeft_ContentMeun = left; contentMenu.layout(left,0,left+contentMenu.getWidth(),contentMenu.getHeight()); float percent = currentLeft_ContentMeun*1.0f/widthScope*1.0f; Log.i(Tag,"percent:"+percent+"currentLeft_ContentMeun:"+currentLeft_ContentMeun +"widthScope"+widthScope); doAnim(percent);
public void doAnim(float precent){ //contentMeun,缩小 precent 0,1 缩小 1,0.8 contentMenu.setScaleY(evaluate(precent,1f,0.8f)); //leftMenu x坐标,从-width/2 到0,大小,从0.5 到1 leftMenu.setTranslationX(evaluate(precent,-leftMenu.getWidth()/2,0)); leftMenu.setScaleX(evaluate(precent,0.5,1f)); leftMenu.setScaleY(evaluate(precent,0.5,1f));}
写到这里基本完成90%,最后还有两个问题需要解决,主菜单中的子view如果可以点击,在水平方向要返回一个滑动的范围
@Overridepublic int getViewHorizontalDragRange(View child) { return widthScope;}
那么现在任何时候都可以获取到点击事件,比如有个listview,就可以在主菜单非关闭的时候还是可以滑动,这样体验不太好,需要自定义ViewGroup,在MySlideMenu处于非关闭状态的时候,自定义ViewGroup都会拦截所有事件不会往下传递。
最后处理一个调试时候可能不会出现的bug,在华为手机锁屏再打开后,拖动的view会回到原来未拖动的地方,所以我们需要每次记录下主菜单的left坐标
然后在MySlideMenu执行onlayout方法的时候,重新恢复保存的主菜单的位置
//位置改变@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { //如果拖动的是leftMenu,位置还原到0,0,并且把contentMeun的位置移动 if(changedView==leftMenu){ leftMenu.layout(0,0,leftMenu.getWidth(),leftMenu.getHeight()); left =fixHorX(contentMenu.getLeft()+dx); }else{ left=fixHorX(left); } currentLeft_ContentMeun = left;//保存坐标 contentMenu.layout(left,0,left+contentMenu.getWidth(),contentMenu.getHeight()); float percent = currentLeft_ContentMeun*1.0f/widthScope*1.0f; Log.i(Tag,"percent:"+percent+"currentLeft_ContentMeun:"+currentLeft_ContentMeun +"widthScope"+widthScope); doAnim(percent); updateStatus();}恢复坐标
@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //防止在重新刷新页面的时候出现content回到0,0,恢复contentMenu的位置 contentMenu.layout(currentLeft_ContentMeun,0,contentMenu.getWidth(),contentMenu.getHeight());}
最后一点点就是状态的监听了,添加接口和泛型没有什么好说的,在源码中。。。
源码下载
- ViewDragHelper 自定义ViewGroup实现QQ5.0侧滑效果
- 自定义ViewGroup之QQ5.0侧滑效果实现
- 自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)
- QQ5.0侧滑效果的实现
- ViewDragHelper实现QQ5.0侧滑并处理与ViewPager的滑动冲突
- ViewDragHelper实现QQ5.0侧滑并处理与ViewPager的滑动冲突
- Android ViewDragHelper 实现 QQ5.0 侧滑
- 仿QQ5.0侧滑(基于ViewDragHelper实现)
- 仿QQ5.0侧滑(基于ViewDragHelper实现)
- Android 自定义SlidingMenu 实现QQ5.0侧滑菜单动画效果
- 安卓开发笔记——自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)
- Android QQ5.0 侧滑菜单效果 自定义控件
- 自定义控件之QQ5.0侧滑效果
- 自定义ViewGroup+ViewDragHelper —— 侧滑菜单
- QQ5.0 新版 侧滑菜单效果实现!
- Android实现仿QQ5.0的侧滑效果
- 自定义ViewGroup神器ViewDragHelper
- 自定义ViewGroup笔记--ViewDragHelper
- 动态链接库
- XML学习笔记(三) -- Schema
- 分析与设计(A&D)简介(3)
- BZOJ2109: [Noi2010]Plane 航空管制 解题报告
- hdu 1010 搜索(dfs+剪枝)
- ViewDragHelper 自定义ViewGroup实现QQ5.0侧滑效果
- java 堆内存 与栈内存
- 串口通信设置步骤
- Ubuntu--(5)Linux下C++编译生成自定义静态库/动态库
- 视觉slam学习资料
- 关于HTTP的总结
- [luogu3393 逃离僵尸岛]
- Error: demo (wrong name: Dem o)
- HDFS中dfsadmin命令的使用