Android 抽屉用法

前言

如今安卓开发用抽屉的APP基本上很少见了,并不常用,无聊突然翻到以前自己写的自定义抽屉,特此来跟大家详解一下


介绍

实现原理:自定义继承自分层布局,使用事件分发,根据手指滑动的方向和距离进行判断抽屉打开的方向和位置


使用

1. 系统提供的抽屉框架包的使用

public class MainActivity extends AppCompatActivity implements View.OnDragListener, DrawerLayout.DrawerListener, View.OnClickListener {
    private DrawerLayout activity_main;//GOOGLE 框架包
    private RelativeLayout rl_left;//  左边抽屉
    private ListView lv_left;// ListView
    private List<String> list = new ArrayList<>();
    private Button button;//开启左边抽屉


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initList();
        initView();
    }

    private void initList() {
        for (int i = 0; i < 30; i++) {
            list.add("sasf" + i);
        }
    }

    private void initView() {
        activity_main = (DrawerLayout) findViewById(R.id.activity_main);
        rl_left = (RelativeLayout) findViewById(R.id.rl_left);
        lv_left = (ListView) findViewById(R.id.lv_left);
        button = (Button) findViewById(R.id.button);


        lv_left.setAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, list));//给左边菜单写入数据

        activity_main.setScrimColor(Color.TRANSPARENT);

        activity_main.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);

        activity_main.setDrawerListener(this);
        button.setOnClickListener(this);

    }
    //当抽屉正在滑动的时候
    @Override
    public void onDrawerSlide(View drawerView, float slideOffset) {
        //获取第一个主页面的布局
        View mContext = activity_main.getChildAt(0);
        //获取滑动的抽屉的布局
        View draw = drawerView;
        float scale = 1 - slideOffset;
        // 意思就是  如果不滑动  scale 是1 , 没有发生变化,滑动后 会缩小
        float suofang = 0.8f + scale * 0.2f;
        //测量 抽屉的宽度
        int drawWidth = draw.getMeasuredWidth();
        //主页区域
        ViewHelper.setScaleX(mContext, suofang);
        ViewHelper.setScaleY(mContext, suofang);
        //抽屉区域
        ViewHelper.setScaleX(draw, 0.7f + slideOffset * 0.3f);
        ViewHelper.setScaleY(draw, 0.7f + slideOffset * 0.3f);
        ViewHelper.setAlpha(draw, 0.6f + slideOffset * 0.4f);




        //如果两个抽屉的话 可以对每一个抽屉  设置一个TAG  然后判断
        ViewHelper.setTranslationX(mContext, drawWidth * slideOffset);


    }


    @Override
    public void onDrawerOpened(View drawerView) {

    }
    //当抽屉关闭的时候会调用

    @Override
    public void onDrawerClosed(View drawerView) {
        activity_main.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);

    }

    @Override
    public void onDrawerStateChanged(int newState) {

    }

    @Override
    public boolean onDrag(View v, DragEvent event) {
        return false;
    }

    @Override
    public void onClick(View v) {
        activity_main.openDrawer(Gravity.LEFT);
        activity_main.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT);

    }
}

2. 自定义 继承 FrameLayout 布局

public class SildingView extends FrameLayout {
    private boolean IsFirst=true;//第一次进入的标志
    private boolean IsSping=false;//抽屉菜单打开关闭的标志----》默认情况下关闭的
    private PointF pf=new PointF();//记录坐标
    private PointF pf1=new PointF();
    private LinearLayout mBootomlinear;//底层的布局
    private LinearLayout mToplinear;//顶层的布局
    private int maxWidth=0;//抽屉打开的最大宽度

    public SildingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }
    private void initView() {
        //实列化顶部底部布局
        mBootomlinear=new LinearLayout(getContext());
        mToplinear=new LinearLayout(getContext());
        //设置线性方向-垂直
        mBootomlinear.setOrientation(LinearLayout.VERTICAL);
        mToplinear.setOrientation(LinearLayout.VERTICAL);
        //方便查看设置背景颜色
        mBootomlinear.setBackgroundColor(Color.BLACK);
        mToplinear.setBackgroundColor(Color.WHITE);
    }
    //重写onmesure方法,获取底部布局的最大宽度
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取view的宽度----》设置底部的宽度
        if (IsFirst) {
            maxWidth=(int) (getMeasuredWidth()*0.7);
            mBootomlinear.setLayoutParams(new FrameLayout.LayoutParams(maxWidth, FrameLayout.LayoutParams.MATCH_PARENT));
            mToplinear.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
            //将布局添加到自定义里面
            addView(mBootomlinear);//先添加哪个就在最底层
            addView(mToplinear);
        }
        IsFirst=false;
    }
    //设置底部linear布局的方法
    public void setBootom(View v){
        //给要再bootom里面添加的布局设置其在父容器所占位置的宽高属性
        v.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
        //将布局添加到底部linear中
        mBootomlinear.addView(v);
    }
    public void setTop(View v){
        v.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
        mToplinear.addView(v);
    }
    //底层事件的处理:return :1.true :自己处理了,不往下发 2.return super.dispatchtouchenvent(ev),交给系统自己处理-----》往下发
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction()==MotionEvent.ACTION_DOWN) {
            //记录手指的坐标
            pf.x=ev.getX();
            pf.y=ev.getY();
            pf1.x=ev.getX();
            pf1.y=ev.getY();
        }else if (ev.getAction()==MotionEvent.ACTION_MOVE) {
            //获取当前手指滑动后的坐标
            int x=(int) ev.getX();
            int y=(int) ev.getY();
            //计算手指滑动后的坐标距离: disX  disY
            int disX=(int) (x-pf.x);//x轴移动的距离
            int disY=(int) (y-pf.y);//y轴移动的距离
            //根据正余玹定理来判断, 水平滑动或者是垂直滑动
            if (Math.abs(disX)/2-Math.abs(disY)>0) {
                //不做处理
            }else {//垂直状态--》抽屉应该关闭
                //TODO 通过抽屉的开关来判断上层可否移动
                if (!IsSping) {
                    return super.dispatchTouchEvent(ev);
                }else {
                    return true;
                }
            }
            //设置一个边界值:防止手指按下出现抽屉抖动的情况
            if (Math.abs(disX)<10) {
                return super.dispatchTouchEvent(ev);
            }
            //根据手指滑动的x轴移动的距离的正负,判断抽屉打开的方向
            if (disX>0) {//
                FrameLayout.LayoutParams lp=(LayoutParams) mToplinear.getLayoutParams();
                //判断左边距超过最大边距。将最大边距设置给滑动 距离
                if (lp.leftMargin>=maxWidth) {
                    disX=maxWidth;
                    IsSping=true;//抽屉开启
                }
                lp.leftMargin=disX;//将移动的距离设置给左边距
                lp.rightMargin=-disX;
                mToplinear.setLayoutParams(lp);//将属性设置给顶部布局
            }else if (disX<0) {
                //获取到顶部布局的属性lp
                FrameLayout.LayoutParams lp=(LayoutParams) mToplinear.getLayoutParams();
                if (lp.leftMargin<=0) {
                    disX=0;
                    IsSping=false;
                }
                lp.leftMargin=lp.leftMargin-Math.abs(disX);
                lp.rightMargin=-lp.leftMargin;
                mToplinear.setLayoutParams(lp);
                pf.x=x;//将移动后的坐标赋值给初始坐标,解决再次移动的问题
            }
            requestLayout();//刷新界面
            return true;
        }else if (ev.getAction()==MotionEvent.ACTION_UP) {
            //区分是点击还是滑动
            int disX=(int) Math.abs(ev.getX()-pf1.x);
            if (disX>10) {
                //以底部linear的宽度的一半为分割线,超过分割线,手指抬起,手指抬起,抽屉自动打开或关闭
                FrameLayout.LayoutParams lp=(LayoutParams) mToplinear.getLayoutParams();
                if (lp.leftMargin>maxWidth/2) {//抽屉自动打开
                    lp.leftMargin=maxWidth;
                    lp.rightMargin=-maxWidth;
                    IsSping=true;
                }else {
                    //抽屉关闭
                    lp.leftMargin=0;
                    lp.rightMargin=0;
                    IsSping=false;
                }
                mToplinear.setLayoutParams(lp);
                requestLayout();
                return true;//自己处理
            }
        }
        return super.dispatchTouchEvent(ev);
    }
}

3. 系统提供的横向 滑动的 布局 HorizontalScrollView

public class MySlidingMenu extends HorizontalScrollView {
    // 屏幕宽度 单位:px
    private int ScreenWidth;
    // 内容区域宽度
    private int contentWidth;
    // 菜单宽度
    private int menuWidth;
    // 菜单一半的宽度
    private int halfmenuWidth;
    // 菜单的右边距
    private int rightPadding;

    private boolean isMesure;
    // 是否打开菜单
    private boolean isOpen;

public MySlidingMenu(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        //用来添加view
        WindowManager wm = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);

        //获取手机分辨率
        DisplayMetrics outMetrics = new DisplayMetrics();
        //获取屏幕的大小
        wm.getDefaultDisplay().getMetrics(outMetrics);

        //widthPixels是宽度方向上的像素点的个数。
        ScreenWidth = outMetrics.widthPixels;

        //自定义属性
        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.MySlidingMenu, defStyle, 0);
        int count =ta.getIndexCount();
        for(int i=0;i<count;i++){

            int attr=ta.getIndex(i);
            Log.e("attr", " "+attr);
            switch(attr){
            case R.styleable.MySlidingMenu_rightPadding:
                //把dp转化为px
                rightPadding=ta.getDimensionPixelOffset(attr,
                        (int) TypedValue.applyDimension( 
                                TypedValue.COMPLEX_UNIT_DIP, 200,
                                getResources().getDisplayMetrics()));
                break;

            }
        }
    }

    public MySlidingMenu(Context context) {
        this(context, null, 0);
    }



    public MySlidingMenu(Context context, AttributeSet attrs) {
        this(context, attrs,0);

//      WindowManager wm = (WindowManager) context
//              .getSystemService(Context.WINDOW_SERVICE);
//
//      DisplayMetrics outMetrics = new DisplayMetrics();
//
//      wm.getDefaultDisplay().getMetrics(outMetrics);
//      ScreenWidth = outMetrics.widthPixels;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!isMesure) {
            // 避免 Measure方法多次调用
            isMesure = true;

            LinearLayout wrapper = (LinearLayout) getChildAt(0);
            ViewGroup menu = (ViewGroup) wrapper.getChildAt(0);
            ViewGroup content = (ViewGroup) wrapper.getChildAt(1);
//          rightPadding = (int) TypedValue.applyDimension(
//                  TypedValue.COMPLEX_UNIT_DIP, 100, content.getResources()
//                          .getDisplayMetrics());

            //屏幕宽度-菜单的右边距 = 菜单的宽度
            menuWidth = ScreenWidth - rightPadding;

            halfmenuWidth = menuWidth / 2;
            //获取菜单的布局属性获得菜单的宽度

            menu.getLayoutParams().width = menuWidth;
            //主布局的宽度 = 屏幕的宽度
            content.getLayoutParams().width = ScreenWidth;

        }
    } 

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);

        if (changed) {
            //(让ScrollView滚动到(menuWidth, 0)的位置,也就是刚好显示主视图)不带效果的滑动
            this.scrollTo(menuWidth, 0);
        }
    }
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
        case MotionEvent.ACTION_UP:
            //就是当前view的左上角相对于母视图的左上角的X轴偏移量。
            int scrollX = getScrollX();
            if (scrollX > halfmenuWidth) {
                this.smoothScrollTo(menuWidth, 0);
            } else {
                this.smoothScrollTo(0, 0);
            }

            return true;
        }
        return super.onTouchEvent(ev);
    }
    public void openDraw() {
        if (isOpen) {
            return;
        }
        //让ScrollView滚动到(0,0)的位置
        this.smoothScrollTo(0,0);
        isOpen=true;
    }
    public void close() {
        if (isOpen) {
            //让ScrollView滚动到(menuWidth,0)的位置
            //带效果的滑动
            this.smoothScrollTo(menuWidth,0);
            isOpen=false;
        }
    }
    /*
    可以设置一个按钮控制抽屉的开关
*/
    public void toggle(){
        if(isOpen){
            close();
        }else{
            openDraw();
        }
    }
}

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr
        name="rightPadding" format="dimension"
    />
    <declare-styleable name="MySlidingMenu">
        <attr name="rightPadding" />
    </declare-styleable>

</resources>

好了,了解一下,现在抽屉已经使用不多了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值