超简单侧滑(仿QQ)自定义View

原作者写的文章很详细,现在我只是一个卑微的代码搬运工

原文:Android自定义View之仿QQ侧滑菜单实现

自定义View SlidingMenu(解决与RecyclerView滑动冲突,但是效果好像有点不太灵敏的样子)

public class SlidingMenu extends HorizontalScrollView {
    //自定义View布局中内嵌的最外层的LinearLayout
    private LinearLayout mWapper;
    //菜单布局
    private ViewGroup mMenu;
    //内容布局
    private ViewGroup mContent;
    //屏幕宽度
    private int mScreenWidth;
    //菜单距屏幕右侧的距离,单位dp
    private int mMenuRightPadding = 0;
    //菜单的宽度
    private int mMenuWidth;
    //定义标志,保证onMeasure只执行一次
    private boolean once = false;
    //菜单是否是打开状态
    private boolean isOpen = false;
    //是否是抽屉式
    private boolean isDrawerType = false;
    
	private float startPos[]=new float[2];

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        int action=ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:

                //记录touch事件开始的坐标
                startPos[0]=ev.getX();
                startPos[1]=ev.getY();

                break;
            case MotionEvent.ACTION_MOVE:

                //捕获移动事件
                float x=ev.getX();
                float y=ev.getY();
                //计算和初始坐标的绝对值  如果左右移动距离 大于上下移动距离  拦截这次的touch事件  反之下发给子view处理
                if(Math.abs(x-startPos[0])>Math.abs(y-startPos[1])){
                    return true;
                }else{
                    return false;
                }
        }
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 在代码中使用new时会调用此方法
     *
     * @param context
     */
    public SlidingMenu(Context context) {
        this(context, null);
    }

    /**
     * 未使用自定义属性时默认调用
     *
     * @param context
     * @param attrs
     */
    public SlidingMenu(Context context, AttributeSet attrs) {
        //调用三个参数的构造方法
        this(context, attrs, 0);

    }

    /**
     * 当使用了自定义属性时会调用此方法
     *
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //获取我们自定义的属性
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.SlidingMenu, defStyleAttr, 0);
        int n = typedArray.getIndexCount();
        //遍历每一个属性
        for (int i = 0; i < n; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                //对我们自定义属性的值进行读取
                case R.styleable.SlidingMenu_rightPadding:
                    //如果在应用样式时没有赋值则使用默认值50,如果有值则直接读取
                    mMenuRightPadding = typedArray.getDimensionPixelSize(attr,
                            (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mMenuRightPadding, context.getResources().getDisplayMetrics()));
                    break;
                case R.styleable.SlidingMenu_drawerType:
                    isDrawerType = typedArray.getBoolean(attr, false);
                    break;
                default:
                    break;
            }
        }
        //释放
        typedArray.recycle();

        //通过以下步骤拿到屏幕宽度的像素值
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        mScreenWidth = displayMetrics.widthPixels;
    }

    /**
     * 设置子View的宽和高
     * 设置自身的宽和高
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!once) {
            once = true;
            mWapper = (LinearLayout) getChildAt(0);
            mMenu = (ViewGroup) mWapper.getChildAt(0);
            mContent = (ViewGroup) mWapper.getChildAt(1);
            //菜单和内容区域的高度都可以保持默认match_parent
            //菜单宽度 = 屏幕宽度 - 菜单距屏幕右侧的间距
            mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding;
            mContent.getLayoutParams().width = mScreenWidth;
            //当设置了其中的菜单的宽高和内容区域的宽高之后,最外层的LinearLayout的mWapper就自动设置好了
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 通过设置偏移量将Menu隐藏
     *
     * @param changed
     * @param l
     * @param t
     * @param r
     * @param b
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        super.onLayout(changed, l, t, r, b);
        if (changed) {
            //布局发生变化时调用(水平滚动条向右移动menu的宽度,则正好将menu隐藏)
            this.scrollTo(mMenuWidth, 0);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        //按下和移动使用HorizontalScrollView的默认处理
        switch (action) {
            case MotionEvent.ACTION_UP:
                //隐藏在左边的位置
                int scrollX = getScrollX();
                if (scrollX > mMenuWidth / 2) {
                    //隐藏的部分较大, 平滑滚动不显示菜单
                    this.smoothScrollTo(mMenuWidth, 0);
                    isOpen = false;
                } else {
                    //完全显示菜单
                    this.smoothScrollTo(0, 0);
                    isOpen = true;
                }
                return true;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 打开菜单
     */
    public void openMenu() {
        if (!isOpen) {
            this.smoothScrollTo(0, 0);
            isOpen = true;
        }
    }

    /**
     * 关闭菜单
     */
    public void closeMenu() {
        if (isOpen) {
            this.smoothScrollTo(mMenuWidth, 0);
            isOpen = false;
        }
    }

    /**
     * 切换菜单
     */
    public void toggleMenu() {
        if (isOpen) {
            closeMenu();
        } else {
            openMenu();
        }
    }

    /**
     * 滚动发生时调用
     *
     * @param l    getScrollX()
     * @param t
     * @param oldl
     * @param oldt
     */
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (isDrawerType) {
            float scale = l * 1.0f / mMenuWidth;  //1 ~ 0
            //调用属性动画,设TranslationX
            mMenu.setTranslationX(mMenuWidth * scale);
        }
    }
}

res/values 目录下新建attr.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="rightPadding" format="dimension"/>
    <attr name="drawerType" format="boolean"/>
    <declare-styleable name="SlidingMenu">
        <attr name="rightPadding"/>
        <attr name="drawerType"/>
    </declare-styleable>
</resources>

布局中应用

<?xml version="1.0" encoding="utf-8"?>
<com.zbzl.ui.teacher.constant.SlidingMenu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:id="@+id/sliding_menu"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <!--引入菜单布局-->
        <include layout="@layout/teacher_mine"/>
        <!--内容布局-->
        <include layout="@layout/teacher_home"/>
    </LinearLayout>

</com.zbzl.ui.teacher.constant.SlidingMenu>

好了,现在就已经有效果了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值