Android 右滑抽屉菜单

概述

本篇只是个示例,理解本篇博客后,可实现左侧滑动菜单、左右两侧滑动菜单。再加上各种缩放,平移特效。DuangDuang的。本篇效果如下:

实现步骤

  1. 因为需要水平滑动,所以继承HorizontalScrollView
  2. 本Domo分为两个部分mMainLayout和mRightLayout。在onMeasure初始化这两部分的宽度
  3. 在onTouchEvent中判断是否完全展示,拦截当前触摸事件
  4. 前三步已经实现最简单的滑动布局,最关键的是第四步。mMainLayout跟随手势不断滑动,实现抽屉菜单。
  5. 自己再加各种特效。

开启侧滑之旅

package com.example.chouti;

import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

public class SlidingRightView extends HorizontalScrollView {
    /** * 主布局界面 */
    private ViewGroup mMainLayout;
    /** * 侧滑界面 */
    private ViewGroup mRightLayout;
    /** * 侧滑界面宽度 */
    private int mRightLayoutWidth;
    /** * 侧滑界面距离屏幕左边的距离 */
    private int mRightLayoutMarginLeft = 200; //px
    /** * 是否展示侧滑 */
    private boolean isOpen;

    private int mScreenWidth;
    private int mScreenHeight;

    private Context mContext;


    public SlidingRightView(Context context) {
        this(context, null);
    }

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

    public SlidingRightView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView(context);
    }

    private void initView(Context context) {
        mContext = context;
        getScreenWidthAndHeight();
    }

    private void getScreenWidthAndHeight() {
        WindowManager wm = (WindowManager) mContext.getSystemService(mContext.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        mScreenWidth = outMetrics.widthPixels;
        mScreenHeight = outMetrics.heightPixels;
        Log.i("TAG", "mScreenWidth=" + mScreenWidth + "mScreenHeight=" + mScreenHeight);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        LinearLayout v = (LinearLayout) this.getChildAt(0);
        mMainLayout = (ViewGroup) v.getChildAt(0);
        mRightLayout = (ViewGroup) v.getChildAt(1);

        mMainLayout.getLayoutParams().width = mScreenWidth;
        mRightLayout.getLayoutParams().width = mRightLayoutWidth = mScreenWidth - mRightLayoutMarginLeft;
        Log.i("TAG", " mRightLayout.getLayoutParams().width=" + mRightLayout.getLayoutParams().width);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            this.scrollTo(0, 0);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_UP:
                // scrollX为水平滚动条滚动的宽度
                int scrollX = getScrollX();
                if (scrollX >= mRightLayoutWidth / 2) {
                    this.smoothScrollTo(mRightLayoutWidth, 0);
                    isOpen = true;
                } else {
                    this.smoothScrollTo(0, 0);
                    isOpen = false;
                }
                // 拦截事件
                return true;
        }
        return super.onTouchEvent(ev);
    }

    /** * 抽屉开关 */
    public void toggleRightLayout() {
        if (isOpen) {
            closeRightLayout();
            isOpen = false;
        } else {
            openRightLayout();
            isOpen = true;
        }
    }

    /** * 打开抽屉 */
    public void openRightLayout() {
        if (isOpen) {
            return;
        }
        this.smoothScrollTo(mRightLayoutWidth, 0);
        isOpen = true;
    }

    /** * 关闭抽屉 */
    public void closeRightLayout() {
        if (!isOpen) {
            return;
        }
        this.smoothScrollTo(0, 0);
        isOpen = false;
    }


}

上文是前三步工程,完成上面三步之后我们已经有一个基础的滑动布局了。下面对上段代码进行解析。可以看到我们首先获取了屏幕的宽高。在onMeasure中我们为SlingRightView唯一childView的第一个childView和第二个childView宽赋值。在onLayout中默认不显示右侧布局。在onTouchEvent中判断滑动的宽度,如果大于右侧布局宽度的一半,则完全展示右侧布局。否则,不展示右侧布局。拦截当前事件。自定义View流程和拦截点击事件不太了解的同学可以看下我的另外两篇博客自定义View总结 Android 事件传递机制

关键的第四步

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        float scale = l * 1.0f / mRightLayoutWidth;// 0.0~1.0

        // 将mRightLayout平移l(第一个参数) 实现抽屉式侧滑菜单
        // 这是个相对运动的过程 一定要注意理解
        mMainLayout.setTranslationX(mRightLayoutWidth * scale);

    }

两行代码实现抽屉式菜单。 第一个参数l为HorizontalScrollView偏移的长度(其实和getScrollX等价) 初始化时没有滑动,此时l=0。这里使用了属性动画。因为是右边布局滑动,主布局需要跟随手势滑动,滑动距离等于右侧布局的宽度。主布局看起来就像是一直“固定”在页面一样,其实是动态的滑动。mMainLayout.setTranslationX(mRightLayoutWidth * scale);抽屉的精髓就是这行代码,一定要理解这个相对运动

布局文件示例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">

    <com.example.chouti.SlidingRightView  android:id="@+id/sliding" android:layout_width="wrap_content" android:layout_height="match_parent">

        <LinearLayout  android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal">

            <LinearLayout  android:id="@+id/mainLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ff0000" android:orientation="vertical">

                <Button  android:id="@+id/open" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="open" android:textAllCaps="false" />

                <Button  android:id="@+id/close" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="close" android:textAllCaps="false" />

                <Button  android:id="@+id/toggle" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="toggle" android:textAllCaps="false" />
            </LinearLayout>

            <LinearLayout  android:id="@+id/rightLayout" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="#00ff00" android:orientation="vertical">

                <Button  android:id="@+id/test" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAllCaps="false" android:text="test" />

            </LinearLayout>
        </LinearLayout>
    </com.example.chouti.SlidingRightView>
</LinearLayout>

MainActivity调用

package com.example.chouti;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Toast;

public class MainActivity extends Activity {

    private SlidingRightView slidingRightView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        slidingRightView = (SlidingRightView) findViewById(R.id.sliding);

        findViewById(R.id.open).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               slidingRightView.openRightLayout();
            }
        });

        findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                slidingRightView.closeRightLayout();
            }
        });

        findViewById(R.id.toggle).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                slidingRightView.toggleRightLayout();
            }
        });

        findViewById(R.id.test).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "click test", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

DuangDuang特效加起来

想要实现更酷炫的特效当然要用到动画。比如将抽屉进行缩放啦,透明度的变化了。具体看项目需求,下面是个简单的示例。

        // 为mRightLayout设置缩放和透明度的变化
        mRightLayout.setScaleX(mRightLayoutScale);
        mRightLayout.setScaleY(mRightLayoutScale);
        mRightLayout.setAlpha(mRightLayoutAlpha);

右滑抽屉式菜单到此已经完毕,感谢耐心读完。再次重申:这只是个Demo。现在假设需要左滑抽屉、左右滑抽屉、甚至更变态的各种滑,如果你有思路那么本篇博客的目的达到了。如果没有,建议重新阅读本篇博客。

你可能感兴趣的:(android,滑动菜单,抽屉,抽屉式菜单,右滑抽屉)