美文网首页
(六)仿QQ首页drawer/侧滑删除/浮动imgaeView/

(六)仿QQ首页drawer/侧滑删除/浮动imgaeView/

作者: 达浪儿 | 来源:发表于2018-11-23 15:00 被阅读0次

    效果图如下:


    demo6.gif

    1.首页左侧drawerLaout
    借鉴 https://github.com/qiantao94/CoordinatorMenu
    小作修改:
    因为该库不支持自定义侧边栏的宽度,我这边增加了一个属性drawerPercent,是指侧边栏占据手机屏幕的百分比。

     <com.dl.common.widget.drawerlayout.DrawerMenu
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/menu"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:drawerPercent="0.8">
    
            <include layout="@layout/demo6_layout_drawer_view" />
    
            <include layout="@layout/demo6_layout_main_view" />
    
    
        </com.dl.common.widget.drawerlayout.DrawerMenu>
    
    

    2.item侧滑删除
    借鉴 https://github.com/mcxtzhang/SwipeDelMenuLayout
    注意:如果侧滑删除要和角标拖拽一起使用,直接依赖该库会出现滑动冲突。需要做如下修改:

     @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            LogUtils.d(TAG, "dispatchTouchEvent() called with: " + "ev = [" + ev + "]"+isSwipeEnable);
            if (isSwipeEnable) {
                acquireVelocityTracker(ev);
                final VelocityTracker verTracker = mVelocityTracker;
                switch (ev.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        isUserSwiped = false;//2016 11 03 add,判断手指起始落点,如果距离属于滑动了,就屏蔽一切点击事件。
                        isUnMoved = true;//2016 10 22 add , 仿QQ,侧滑菜单展开时,点击内容区域,关闭侧滑菜单。
                        iosInterceptFlag = false;//add by 2016 09 11 ,每次DOWN时,默认是不拦截的
                        if (isTouching) {//如果有别的指头摸过了,那么就return false。这样后续的move..等事件也不会再来找这个View了。
                            return false;
                        } else {
                            isTouching = true;//第一个摸的指头,赶紧改变标志,宣誓主权。
                        }
    
                        mLastP.set(ev.getRawX(), ev.getRawY());
                        mFirstP.set(ev.getRawX(), ev.getRawY());//2016 11 03 add,判断手指起始落点,如果距离属于滑动了,就屏蔽一切点击事件。
    
                        //如果down,view和cacheview不一样,则立马让它还原。且把它置为null
                        if (mViewCache != null) {
                            LogUtil.d("4");
                            if (mViewCache != this) {
                                mViewCache.smoothClose();
    
                                iosInterceptFlag = isIos;//add by 2016 09 11 ,IOS模式开启的话,且当前有侧滑菜单的View,且不是自己的,就该拦截事件咯。
                            }
    
                            //只要有一个侧滑菜单处于打开状态, 就不给外层布局上下滑动了
                            getParent().requestDisallowInterceptTouchEvent(true);
                        }
    
                        //求第一个触点的id, 此时可能有多个触点,但至少一个,计算滑动速率用
                        mPointerId = ev.getPointerId(0);
    
                        break;
                    case MotionEvent.ACTION_MOVE:
                        //add by 2016 09 11 ,IOS模式开启的话,且当前有侧滑菜单的View,且不是自己的,就该拦截事件咯。滑动也不该出现
                        if (iosInterceptFlag) {
                            break;
                        }
                        float gap = mLastP.x - ev.getRawX();
                        //为了在水平滑动中禁止父类ListView等再竖直滑动
                        if (Math.abs(gap) > 10 || Math.abs(getScrollX()) > 10) {//2016 09 29 修改此处,使屏蔽父布局滑动更加灵敏,
                            getParent().requestDisallowInterceptTouchEvent(true);
                        }
                        //2016 10 22 add , 仿QQ,侧滑菜单展开时,点击内容区域,关闭侧滑菜单。begin
                        if (Math.abs(gap) > mScaleTouchSlop) {
                            isUnMoved = false;
                        }
                        //2016 10 22 add , 仿QQ,侧滑菜单展开时,点击内容区域,关闭侧滑菜单。end
                        //如果scroller还没有滑动结束 停止滑动动画
    /*                    if (!mScroller.isFinished()) {
                            mScroller.abortAnimation();
                        }*/
                        scrollBy((int) (gap), 0);//滑动使用scrollBy
                        //越界修正
                        if (isLeftSwipe) {//左滑
                            if (getScrollX() < 0) {
                                scrollTo(0, 0);
                            }
                            if (getScrollX() > mRightMenuWidths) {
                                scrollTo(mRightMenuWidths, 0);
                            }
                        } else {//右滑
                            if (getScrollX() < -mRightMenuWidths) {
                                scrollTo(-mRightMenuWidths, 0);
                            }
                            if (getScrollX() > 0) {
                                scrollTo(0, 0);
                            }
                        }
    
                        mLastP.set(ev.getRawX(), ev.getRawY());
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        //2016 11 03 add,判断手指起始落点,如果距离属于滑动了,就屏蔽一切点击事件。
                        if (Math.abs(ev.getRawX() - mFirstP.x) > mScaleTouchSlop) {
                            isUserSwiped = true;
                        }
    
                        //add by 2016 09 11 ,IOS模式开启的话,且当前有侧滑菜单的View,且不是自己的,就该拦截事件咯。滑动也不该出现
                        if (!iosInterceptFlag) {//且滑动了 才判断是否要收起、展开menu
                            //求伪瞬时速度
                            verTracker.computeCurrentVelocity(1000, mMaxVelocity);
                            final float velocityX = verTracker.getXVelocity(mPointerId);
                            if (Math.abs(velocityX) > 1000) {//滑动速度超过阈值
                                if (velocityX < -1000) {
                                    if (isLeftSwipe) {//左滑
                                        //平滑展开Menu
                                        smoothExpand();
    
                                    } else {
                                        //平滑关闭Menu
                                        smoothClose();
                                    }
                                } else {
                                    if (isLeftSwipe) {//左滑
                                        // 平滑关闭Menu
                                        smoothClose();
                                    } else {
                                        //平滑展开Menu
                                        smoothExpand();
    
                                    }
                                }
                            } else {
                                if (Math.abs(getScrollX()) > mLimit) {//否则就判断滑动距离
                                    //平滑展开Menu
                                    smoothExpand();
                                } else {
                                    // 平滑关闭Menu
                                    smoothClose();
                                }
                            }
                        }
           
                        releaseVelocityTracker();
                        isTouching = false;
                        break;
                    default:
                        break;
                }
            } else {
                isTouching=false; //此处是处理冲突的位置,必须加
            }
            return super.dispatchTouchEvent(ev);
        }
    

    3.消息角标自由拖拽
    借鉴:https://github.com/qstumn/BadgeView
    如果开启拖拽和滑动删除一起使用,上面冲突要处理,下面代码也要写

           badge.setOnDragStateChangedListener((dragState, badge1, targetView) -> {
          //拖拽成功和拖拽取消后开启侧滑  其他状态关闭侧滑  
      //必须调用smoothClose()
                if (dragState == Badge.OnDragStateChangedListener.STATE_SUCCEED) {
                    ((SwipeMenuLayout) helper.getRecyclerViewHolder().itemView).smoothClose();
                    ((SwipeMenuLayout) helper.getRecyclerViewHolder().itemView).setIos(true).setSwipeEnable(true);
                } else if (dragState == Badge.OnDragStateChangedListener.STATE_CANCELED) {
                    ((SwipeMenuLayout) helper.getRecyclerViewHolder().itemView).smoothClose();
                    ((SwipeMenuLayout) helper.getRecyclerViewHolder().itemView).setIos(true).setSwipeEnable(true);
    
                } else {
                    ((SwipeMenuLayout) helper.getRecyclerViewHolder().itemView).smoothClose();
                    ((SwipeMenuLayout) helper.getRecyclerViewHolder().itemView).setIos(false).setSwipeEnable(false);
                }
            });
            badge.setBadgeNumber(model.getCount());
    

    4.可浮动的ImageView

    @SuppressLint("AppCompatCustomView")
    public class MoveImageView extends ImageView {
    
        private Drawable mDrawable;
        private int mLeft = 0;
        private int mTop = 0;
        private int mSpeed = 2;
        private boolean isSetVerticalMove;
        private boolean isMoveLeft;
        private boolean isMoveUp;
        private Handler mHandler;
        private int mCanvasBgSize;
    
    
    
        public MoveImageView(Context context) {
            super(context);
        }
    
        public MoveImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setUp(context, attrs);
        }
    
        public MoveImageView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            setUp(context, attrs);
        }
    
        private void setUp(Context context, AttributeSet attrs) {
            TypedArray a =context.obtainStyledAttributes(attrs, R.styleable.MoveImage);
            int direction=a.getInteger(R.styleable.MoveImage_direction,0);
            mSpeed=a.getInteger(R.styleable.MoveImage_speed,2);
    
            if (direction == 0) {
                isSetVerticalMove = true;
            } else {
                isSetVerticalMove = false;
            }
    
            mDrawable = getDrawable();
            mHandler = new MoveHandler();
            mHandler.sendEmptyMessageDelayed(1, 220L);
    
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (isSetVerticalMove) {
                canvas.translate(0.0F, mTop);
            } else {
                canvas.translate(mLeft, 0.0F);
            }
            mDrawable.draw(canvas);
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
    
            if (isSetVerticalMove) {
                mCanvasBgSize = getMeasuredHeight() * 3 / 2;
                mDrawable.setBounds(0, 0, getMeasuredWidth(), mCanvasBgSize);
            } else {
                mCanvasBgSize = getMeasuredWidth() * 3 / 2;
                mDrawable.setBounds(0, 0, mCanvasBgSize, getMaxHeight());
            }
        }
    
        private class MoveHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (isSetVerticalMove) {
                    if (isMoveUp) {
                        if (mTop <= getMeasuredHeight() - mCanvasBgSize)//此时表示移到了最up的位置
                        {
                            mTop += mSpeed;
                            isMoveUp = false;
                        } else//继续下移
                        {
                            mTop -= mSpeed;
                        }
                    } else {
                        if (mTop == 0)//此时表示移动到了最down,此时图片的up侧应该与屏幕up侧对齐,即坐标值为0
                        {
                            mTop -= mSpeed;
                            isMoveUp = true;//图片已经移动到了最down侧,需要修改其移动方向为up
                        } else {
    
                            mTop += mSpeed;//继续下移
                        }
                    }
                } else {
                    if (isMoveLeft)//向左移动
                    {
    
                        if (mLeft <= getMeasuredWidth() - mCanvasBgSize)//此时表示移到了最左侧的位置
                        {
                            mLeft += mSpeed;
                            isMoveLeft = false;
                        } else//继续左移
                        {
                            mLeft -= mSpeed;
                        }
    
                    } else {
                        if (mLeft == 0)//此时表示移动到了最右侧,此时图片的左侧应该与屏幕左侧对齐,即坐标值为0
                        {
                            mLeft -= mSpeed;
                            isMoveLeft = true;//图片已经移动到了最右侧,需要修改其移动方向为向左
                        } else {
                            mLeft += mSpeed;//继续右移
                        }
                    }
                }
                invalidate();
                mHandler.sendEmptyMessageDelayed(1, 22);
            }
        }
    }
    

    5.讨论组头像展示
    借鉴 https://github.com/jinyb09017/MutiImgLoader

    6.仿IOS弹性scrollview

    /**
     * created by dalang at 2018/11/21
     * 仿IOS 弹性scrollview
     */
    public class ReboundScrollView extends NestedScrollView {
    
        //移动因子, 是一个百分比, 比如手指移动了100px, 那么View就只移动50px
        //目的是达到一个延迟的效果
        private static final float MOVE_FACTOR = 0.3f;
    
        //松开手指后, 界面回到正常位置需要的动画时间
        private static final int ANIM_TIME = 300;
    
        //ScrollView的子View, 也是ScrollView的唯一一个子View
        private View contentView;
    
        //手指按下时的Y值, 用于在移动时计算移动距离
        //如果按下时不能上拉和下拉, 会在手指移动时更新为当前手指的Y值
        private float startY;
    
        //用于记录正常的布局位置
        private Rect originalRect = new Rect();
    
        //手指按下时记录是否可以继续下拉
        private boolean canPullDown = false;
    
        //手指按下时记录是否可以继续上拉
        private boolean canPullUp = false;
    
        //在手指滑动的过程中记录是否移动了布局
        private boolean isMoved = false;
    
    
        public ReboundScrollView(Context context) {
            super(context);
        }
    
    
        public ReboundScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            if (getChildCount() > 0) {
                contentView = getChildAt(0);
            }
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
    
            if(contentView == null) return;
    
            //ScrollView中的唯一子控件的位置信息, 这个位置信息在整个控件的生命周期中保持不变
            originalRect.set(contentView.getLeft(), contentView.getTop(), contentView
                    .getRight(), contentView.getBottom());
        }
    
        /**
         * 在触摸事件中, 处理上拉和下拉的逻辑
         */
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
    
            if (contentView == null) {
                return super.dispatchTouchEvent(ev);
            }
    
            int action = ev.getAction();
    
            switch (action) {
                case MotionEvent.ACTION_DOWN:
    
                    //判断是否可以上拉和下拉
                    canPullDown = isCanPullDown();
                    canPullUp = isCanPullUp();
    
                    //记录按下时的Y值
                    startY = ev.getY();
                    break;
    
                case MotionEvent.ACTION_UP:
    
                    if(!isMoved) break;  //如果没有移动布局, 则跳过执行
    
                    // 开启动画
                    TranslateAnimation anim = new TranslateAnimation(0, 0, contentView.getTop(),
                            originalRect.top);
                    anim.setDuration(ANIM_TIME);
    
                    contentView.startAnimation(anim);
    
                    // 设置回到正常的布局位置
                    contentView.layout(originalRect.left, originalRect.top,
                            originalRect.right, originalRect.bottom);
    
                    //将标志位设回false
                    canPullDown = false;
                    canPullUp = false;
                    isMoved = false;
    
                    break;
                case MotionEvent.ACTION_MOVE:
    
                    //在移动的过程中, 既没有滚动到可以上拉的程度, 也没有滚动到可以下拉的程度
                    if(!canPullDown && !canPullUp) {
                        startY = ev.getY();
                        canPullDown = isCanPullDown();
                        canPullUp = isCanPullUp();
    
                        break;
                    }
    
                    //计算手指移动的距离
                    float nowY = ev.getY();
                    int deltaY = (int) (nowY - startY);
    
                    //是否应该移动布局
                    boolean shouldMove =
                            (canPullDown && deltaY > 0)    //可以下拉, 并且手指向下移动
                                    || (canPullUp && deltaY< 0)    //可以上拉, 并且手指向上移动
                                    || (canPullUp && canPullDown); //既可以上拉也可以下拉
                                (这种情况出现在ScrollView包裹的控件比ScrollView还小)
    
                    if(shouldMove){
                        //计算偏移量
                        int offset = (int)(deltaY * MOVE_FACTOR);
    
                        //随着手指的移动而移动布局
                        contentView.layout(originalRect.left, originalRect.top + offset,
                                originalRect.right, originalRect.bottom + offset);
    
                        isMoved = true;  //记录移动了布局
                    }
    
                    break;
                default:
                    break;
            }
    
            return super.dispatchTouchEvent(ev);
        }
    
    
        /**
         * 判断是否滚动到顶部
         */
        private boolean isCanPullDown() {
            return getScrollY() == 0 ||
                    contentView.getHeight() < getHeight() + getScrollY();
        }
    
        /**
         * 判断是否滚动到底部
         */
        private boolean isCanPullUp() {
            return  contentView.getHeight() <= getHeight() + getScrollY();
        }
    }
    

    其他文章链接地址:
    (一)高斯模糊实现毛玻璃效果丶共享元素动画 丶地址选择器
    (二)仿京东顶部伸缩渐变丶自定义viewpager指示器丶viewpager3D回廊丶recyclerview瀑布流
    (三)RxJava2常用操作符merge、flatmap、zip--结合MVP架构讲解
    (四)仿支付宝首页顶部伸缩滑动/中间层下拉刷新
    (五)TabLayout+ViewPager悬浮吸顶及刷新数量动画显示
    (七)仿微信发布朋友圈拖拽删除

    将持续更新.. 不喜勿喷,仅个人分享,希望能帮助到你

    源码地址:Github传送门

    相关文章

      网友评论

          本文标题:(六)仿QQ首页drawer/侧滑删除/浮动imgaeView/

          本文链接:https://www.haomeiwen.com/subject/cqejqqtx.html