AndroidSweetSheet类库的使用

普通的弹框多以dialog的形式弹出,这个类库是以布局的形式弹出来的。

以下是作者的源类库中的包定义:

SweetSheet.class

代码中主要使用的类,在初始化时候需要传入依附的父布局(以后弹出的view就是要加入到这个view中的):

// SweetSheet 控件,根据 rl 确认位置
        mSweetSheet = new SweetSheet(rl);

然后将代理类设置到该类中,以后实际就是通过代理类来控制属性:

 //根据设置不同的 Delegate 来显示不同的风格.
        mSweetSheet.setDelegate(new RecyclerViewDelegate(true));

实际上调用:

 //设置代理对象
    public void setDelegate(Delegate delegate) {
        mDelegate = delegate;
        mDelegate.init(mParentVG);
        setup();

    }
这里通过代理类中的初始化方法来先初始化一个点击背景(该背景可以设置模糊的效果),加载列表代理类RecyclerViewDelegate实现的抽象方法createView(),实现如下:

 @Override
    protected View createView() {
        View rootView = LayoutInflater.from(mParentVG.getContext()).inflate(R.layout.layout_rv_sweet, null, false);
        //动画效果控件
        mSweetView = (SweetView) rootView.findViewById(R.id.sv);
        //控制随着手势移动后重绘布局
        mFreeGrowUpParentRelativeLayout = (FreeGrowUpParentRelativeLayout) rootView.findViewById(R.id.freeGrowUpParentF);
        //列表
        mRV = (RecyclerView) rootView.findViewById(R.id.rv);
        //手势拖动布局
        sliderIm = (CRImageView) rootView.findViewById(R.id.sliderIM);
        mRV.setLayoutManager(new LinearLayoutManager(mParentVG.getContext(), LinearLayoutManager.VERTICAL, false));
        mSweetView.setAnimationListener(new AnimationImp());
        //设置内容的高度
        if (mContentViewHeight > 0) {
            mFreeGrowUpParentRelativeLayout.setContentHeight(mContentViewHeight);
        }
        return rootView;
    }

然后需要传入数据集合:

//设置数据源 (数据源支持设置 list 数组,也支持从菜单中获取)
        mSweetSheet.setMenuList(list);
实际上是调用了代理抽象类中的方法:

 /**
     * 设置数据源
     *
     * @param list
     */
    protected abstract void setMenuList(List<MenuEntity> list);
不同的子代理可以通过不同的途径来实现该方法以展示数据的显示,例如列表子代理RecyclerViewDelegate的实现如下:

//设置数据源之后
    protected void setMenuList(final List<MenuEntity> menuEntities) {
        mMenuRVAdapter = new MenuRVAdapter(menuEntities, SweetSheet.Type.RecyclerView);
        mRV.setAdapter(mMenuRVAdapter);
        mMenuRVAdapter.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (mOnMenuItemClickListener != null) {
                    if (mOnMenuItemClickListener.onItemClick(position, menuEntities.get(position))) {
                        delayedDismiss();
                    }
                }
            }
        });
        mRV.setLayoutAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                mRV.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        return true;
                    }
                });
                mFreeGrowUpParentRelativeLayout.setClipChildren(false);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mRV.setOnTouchListener(null);

                mFreeGrowUpParentRelativeLayout.setClipChildren(true);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
这里就是简单的设置了适配器和监听器。

然后还可以设置背景的效果:

//根据设置不同Effect 来显示背景效果BlurEffect:模糊效果.DimEffect 变暗效果
        mSweetSheet.setBackgroundEffect(new BlurEffect(8));
设置菜单点击监听:

//设置点击事件
        mSweetSheet.setOnMenuItemClickListener(new SweetSheet.OnMenuItemClickListener() {
            @Override
            public boolean onItemClick(int position, MenuEntity menuEntity1) {

                //根据返回值, true 会关闭 SweetSheet ,false 则不会.
                Toast.makeText(MainActivity.this, menuEntity1.title + "  " + position, Toast.LENGTH_SHORT).show();
                return true;
            }
        });

以上就是一些初始化的操作,总结就是再xml的一个父布局中先加入一个背景view,再加入一个内容的view.


接下来就是点击触发弹出效果的过程:

SweetSheet类中主要提供了这三个方法来控制菜单的显示与移除,实际也是通过代理类来实现的:

 //显示菜单
    public void show() {
        if (mDelegate != null) {
            mDelegate.show();
        } else {
            Log.e(Tag, "you must setDelegate before");
        }
    }

    //关闭菜单
    public void dismiss() {
        if (mDelegate != null) {
            mDelegate.dismiss();
        } else {
            Log.e(Tag, "you must setDelegate before");
        }
    }

    //切换显示关闭菜单
    public void toggle() {
        if (mDelegate != null) {
            mDelegate.toggle();
        } else {
            Log.e(Tag, "you must setDelegate before");
        }
    }
由于这三个方法属于共同的操作,所以在抽象代理类中已经就又相应的实现,子类可以看情况重写:

//默认的显示方法,子类需要super调用该方法
    protected void show() {
        if (getStatus() != SweetSheet.Status.DISMISS) {
            return;
        }
        mBg.setClickable(mIsBgClickEnable);
        showShowdown();
    }

    /**
     * 显示模糊背景
     */
    protected void showShowdown() {
        ViewHelper.setTranslationY(mRootView, 0);
        //设置模糊效果
        mEffect.effect(mParentVG, mBg);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        //先添加一个iamgeview用作模糊背景
        mParentVG.addView(mBg, lp);
        //初始化模糊效果为透明
        ViewHelper.setAlpha(mBg, 0);
        //模糊背景从虚到实
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mBg, "alpha", 0, 1);
        objectAnimator.setDuration(400);
        objectAnimator.start();
    }

    /**
     * 消失
     */
    protected void dismiss() {
        if (getStatus() == SweetSheet.Status.DISMISS) {
            return;
        }
        mBg.setClickable(false);
        // 隐藏模糊背景, 并在结束的时候移除
        dismissShowdown();
        //整个布局的向下移动动画
        ObjectAnimator translationOut = ObjectAnimator.ofFloat(mRootView,
                "translationY", 0, mRootView.getHeight());
        translationOut.setDuration(600);
        translationOut.setInterpolator(new DecelerateInterpolator());
        translationOut.addListener(new SimpleAnimationListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                mStatus = SweetSheet.Status.DISMISSING;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //设置状态为dismiss,并移除view
                mStatus = SweetSheet.Status.DISMISS;
                mParentVG.removeView(mRootView);
            }
        });
        translationOut.start();
    }

    /**
     * 隐藏模糊背景, 并在结束的时候移除
     */
    protected void dismissShowdown() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mBg, "alpha", 1, 0);
        objectAnimator.setDuration(400);
        objectAnimator.start();
        objectAnimator.addListener(new SimpleAnimationListener() {

            @Override
            public void onAnimationEnd(Animator animation) {
                mParentVG.removeView(mBg);
            }


        });
    }

    //切换显示关闭菜单
    protected void toggle() {
        switch (mStatus) {
            case SHOW:
                dismiss();
                break;
            case DISMISS:
                show();
                break;
            default:
                break;
        }
    }
从以上代码可以看出,show的时候先是加入一个背景的view,该view可以设置背景模糊效果,dim效果等等,然后先设置背景为透明,然后再给他一个alpha属性从0到1的动画效果,然后在列表代理类RecyclerViewDelegate中重新实现show方法,这里在加入背景view后,紧接着把内容的view加入到父布局中:

 @Override
    protected void show() {
        super.show();
        ViewGroup.LayoutParams lp =
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mParentVG.addView(mRootView, lp);
        mSweetView.show();
    }
然后就是调用SweetView类的show方法,弹出动画的弹性效果就是该方法中实现的:

 public void show() {
        mStatus = Status.STATUS_SMOOTH_UP;
        if (mAnimationListener != null) {
            mAnimationListener.onStart();
            this.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mAnimationListener.onContentShow();
                }
            }, 600);
        }

        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, mMaxArcHeight);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mArcHeight = value;

                if (value == mMaxArcHeight) {
                    duang();
                }
                invalidate();
            }
        });
        valueAnimator.setDuration(800);
        valueAnimator.setInterpolator(new AccelerateInterpolator());
        valueAnimator.start();
    }
1.这里首相调用了mAnimationListener.onStart();方法,这里主要是设置列表和手势拖动view的隐藏:

@Override
        public void onStart() {
            mFreeGrowUpParentRelativeLayout.reset();
            mStatus = SweetSheet.Status.SHOWING;
            sliderIm.setVisibility(View.INVISIBLE);
            mRV.setVisibility(View.GONE);
        }
2.延迟600毫秒后就开始调用mAnimationListener.onContentShow();主要是将列表控制设置显示并设置适配器,并执行动画。实现如下:

 @Override
        public void onContentShow() {
            mRV.setVisibility(View.VISIBLE);
            mRV.setAdapter(mMenuRVAdapter);
            mRV.scheduleLayoutAnimation();
        }
然后在RecyclerView的布局动画监听器中有做如下的操作,设置mFreeGrowUpParentRelativeLayout.setClipChildren(false);是让动画的时候绘制可以超出view的范围,就是弹性效果:

 mRV.setLayoutAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                mRV.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        return true;
                    }
                });
                mFreeGrowUpParentRelativeLayout.setClipChildren(false);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mRV.setOnTouchListener(null);

                mFreeGrowUpParentRelativeLayout.setClipChildren(true);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
3.接着就是设置属性动画,并开启:

 ValueAnimator valueAnimator = ValueAnimator.ofInt(0, mMaxArcHeight);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mArcHeight = value;

                if (value == mMaxArcHeight) {
                    duang();
                }
                invalidate();
            }
        });
        valueAnimator.setDuration(800);
        valueAnimator.setInterpolator(new AccelerateInterpolator());
        valueAnimator.start();
这里只是设置了一个属性值的计算,通过invalidate();来最终调用drawBG方法,drawBG方法就是通过Path的api来画弧线,最后到达顶部mMaxArcHeight的高度的时候是一个圆环状:

private void drawBG(Canvas canvas) {
        mPath.reset();
        int currentPointY = 0;
        switch (mStatus) {
            case NONE:
                currentPointY = mMaxArcHeight;
                break;
            case STATUS_SMOOTH_UP:
            case STATUS_UP:
                currentPointY = getHeight() - (int) ((getHeight() - mMaxArcHeight) * Math.min(1, (mArcHeight - mMaxArcHeight / 4) * 2.0 / mMaxArcHeight * 1.3));
                break;
            case STATUS_DOWN:
                currentPointY = mMaxArcHeight;
                break;
        }

        mPath.moveTo(0, currentPointY);
        mPath.quadTo(getWidth() / 2, currentPointY - mArcHeight, getWidth(), currentPointY);
        mPath.lineTo(getWidth(), getHeight());
        mPath.lineTo(0, getHeight());
        mPath.lineTo(0, currentPointY);
        canvas.drawPath(mPath, mPaint);
    }
效果:

然后在到达最大属性值的时候,又会开启另外一个属性值的计算,这里间插值设置了OvershootInterpolator,这个是api22中的新类,是一个超出一点然后再回来的间插值,这个无关紧要:

public void duang() {
        mStatus = Status.STATUS_DOWN;
        ValueAnimator valueAnimator = ValueAnimator.ofInt(mMaxArcHeight, 0);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mArcHeight = (int) animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.addListener(new SimpleAnimationListener() {


            @Override
            public void onAnimationEnd(Animator animation) {
                if (mAnimationListener != null) {
                    mAnimationListener.onEnd();
                }
            }

        });
        valueAnimator.setDuration(500);
        valueAnimator.setInterpolator(new OvershootInterpolator(4f));
        valueAnimator.start();

    }
然后在回弹效果结束钱调用了mAnimationListener.onEnd();方法,这个方法的实现主要是判断可拖动控件是否显示:

@Override
        public void onEnd() {
            if (mIsDragEnable) {
                sliderIm.setVisibility(View.VISIBLE);
                sliderIm.circularReveal(sliderIm.getWidth() / 2, sliderIm.getHeight() / 2, 0, sliderIm.getWidth());
            }
            mStatus = SweetSheet.Status.SHOW;
        }
这里主要分析了使用列表的情况,另外的模式就不再累赘描述了。






















评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值