自定义的侧滑栏?自定义ViewGroup的一小步

在哔哩哔哩看到侧滑整不出来?侧滑控件仅仅改了几行,让它跑起来

package com.example.fill;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Scroller;

public class SlideMenu extends FrameLayout {
    private View menuView, mainView;
    private int menuWidth;
    private Scroller scroller;

    public SlideMenu(Context context) {
        super(context);
        init();
    }

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

    private void init() {
        scroller = new Scroller(getContext());
    }

    /**
     * 当1级子view全部加载完调用,可以用初始化子view引用
     * 注意这里无法获取子view的宽高
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        menuView = getChildAt(0);
        mainView = getChildAt(1);
    }

    //使Menu也具有滑动功能
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) ev.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = (int) (ev.getX() - downX);
                if (Math.abs(deltaX) > 8) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * s设置两个子view在页面上的布局
     *
     * @param l:当前子view的左边在父view的坐标系的x坐标
     * @param t:当前子view的顶边在父view的坐标系的y坐标
     * @param r:当前子view的宽
     * @param b:当前子view的高
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        menuWidth = menuView.getLayoutParams().width;

        /**
         * 众所周知
         * {@link android.view.ViewGroup.LayoutParams}
         * <P>public static final int FILL_PARENT = -1;</P>
         * <P>public static final int MATCH_PARENT = -1;</P>
         * <P>public static final int WRAP_CONTENT = -2;</P>
         */
        if (menuWidth < 0) {
            menuWidth = r - l;
        }

        menuView.layout(-menuWidth, 0, 0, b);
        mainView.layout(0, 0, r, b);
    }

    /**
     * 处理屏幕滑动事件
     */
    private int downX;

    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("TAG", "---------------onTouchEvent------------------ ");

                int moveX = (int) event.getX();
                Log.i("TAG", "moveX:" + moveX);
                int deltaX = moveX - downX;
                Log.i("TAG", "deltaX:" + deltaX);
                int newScrollX = getScrollX() - deltaX;
                Log.i("TAG", "newScrollX:" + newScrollX);
                if (newScrollX < -menuWidth) newScrollX = -menuWidth;
                Log.i("TAG", "newScrollX:" + newScrollX);
                if (newScrollX > 0) newScrollX = 0;
                Log.i("TAG", "newScrollX:" + newScrollX);
                scrollTo(newScrollX, 0);
                downX = moveX;
                break;
            case MotionEvent.ACTION_UP:
                //当滑动距离小于Menu宽度的一半时,平滑滑动到主页面
                if (getScrollX() > -menuWidth / 2) {
                    closeMenu();
                } else {
                    //当滑动距离大于Menu宽度的一半时,平滑滑动到Menu页面
                    openMenu();
                }
                break;
        }
        return true;
    }

    //关闭menu
    private void closeMenu() {
        scroller.startScroll(getScrollX(), 0, 0 - getScrollX(), 0, 400);
        invalidate();
    }

    //打开menu
    private void openMenu() {
        scroller.startScroll(getScrollX(), 0, -menuWidth - getScrollX(), 0, 400);
        invalidate();
    }

    /**
     * Scroller不主动去调用这个方法
     * 而invalidate()可以调用这个方法
     * invalidate->draw->computeScroll
     */
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset()) {
            //返回true,表示动画没结束
            scrollTo(scroller.getCurrX(), 0);
            invalidate();
        }
    }

    /**
     * 切换菜单的开和关
     */
    public void switchMenu() {
        if (getScrollX() == 0) {
            openMenu();
        } else {
            closeMenu();
        }
    }

}


为什么我之前不显示侧滑栏或者滑动没反应?

我们来看一下滑动控制的代码:

int downX = 0;

public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getX();//按下时的地方
                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getX();//当前滑动的位置
                int deltaX = moveX - downX;//与上一次滑动的距离(这个downX在case最后被赋值了,别被名字骗了
                int newScrollX = getScrollX() - deltaX;//上一次的滑动x距离和此次的滑动距离差(即真正需要滑动的距离
                if (newScrollX < -menuWidth) newScrollX = -menuWidth;//如果滑动距离居然要比滑动部分的宽度还要大,重置这个大小仅限控件宽度(防止划过头出现破绽
                if (newScrollX > 0) newScrollX = 0;//如果你是往右边扒拉,对不起,不动
               
                scrollTo(newScrollX, 0);//开划
                downX = moveX;
                break;
            case MotionEvent.ACTION_UP:
                //当滑动距离小于Menu宽度的一半时,平滑滑动到主页面
                if (getScrollX() > -menuWidth / 2) {
                    closeMenu();
                } else {
                    //当滑动距离大于Menu宽度的一半时,平滑滑动到Menu页面
                    openMenu();
                }
                break;
        }
        return true;
    }

看完注释你应该会感觉清晰一点:计算滑动距离,防止划过头或者反向滑动。
为什么出现划不动的情况?我们把目光放到这个地方:

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        menuView = getChildAt(0);
        mainView = getChildAt(1);
        menuWidth = menuView.getLayoutParams().width;
    }

关注最后一行:menuView.getLayoutParams().width得到了侧滑栏的宽度。按道理说是没啥问题。但是如果你了解LayoutParams,你大概知道我们经常写的wram_contentmatch_parent在程序内部到底被转换成什么了?
我们瞄一眼ViewGroup.LayoutParams的部分代码:

    public static class LayoutParams {
        /**
         * Special value for the height or width requested by a View.
         * FILL_PARENT means that the view wants to be as big as its parent,
         * minus the parent's padding, if any. This value is deprecated
         * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
         */
        @SuppressWarnings({"UnusedDeclaration"})
        @Deprecated
        public static final int FILL_PARENT = -1;

        /**
         * Special value for the height or width requested by a View.
         * MATCH_PARENT means that the view wants to be as big as its parent,
         * minus the parent's padding, if any. Introduced in API Level 8.
         */
        public static final int MATCH_PARENT = -1;

        /**
         * Special value for the height or width requested by a View.
         * WRAP_CONTENT means that the view wants to be just large enough to fit
         * its own internal content, taking its own padding into account.
         */
        public static final int WRAP_CONTENT = -2;
        
}

英语不错的童鞋可以直接看注释。否则你就直接理解成为:

  • wram_content 在程序当中就是 -2
  • match_parent 在程序当中就是 -1

回到刚刚的那几行代码:

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        menuView = getChildAt(0);
        mainView = getChildAt(1);
        menuWidth = menuView.getLayoutParams().width;
    }

假如你在不知情的情况下随意把侧滑栏的大小填成了:wram_content 或者 match_parent
恭喜你,掉坑了。
menuView.getLayoutParams().width 将会得到-1 或者是 -2
当程序在执行滑动部分代码的时候:


            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getX();
                int deltaX = moveX - downX;
                int newScrollX = getScrollX() - deltaX;
                //当menuWidth为负值,-1或者-2,这个判断将会生效,将会把滑动距离赋值为-1或者-2
                if (newScrollX < -menuWidth) newScrollX = -menuWidth;
                if (newScrollX > 0) newScrollX = 0;
               
                scrollTo(newScrollX, 0);//开划
                downX = moveX;
                break;

如上。

当menuWidth为-1或者-2 ,滑动1个像素,几乎和没滑动一样

代码来源:

link@CSDN.Page

教学视频来源:

link@BLBL.Vedio

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值