QQ侧滑面板特效的实现
来源:互联网 发布:怎样兼职网络授课教师 编辑:程序博客网 时间:2024/04/29 16:13
ViewDragHelper的介绍
要实现和QQ5.0侧滑的特效,需要借助谷歌在2013年I/O大会上发布的ViewDragHelper类,提供这个类目的就是为了解决拖拽滑动问题。
使用v4包中的ViewDragHelper为了兼容低版本,所以在创建ViewDragHelper对象时如果找不到ViewDragHelper这个类,可以从sdk中拷贝出最新的v4包覆盖lib目录中的V4包即可。
- 效果图:
/** * Created by Alan on 2016/8/18. * QQ5.0侧滑菜单特效实现 */ //自定义一个类继承FrameLayoutpublic class DragLayout extends FrameLayout {/** * ViewDragHelper: * v4包中的api,2013年谷歌I/O大会上提出,用于解决控件拖动问题。 */ private ViewDragHelper mDragHelper; private View mMenuView;//菜单 private View mMainView;//主界面 private int mWidth;//控件宽度 private int mHeight;///控件高度 private int mMRange;//侧拉菜单打开的最大宽度 public DragLayout(Context context) { super(context); init(); } public DragLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } //4.处理Callback接口中的回调方法 ViewDragHelper.Callback mCallack = new ViewDragHelper.Callback() { /** * 根据返回值决定子控件是否可以拖动, true表示可以拖动 * @param child 被捕获的子控件 * @param pointerId * @return */ @Override public boolean tryCaptureView(View child, int pointerId) { return true; } /**当开始拖动时回调*/ @Override public void onViewCaptured(View capturedChild, int activePointerId) { System.out.println("onViewCaptured -- 拖动"); } /** * 根据返回值决定子控件将要显示的位置 * @param child * @param left * @param dx * @return */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if (child == mMainView) { left = fixDragLeft(left); } return left; } /** * 获取水平方向拖动的最大范围 * 注意:此方法半不会真正限制子控件的滑动范围, * 如果要实现拖动效果,应该返回大于0的值 * @param child * @return mMRange 范围 */ @Override public int getViewHorizontalDragRange(View child) { return mMRange; } /** * 当拖动结束松开手时回调, 需要在此方法中设置菜单为打开或关闭状态 * 侧滑菜单何时打开和关闭? * 打开的情况: * 1) 滑动速度大于0 * 2) 速度是0且位置大于拖拽范围的一半 * 关闭的情况:其他的情况都是关闭 * * @param releasedChild * @param xvel 松开手时水平方向的速度,像素/秒,往右为正 * @param yvel */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); // 松开手时打开或关闭侧滑菜单 if (xvel > 0) { open(); } else if (xvel == 0 && mMainView.getLeft() > mMRange / 2) { open(); } else { close(); } } /** * 当前界面发生改变回调 * 处理逻辑: 关联滑动, 事件监听, 伴随动画 * @param changedView * @param left * @param top * @param dx * @param dy */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { // super.onViewPositionChanged(changedView, left, top, dx, dy); // 当滑动菜单时,同时滑动主界面 if (changedView == mMenuView) { // 保持菜单界面位置不变 mMenuView.layout(0, 0, mWidth, mHeight); // 关联滑动,菜单滑动多少,主界面也滑动多少 int newLeft = mMainView.getLeft() + dx; newLeft = fixDragLeft(newLeft); mMainView.layout(newLeft, 0, newLeft + mWidth, mHeight); } // 监听打开关闭拖动的状态 listenDragState(); // 伴随动画 animateChildren(); } }; /** * 伴随动画 */ private void animateChildren() { float percent = ((float) mMainView.getLeft()) / mMRange; //菜单界面 // 平移: [-mWidth / 2, 0] // 透明度变化: [0.4, 1] // 缩放: [0.5f, 1] mMenuView.setTranslationX(evaluate(-mWidth/2,0,percent)); mMenuView.setAlpha(evaluate(0.4f, 1, percent)); mMenuView.setScaleX(evaluate(0.5f, 1f, percent)); mMenuView.setScaleY(evaluate(0.5f, 1f, percent)); // 主界面: 缩放[1, 0.8f] mMainView.setScaleX(evaluate(1f, 0.8f, percent)); mMainView.setScaleY(evaluate(1f, 0.8f, percent)); Drawable drawable = getBackground(); if (drawable != null) { int color = (int) evaluateColor(percent, Color.BLACK, Color.TRANSPARENT); drawable.setColorFilter(color , PorterDuff.Mode.SRC_OVER); } } /** * 估值器 */ public float evaluate(float start,float end,float percent){ // 中间值 = 开始值 + (结束值 - 开始值) * 百分比 return start+(end - start)*percent; } 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)))); }//------------侧滑菜单事件监听------以下------------ /** * 事件监听(打开,关闭,拖动) */ private void listenDragState() { int left = mMainView.getLeft(); if(left == 0){ mCurrentStatus = DragStatus.CLOSE;//关闭 }else if(left == mMRange){ mCurrentStatus = DragStatus.OPEN;//打开 }else{ mCurrentStatus = DragStatus.DRAGGING;//拖动 } //当事件发生时,回调监听器中的方法 if(mOnDragListener != null){ if(mCurrentStatus == DragStatus.OPEN){ //回调监听器中相应的方法 }else if(mCurrentStatus == DragStatus.CLOSE){ mOnDragListener.onClose(); }else{ float v = ((float) mMainView.getLeft()) / mMRange; mOnDragListener.onDraging(v); } } } public boolean isOpen() { return mCurrentStatus == DragStatus.OPEN; } //定义一个枚举 public enum DragStatus{ OPEN,CLOSE,DRAGGING } private DragStatus mCurrentStatus = DragStatus.CLOSE;//默认状态 /** * 提供状态获取 * @return */ public DragStatus getCurrentStatus() { return mCurrentStatus; } /** * 1.自定义的监听器 */ private OnDragListener mOnDragListener; public interface OnDragListener { void onOpen(); void onClose(); void onDraging(float value); } /** * 给外部提供设置监听器的方法 * @param onDragListener */ public void setOnDragListener(OnDragListener onDragListener){ mOnDragListener = onDragListener; } //--------------事件监听-----以上--------------------------- private int fixDragLeft(int left) { if (left < 0) { left = 0; } else if (left > mMRange) { left = mMRange; } return left; } /** * 关闭菜单 */ private void close() { mMenuView.layout(0, 0, mWidth, mHeight); // mMainView.layout(0,0,mWidth,mHeight); //主界面平滑滚动 mDragHelper.smoothSlideViewTo(mMainView, 0, 0); //刷新 调用顺序Invalidate --> onDraw() --> computeScroll(重写,在此方法里面需要继续刷新) ViewCompat.postInvalidateOnAnimation(this); } /** * 打开菜单 */ private void open() { mMenuView.layout(0, 0, mWidth, mHeight); //mMainView.layout(mMRange,0,mWidth+mMRange,mHeight); // 平滑滚动到某一位置: 第一步 mDragHelper.smoothSlideViewTo(mMainView, mMRange, 0); //刷新 调用顺序Invalidate --> onDraw() --> computeScroll(重写,在此方法里面需要继续刷新) ViewCompat.postInvalidateOnAnimation(this); } @Override public void computeScroll() { super.computeScroll(); //第二步 //如果没有滑动到指定位置,需要继续刷新 if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } private void init() { //1.创建ViewDragHelper对象 float sensitivity = 1.0f;//敏感值,数值越大则越容易滑动菜单 mDragHelper = ViewDragHelper.create(this, sensitivity, mCallack); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 2. 让ViewDragHelper决定是否拦截事件 return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { // 3. 把事件交给ViewDragHelper处理 mDragHelper.processTouchEvent(event); //按下时需要返回true,否则无法接收到后续的move和up事件 if (event.getAction() == MotionEvent.ACTION_DOWN) { return true; } return super.onTouchEvent(event); } /** * 当填充结束后回调此方法,注意:不能在此方法获取控件的宽高 */ @Override protected void onFinishInflate() { super.onFinishInflate(); // 健壮性处理 if (getChildCount() < 2) { throw new IllegalStateException("DragLayout至少要有两个子控件"); } mMenuView = getChildAt(0); mMainView = getChildAt(1); } /** * 执行此方法时,已经调用完了onMeasure,所以可以获取控件宽高 * * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //获取控件的宽高 mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); //设置主界面只能滑到 %60; mMRange = (int) (mWidth * 0.6f); }}
头像左右晃动的实现
监听当侧滑面板关闭时通过属性动画实现左右晃动的动画。
mDragLayout.setOnDragListener(new OnDragListener() { @Override public void open() { } @Override public void onDragging(float percent) { iv_header.setAlpha(1 - percent); // 拖动时设置透明度 } @Override public void close() { showToast("close"); TranslateAnimation animation = new TranslateAnimation(0, 10, 0, 0); animation.setDuration(100); animation.setRepeatCount(4); iv_header.startAnimation(animation); }});
问题解决:
处理菜单打开后主界面列表仍可滑动的问题
自定义主界面根布局控件,并重写拦截方法,当侧滑菜单打开时返回true拦截事件:
public class MyLinearLayout extends LinearLayout { private DragLayout dragLayout; public MyLinearLayout(Context context) { super(context); } public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } public void setDragLayout(DragLayout dragLayout) { this.dragLayout = dragLayout; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // 如果侧菜单打开,则拦截事件,不让列表点击或者滚动 if (dragLayout.isOpen()) { return true; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { //如果菜单打开,不允许触摸主界面时,菜单列表滑动 if(layout!= null && layout.isOpen()){ return true; } return super.onTouchEvent(event); } }
完整源码:点击下载
0 0
- QQ侧滑面板特效的实现
- Android开发之QQ侧滑面板
- 仿QQ侧滑面板(一)
- 仿QQ侧滑面板(二)
- 仿QQ侧滑面板(三)
- 超简单的使用drawerlayout实现QQ的侧拉面板效果
- Android 侧滑面板的实现(DragLayout)
- 使用Javascript实现QQ面板
- QQ伸缩面板Java实现
- QQ伸缩面板Java实现
- Android QQ空间浏览图片动画特效的实现(※)
- 1.仿QQ侧滑面板(对ViewGroup的自定义)
- winform中实现QQ面板效果
- jQuery简单实现-QQ客服浮动面板
- QT版:QQ面板抽屉效果实现
- JQuery实现类QQ面板动画功能
- 自动分页的面板制作|QQ面板|java|Swing
- 表情面板的实现
- Java-Print流,Object流
- 【Python】Python_learning2:python中的冒泡排序
- 模块打包剔除冗余代码
- 极光推送企业开发系列之在Android Studio中集成推送功能
- input提示详细解释
- QQ侧滑面板特效的实现
- XML的概念和解析方式
- cas server4.0 自定义异常
- Spring配置文件参考
- 坐标位置的分享
- 设计模式——建造者模式解析
- 基于树莓派的智能寝室终端(Python练手)1
- VBA笔记
- jmeter 3.0 测试mysql tps