美文网首页
Android 使用popuwindow仿drawLayout实

Android 使用popuwindow仿drawLayout实

作者: When_Young | 来源:发表于2019-03-11 11:50 被阅读0次

    Android 使用popuwindow仿drawLayout实现侧滑隐藏

    背景:

    最近公司有一个需求,点击列表项展示列表项详情,我使用popuwindow实现之后公司又要求要实现侧滑返回功能。本着懒的态度我在网上度娘goole一番之后并无头绪。只有撸起袖子自己干。最后功夫不负有心人,最终实现侧滑隐藏,还有很多不足的地方希望大家指正。

    优势:

    使用popuwindow相比drawLayout更大程度的解耦,方便复用。在任何地方都可以弹出popuwindow

    实现思路:

    1、先获取popuwindow的触摸事件

    2、通过触摸事件改变popuwindow的位置

    3、判断滑动位置是否超过阈值,超过就执行消失动画,否者就执行回弹动画

    具体代码

    1、先获取popuwindow的触摸事件

    //先获取 触摸事件
    setTouchInterceptor(this);
    

    2、通过触摸事件改变popuwindow的位置

    如何改变popuwindow的位置,这个问题我在官方api没有找到对应方式,就通过查看popuwindow的源码找到了灵感,下面是源码一部分,通过查看源码不难发现 mDecorView这个成员变量activity的activity.getWindow().getDecorView()所获取的到的类似,就是popuwindow的根view,

      /** View that handles event dispatch and content transitions. */
      private PopupDecorView mDecorView;
    

    之后就是通过反射获取到这个view

     private View getDecorView() {//获取popudowindow 的根view
            try {
                Field field = PopupWindow.class.getDeclaredField("mDecorView");
                field.setAccessible(true);
                return  (View) field.get(this);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            try {//适配api 19
                Field field = PopupWindow.class.getDeclaredField("mPopupView");
                field.setAccessible(true);
                return  (View) field.get(this);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return null;
        }
    
    

    之后就是处理触摸事件来改变mDecorView的位置来实现改变popuwindow的显示位置

        public static final int EVENT_INSIDE=0x001;//初始点在范围外
        public static final int EVENT_OUTSIDE=0x002;//初始点在范围内
        public static final int EVENT_END = 0x003;//结束
        public static final int SLIDE_TYPE_VERTICAL = 0x004;//垂直滑动
        public static final int SLIDE_TYPE_HORIZONTAL = 0x005;//水平滑动
    
    
    
        int eventStatu;
        int slideType;
    
        float eventStartX;
        float eventStartY;
        float viewStartX;
        float viewstartY;
    
    
        float damp = 0.7f;
        float slideTypeXThreshold = 5;//判断滑动方向的阈值
        float slideTypeYThreshold = 5;//判断滑动方向的阈值
        float actionThreshold = 0.25f;//判断当滑动整个弹窗的多少时关闭弹窗
        float distance;
        boolean isAnimation=false;
        int allAnimation = 500;//动画执行最大时间
    
    //处理触摸事件
        private boolean onTouchEvent(MotionEvent ev){
            switch (ev.getAction()){
                case MotionEvent.ACTION_DOWN:
                    if(isAnimation){
                        return true;
                    }
                    //判断按下的点是否在弹窗范围内,如果不是就不处理这个事件了
                    float x = ev.getX();
                    float y = ev.getY();
                    eventStartX = x;
                    eventStartY = y;
    
                    //获取弹窗的范围
                    float popuStartX = rootView.getX();
                    float popuStartY = rootView.getY();
                    viewStartX = popuStartX;
                    viewstartY = popuStartY;
    
    
                    float popuEndX = popuStartX+rootView.getWidth();
                    float popuEndY = popuStartY+rootView.getHeight();
    
                    if(x>=popuStartX&&x<=popuEndX&&y>=popuStartY&&y<=popuEndY){//按下的点在弹窗返回内需要处理本次触摸事件
                        eventStatu = EVENT_INSIDE;
                    }else {
                        eventStatu = EVENT_OUTSIDE;
                    }
                    slideType = 0;//初始化滑动状态
                    distance = 0;
                    mDecorView = null;
    
                    break;
                case MotionEvent.ACTION_UP:
                    if(isAnimation){
                        return true;
                    }
                    eventStatu = EVENT_END;
                    //判断是否需要关闭弹窗
                    if(distance/actionThreshold/damp>=rootView.getWidth()){
                        setDismissAnimation();
                    }else {//回弹效果
                        setSpringbackAnimation();
                    }
    
    
                    break;
                case MotionEvent.ACTION_MOVE:
                    if(isAnimation){
                        return true;
                    }
                    if(eventStatu==EVENT_INSIDE){
                        //如果在竖直滑动之后不处理水平滑动了
                        float newX = ev.getX();
                        float relXDistance = newX - eventStartX;//真实位移
                        float newY = ev.getY();
                        float relYDistance = newY - eventStartY;//真实位移
    
                        if(slideType!=SLIDE_TYPE_HORIZONTAL&&Math.abs(relYDistance)>DensityUtils.dip2px(context,slideTypeYThreshold)){//如果大于阈值
                            slideType = SLIDE_TYPE_VERTICAL;
                        }
    
                        if(slideType!=SLIDE_TYPE_VERTICAL&&Math.abs(relXDistance)>DensityUtils.dip2px(context,slideTypeXThreshold)){//如果大于阈值
                            slideType = SLIDE_TYPE_HORIZONTAL;
                        }
    
                        if(slideType==SLIDE_TYPE_VERTICAL){//如果是竖直方向就继续处理了
                            return false;
                        }
                        if(slideType==SLIDE_TYPE_HORIZONTAL){
                            if(distance<0){
                                return true;
                            }
    
                            distance = relXDistance*damp;//乘以阻尼之后的距离
                            setMDecorView(viewStartX+distance);
                            return true;
                        }
    
    
                    }
                    break;
            }
    
            return false;
        }
    
    
        private void setMDecorView(float x) {
            //改变view的位置
            if(mDecorView==null){
                mDecorView = getDecorView();
            }
    
            if(mDecorView!=null){
                if(x>=viewStartX){//避免移动位置越过popuwindow的初始位置
                    mDecorView.setX(x);
                }
            }
        }
    
    
    
    

    最后就是在触摸事件结束时判断是否滑动距离超过阈值,如果超过就执行隐藏动画,否者就执行回弹

     //判断是否需要关闭弹窗
                    if(distance/actionThreshold/damp>=rootView.getWidth()){
                        setDismissAnimation();
                    }else {//回弹效果
                        setSpringbackAnimation();
                    }
    

    下面是消失动画和回弹动画

       private void setDismissAnimation() {
            //改变view的位置
            if(mDecorView==null){
                mDecorView = getDecorView();
            }
    
            if(mDecorView==null){
                return;
            }
    
    
            float width =Utils.getScreenWidth(context);
            float offSet= width-mDecorView.getX();
            if(offSet<0){
                return;
            }
            int duration = (int) (300*(offSet/(mDecorView.getWidth()*(1-actionThreshold))));
            ObjectAnimator animator = ObjectAnimator.ofFloat(mDecorView,"translationX",mDecorView.getX(),width);
            animator.setDuration(duration);
            animator.start();
            animator.addListener(new AnimatorListenerAdapter() {
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    isAnimation=false;
                    dismiss();
                }
    
                @Override
                public void onAnimationStart(Animator animation) {
                    super.onAnimationStart(animation);
                    isAnimation=true;
    
                }
            });
        }
    private void setSpringbackAnimation() {
            //改变view的位置
            if(mDecorView==null){
                mDecorView = getDecorView();
            }
    
            if(mDecorView==null){
                return;
            }
            float offSet= mDecorView.getX()-viewStartX;
            if(offSet<0){
                return;
            }
            int duration = (int) (allAnimation*(offSet/(mDecorView.getWidth()*actionThreshold)));
            ObjectAnimator animator = ObjectAnimator.ofFloat(mDecorView,"translationX",mDecorView.getX(),viewStartX);
            animator.setDuration(duration);
            animator.start();
            animator.addListener(new AnimatorListenerAdapter() {
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    isAnimation=false;
                }
    
                @Override
                public void onAnimationStart(Animator animation) {
                    super.onAnimationStart(animation);
                    isAnimation=true;
                }
            });
        }
    

    到此就完全完成了

    相关文章

      网友评论

          本文标题:Android 使用popuwindow仿drawLayout实

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