自定义抽屉—QQ特效
来源:互联网 发布:java笔试基础题 编辑:程序博客网 时间:2024/05/17 21:50
这个我是根据黑马视频做的,用来总结一下。抽屉用到的很多,下面写的比较麻烦,需要读者有耐心的看完。写的不对的地方请提出来。抽屉我继承的是FrameLayout,因为他有测量的过程,省略了我手动测量。它最重要的就是实现ViewDragHelper的回调方法。文章最后有下载地址。首先,我们在自定义的View的构造方法里面初始化ViewDragHelper,他是单例模式创建的,并不是new出来的。定义成全局的。`viewDragHelper = ViewDragHelper.create(this, mCallBack);`然后就是重写他的回调方法。回调方法,我都是按他们调用的顺序排好了。
@Override public boolean tryCaptureView(View child, int pointerId) { //尝试拖拽当前View,如果不能拖拽,就不执行之后的方法 return true; }
child是当前触摸的View,pointerId是区分多点触摸的Id(没什么用)。注释里面写的有根据返回值判断当前View能不能被拖拽。如果是true,就是不论那个View都能被拖拽。抽屉一般有两个界面。这里就是在一个XML里面写两个平级的Layout就可以了,就是两个界面。比方说,我现在有两个Layout,一个是mMainContent,另一个是mLeftContent。这里如果写成return child==mLeftContent,那就是只有mLeftContent布局能够被拖拽。这里我们就写成return true。后面会有说明。因为你限制只能拖拽mMainContent,当你拖拽mLeftContent的时候,你还想让mMainContent跟着移动。这个时候就需要测量位置,但是如果mLeftContent不能被拖拽,那他就不会执行后面的测量方法。你这里让mLeftContent可以被拖拽,在你拖拽的时候,你重绘界面就可以了,把mLeftContent的位置写死,他就不会移动了,同时也会走后面的测量方法,这样就达到了我们的目的。
//当View被捕获的时候调用 @Override public void onViewCaptured(View capturedChild, int activePointerId) { super.onViewCaptured(capturedChild, activePointerId); }
这个方法是当View被捕获的时候调用,前面一个方法是尝试捕获。如果,你正在尝试捕获的View不能被拖拽,那他就获取不到当前View。
//获取当前View可以横向拖拽的范围 @Override public int getViewHorizontalDragRange(View child) { //可拖拽的范围,但是不对拖拽进行限制,仅仅是根据可拖拽的范围计算动画的时长 return mRange; }
这个方法注释里面有,你拖拽的时候,不可能拖拽到屏幕外面去,如果出现这样的结果,那就是你代码有bug了。这个方法就是避免这样的情况产生。这个范围我写的是0~0.6f*screenWidth。0到0.6倍的当前屏幕宽度。这是横向的,既然有横向的就一定有纵向的。没必要写纵向的。mRange是怎么来的呢?下面这个方法。
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //在onMeasure方法之后调用,并且只有在当前尺寸与之前测量的不相同时调用 mHeight = getMeasuredHeight(); //获取测量的屏幕宽度 mWidth = getMeasuredWidth(); //获取测量的屏幕高度 mRange = (int) (mWidth * 0.6f); //获取可拖拽的横向范围 }
onMeasure方法大家可能都知道,测量宽高的方法,这个方法在onMeasure方法之后调用,并且只有在当前尺寸与之前测量的不相同时调用,就这样,就获取到了mRange。那最大移动的范围出来了,我们要怎么移动呢?实现下面这个方法
@Override public int clampViewPositionHorizontal(View child, int left, int dx) { //oldLeft:child.getLeft() //left=oldLeft+dx if (child == mMainContent) { left = fixLeft(left); } return left; }
这个方法是:根据建议值修正将要移动到的(横向)位置,此时还没有发生移动。这个方法也有纵向的。child是当前拖拽的View,left是新位置的建议值。dx是位置的变化量。left=oldLeft+dx。oldLeft:child.getLeft()。根据范围修正左边值,我把left的方法提取出来了,后面还有位置用的到。
private int fixLeft(int left) { if (left < 0) { return 0; } else if (left > mRange) { return mRange; } else { return left; } }
前面这个方法是还没有移动的时候调用,这个时候屏幕还不会动,当View发生移动的时候调用下面这个方法。
@Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); int newLeft = left; if (changedView == mLeftContent) { newLeft = mMainContent.getLeft() + dx; } newLeft = fixLeft(newLeft); //要保证移动的距离不能超过mRange if (changedView == mLeftContent) { //mLeftContent保持不变,动态移动mMainContent mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight); mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight); } //更新状态,执行动画 dispatchDragEvent(newLeft); //为了兼容低版本,在每次修改值之后,都要进行重绘 invalidate(); }
移动之后还有一个松开点,这个就是调用下面这个方法。
/** * 4.当View被释放时,处理的事情(执行动画) * * @param releasedChild 被释放的View * @param xvel 水平方向的速度,向右为正 * @param yvel 垂直方向的速度,向下为正 */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); if (xvel == 0 && mMainContent.getLeft() > mRange / 2.0f) { open(); } else if (xvel > 0) { open(); } else { close(); } }
我们需要有一个平滑的移动过程,而不是瞬移。所以,我们要加一个平滑的动画。后面的我也不知道该怎么解释。文字表达能力有限,大家就认真的看完就好了。就是动画效果。是用的github上面一个大牛整理好的。
private void animView(float percent) { // >1.左面板:缩放动画,平移动画,透明动画// mLeftContent.setScaleX(0.5f + 0.5f * percent);// mLeftContent.setScaleY(0.5f + 0.5f * percent); //缩放动画 ViewHelper.setScaleX(mLeftContent, evaluate(percent, 0.5f, 1.0f)); ViewHelper.setScaleY(mLeftContent, 0.5f + 0.5f * percent); //平移动画 ViewHelper.setTranslationX(mLeftContent, evaluate(percent, -mWidth / 2.0f, 0)); //透明度动画 ViewHelper.setAlpha(mLeftContent, evaluate(percent, 0.5f, 1.0f)); // >2.主面板:缩放动画1.0->0.8 ViewHelper.setScaleX(mMainContent, evaluate(percent, 1.0f, 0.8f)); ViewHelper.setScaleY(mMainContent, evaluate(percent, 1.0f, 0.8f)); // >3.背景:亮度变化(颜色变化)// getBackground().setColorFilter((Integer) evaluateColor(percent, Color.YELLOW,Color.BLUE), PorterDuff.Mode.SRC_OVER); getBackground().setColorFilter((Integer) evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.SRC_OVER); }
/** * 颜色变化过度 * * @param fraction * @param startValue * @param endValue * @return */ public Object evaluateColor(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24) & 0xff; int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int) ((startA + (int) (fraction * (endA - startA))) << 24) | (int) ((startR + (int) (fraction * (endR - startR))) << 16) | (int) ((startG + (int) (fraction * (endG - startG))) << 8) | (int) ((startB + (int) (fraction * (endB - startB)))); } /** * 估值器 * * @param percentage 百分比 * @param startValue 开始值 * @param endVlaue 结束值 * @return */ public Float evaluate(Float percentage, Number startValue, Number endVlaue) { float startFloat = startValue.floatValue(); return startFloat + percentage * (endVlaue.floatValue() - startFloat); }
上面这个几个方法就是这样写死,会用就行了。这个我就是copy官方文档里面的。下面这个也是比较重要的,但是不难。
//b.传递触摸事件 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //传递给viewDragHelper,是否需要拦截触摸事件 return viewDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { try { //处理触摸事件,多点触摸有问题 viewDragHelper.processTouchEvent(event); } catch (Exception e) { e.printStackTrace(); } //返回true,持续接收事件 return true; } /** * 当xml添加完成时调用 */ @Override protected void onFinishInflate() { super.onFinishInflate(); if (getChildCount() < 2) { throw new IllegalStateException("Your ViewGroup must have 2 children at least."); } if (!(getChildAt(0) instanceof ViewGroup && getChildAt(1) instanceof ViewGroup)) { throw new IllegalArgumentException("Your children must be instanceof ViewGroup."); } mLeftContent = (ViewGroup) getChildAt(0); mMainContent = (ViewGroup) getChildAt(1); }
持续动画的代码
//持续动画 @Override public void computeScroll() { super.computeScroll(); if (viewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } }
算了,不说了,基本上都说了。下面给出下载项目的链接。下载了自己认真瞅瞅就好了。
项目链接
0 0
- 自定义抽屉—QQ特效
- Android自定义控件:类QQ抽屉效果
- 自定义抽屉
- 自定义抽屉
- iOS开发之有趣的UI —— 抽屉特效
- 自定义View实现手机qq5.X的抽屉特效和聊天界面联系人左滑功能
- 抽屉吐司——自定义的Toast
- iOS 自定义控件——抽屉功能
- jquery实现抽屉式特效
- Android——Slidingmenu互挤抽屉(QQ)应用
- android抽屉侧滑(仿QQ)自定义类,并未使用第三包。
- 仿QQ抽屉式窗体
- 模仿qq的抽屉view
- iOS 仿QQ抽屉效果
- 自定义抽屉控件
- 自定义抽屉 指定把手
- 自定义View---抽屉效果
- 自定义抽屉布局
- Java的引用数据类型及应用
- mac 下配置protobuf 3.0 golang环境
- 图片压缩生成bitmap工具
- Retrofit使用一:超简单先用上
- Sublime Text 3 快捷键总结(拿走)
- 自定义抽屉—QQ特效
- 使用maven的profile和filter插件管理配置项
- LeetCode *** 33. Search in Rotated Sorted Array
- [AngularJS] EasyModal - alert - confirm - modal
- PAT (Basic Level) Practise (中文)1024. 科学计数法 (20)
- WebLogic配置JNDI数据源
- php 实现简单的登录
- Oracle--通配符、Escape转义字符、模糊查询语句
- jdk默认提供的类加载器