Android底部/顶部滑动菜单SldMenu

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xyzdwf/article/details/54289316

原本写在工具类里的滑动菜单连返回按钮都不好处理,就弄成了一个类,增加了状态改变的回调接口。

添加了布局展开监听,在布局展开后获取尺寸,再调用show(),防止定位不准。

create()方法添加了parentView参数,可以指定父容器(为null时为Activity根容器),以便于和其他组件保持层级关系。

public class SldMenu {
    public static final int SHOW_TYPE_BOTTOM=0;//底部显示
    public static final int SHOW_TYPE_TOP=1;//顶部显示

    public static final int MENU_STATE_GONE=0;//关闭
    public static final int MENU_STATE_SHOW=1;//显示
    public static final int MENU_STATE_SHOW_START=2;//显示开始
    public static final int MENU_STATE_GONE_START=3;//关闭开始

    private Context context;
    private OnStateChangeListener stateCallBack;
    //View
    private ViewGroup parentView;//父容器
    private View contentView;//内容
    private View hdVw;//滑动容器
    private View sldVw;//滑动体
    //Params
    private int sldVwHt;//滑动体高度
    private int curState=MENU_STATE_GONE;//菜单状态
    private int showTp;//显示类型
    private int sHeight;//屏幕高度
    private int[] hdLyloc = new int[2];//容器位置0:x

    private boolean hasParent;//提供了父容器
    private boolean aniFlag;//动画进行时标志
    private boolean measureFlag;//测量进行时标志
    private boolean showFlag;//show()已调用标志



    private SldMenu(){}
    /**
     *打开底部/顶部滑动菜单
     * @param context 上下文 Activity
     * @param contentView 菜单内容
     * @param parentView 父容器,为null时将创建在根View中
     * @return 返回值为SldMenu
     */

    public static SldMenu create(Context context, View contentView,ViewGroup parentView) {
        if (context == null || contentView == null) return null;
        final SldMenu thiz=new SldMenu();
        thiz.context=context;
        thiz.contentView=contentView;
        if(parentView!=null)thiz.hasParent=true;
        thiz.sHeight= AndUtil.getScreenHeight(context);
        VerticalHolderView hdVw = new VerticalHolderView(context);
        hdVw.setVerticalScrollBarEnabled(false);
        FrameLayout.LayoutParams reLyPrms1 = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        hdVw.setLayoutParams(reLyPrms1);
        FrameLayout frmLy = new FrameLayout(context);
        FrameLayout.LayoutParams frmLyPrms = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        frmLyPrms.topMargin=thiz.sHeight;
        frmLy.setLayoutParams(frmLyPrms);
        frmLy.addView(contentView);
        hdVw.addView(frmLy);
        thiz.hdVw=hdVw;
        thiz.sldVw=frmLy;
        if(!thiz.hasParent) {
            Activity act = (Activity) context;
            ViewGroup vg = (ViewGroup) act.getWindow().getDecorView();
            thiz.parentView=vg;
        }else{
            thiz.parentView=parentView;
            parentView.setVisibility(View.VISIBLE);
        }

        thiz.measureFlag=true;//开始测量
        ViewTreeObserver vto = thiz.parentView.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {//添加布局展开监听
            public void onGlobalLayout() {
                thiz.measureFlag=false;
                thiz.sldVwHt= thiz.sldVw.getMeasuredHeight();
                if(0==thiz.sldVwHt) {//parentView也未展开
                    thiz.sldVwHt= AndUtil.getViewHeight(thiz.sldVw);
                }
                thiz.parentView.getLocationOnScreen(thiz.hdLyloc);
                thiz.parentView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                if(thiz.showFlag){
                    thiz.show(thiz.showTp);
                }
            }
        });
        thiz.parentView.addView(hdVw);
        return thiz;
    }

    /**
     * 展示Menu
     * @param showTp 0:底部;其他:顶部
     */
    public void show(int showTp) {
        showFlag=true;
        this.showTp=showTp;
        if(measureFlag)return;
        hdVw.setOnTouchListener(new MyOnTouchLsn());
        int hdoff=hdLyloc[1];
        if(showTp==SHOW_TYPE_BOTTOM) {
            doBtmMenuAnimation(sHeight, sHeight - hdoff - sldVwHt, 300, true);
        }else{
            doBtmMenuAnimation(-sldVwHt,0, 300, true);
        }
    }

    /**
     *关闭底部/顶部滑动菜单
     */
    public void cancle() {
        showFlag=false;
        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) sldVw.getLayoutParams();
        int k=params.topMargin;
        if(showTp==SHOW_TYPE_BOTTOM){
            doBtmMenuAnimation(k,sHeight, 300, false);
        }else{
            doBtmMenuAnimation(k,-AndUtil.getViewHeight(sldVw), 300, false);
        }
    }
    /**
     * 打开关闭底部/顶部滑动菜单动画
     * @param begin 位置开始
     * @param end 位置结束
     * @param duration 持续时间
     * @param b false:动画结束移除View
     */
    private void doBtmMenuAnimation(final int begin, final int end, int duration, final boolean b) {
        final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) sldVw.getLayoutParams();
        ValueAnimator mAnimator = ValueAnimator.ofInt(begin, end);
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int aniVal = (int) animation.getAnimatedValue();
                params.topMargin = aniVal;
                sldVw.setLayoutParams(params);
            }
        });
        mAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                aniFlag=true;
                if(!b&&curState==MENU_STATE_SHOW){
                    curState=MENU_STATE_GONE_START;
                    if(stateCallBack!=null){
                        stateCallBack.onStateChange(curState);
                    }
                }else if(b&&curState==MENU_STATE_GONE){
                    curState=MENU_STATE_SHOW_START;
                    if(stateCallBack!=null){
                        stateCallBack.onStateChange(curState);
                    }
                }
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                params.topMargin = end;
                sldVw.setLayoutParams(params);
                if(!b&&curState==MENU_STATE_GONE_START){
                    removeBtmMenuVw();
                    curState=MENU_STATE_GONE;
                    if(stateCallBack!=null){
                        stateCallBack.onStateChange(curState);
                    }
                }else if(b&&curState==MENU_STATE_SHOW_START){
                    curState=MENU_STATE_SHOW;
                    if(stateCallBack!=null){
                        stateCallBack.onStateChange(curState);
                    }
                }
                aniFlag=false;
            }

            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
        mAnimator.setInterpolator(new DecelerateInterpolator());
        mAnimator.setDuration(duration);
        mAnimator.start();
    }
    /**
     *移除底部/顶部滑动菜单View
     */
    private void removeBtmMenuVw(){
        if(hasParent){
            if(1==parentView.getChildCount()) {
                parentView.setVisibility(View.GONE);
            }
        }
        parentView.removeView(hdVw);
    }

    /**
     * 打开或关闭状态改变回调借口
     */
    public interface OnStateChangeListener
    {
        void onStateChange(int state);
    }

    private class MyOnTouchLsn  implements View.OnTouchListener{
        private Context contexta=context;
        private float ny, ny1, ry; //y坐标历史和最新y坐标
        private int magtop; //margin top
        private boolean mvf = false;//move flag 移动标志
        private  FrameLayout.LayoutParams params;
        private int reLHt;//滑动部分高度
        private int hdoff;//容器偏移
        private boolean myFlg = false;

        public MyOnTouchLsn() {
            this.reLHt=AndUtil.getViewHeight(sldVw);
            this.hdoff=hdLyloc[1];
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (aniFlag){
                myFlg = true;
                return myFlg;
            }
            if(myFlg){//拦截重复触发
                if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL){
                    myFlg = false;
                    //Log.w("SldMenu¥MyOnTouchLsn", "nomeaning touch stoped!");
                }
                return true;
            }
            ry = (int) event.getRawY();
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                mvf = false;
                if(showTp==0){
                    magtop = sHeight-hdoff-reLHt;
                }else{
                    magtop = 0;
                }
                params = (FrameLayout.LayoutParams) sldVw.getLayoutParams();
            } else if (event.getAction() == MotionEvent.ACTION_UP||event.getAction() == MotionEvent.ACTION_CANCEL) {
                if (mvf) {
                    boolean b = false;
                    if (ny > ny1) {
                        b = true;
                    }
                    int endp;
                    if(showTp==0) {
                        endp=b?sHeight:sHeight - hdoff - reLHt;
                        b=!b;
                    }else{
                        endp=b?0:-reLHt;
                    }
                    doBtmMenuAnimation(magtop,endp, 300, b);
                } else {//这里处理类似单击事件
                    int lnup=magtop+hdoff;
                    int lndn=lnup+reLHt;
                    if(ry>lndn||ry<lnup){
                        cancle();
                    }
                }
            } else if (event.getAction() == MotionEvent.ACTION_MOVE && Math.abs(ry - ny) >= 5) {
                mvf = true;
                magtop += (int) (ry - ny);
                if(showTp==0){
                    int mxmg=sHeight-hdoff-reLHt;
                    if(magtop<=mxmg){
                        magtop=mxmg;
                    }
                }else{
                    if(magtop>=0){
                        magtop=0;
                    }
                }
                if (params != null) {
                    params.topMargin = magtop;
                    sldVw.setLayoutParams(params);
                }
            }
            ny1 = ny;
            ny = ry;
            return true;
        }
    }

    public SldMenu setOnStateChangeListener(OnStateChangeListener stateCallBack) {
        this.stateCallBack = stateCallBack;
        return this;
    }

    public View getContentView() {
        return contentView;
    }

}


相关的类,滑动容器,其实就是ScrollView取消了Touch事件:

public class VerticalHolderView extends ScrollView {
    public VerticalHolderView(Context context) {
        super(context);
    }

    public VerticalHolderView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VerticalHolderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return false;
    }
}

工具类中的方法:

public class AndUtil {
    public static int dp2Px(Context context, float dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dp, context.getResources().getDisplayMetrics());
    }

    public static float px2Dp(Context context, float px) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (px / scale);
    }

    public static int sp2px(Context context, float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, context.getResources().getDisplayMetrics());
    }

    public static float px2sp(Context context, float pxVal) {
        return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
    }

    /**
     * 获得屏幕宽度
     *
     * @param context
     * @return
     */
    public static int getScreenWidth(Context context) {
        WindowManager wm = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        return outMetrics.widthPixels;
    }

    /**
     * 获得高度
     *
     * @param context
     * @return
     */
    public static int getScreenHeight(Context context) {
        WindowManager wm = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        return outMetrics.heightPixels;
    }

    public static int getViewWidth(View view) {
        int width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int height = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(width, height);
        int w = view.getMeasuredWidth();
        return w;
    }

    public static int getViewHeight(View view) {
        int width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int height = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(width, height);
        int h = view.getMeasuredHeight();
        return h;
    }

最后测试下,show之前添加监听:

        tmpSldMenu = SldMenu.create(this, hdvw,secondHolderLayer);
        tmpSldMenu.setOnStateChangeListener(new SldMenu.OnStateChangeListener() {
            @Override
            public void onStateChange(int state) {
				Log.i(TAG,"--->"+state);
 
            }
        });
        tmpSldMenu.show(0);


猜你喜欢

转载自blog.csdn.net/xyzdwf/article/details/54289316