仿QQ5.0侧滑菜单实现

周末两天时间在公司里研究新版QQ的侧滑菜单的实现,在网上也看到一些实例,也看了慕课网上的课程,终于完全搞明白了整个实现过程,现将我的理解和实现写出来。

自定义控件--显示侧滑菜单和内容的horizontalScrollView

需要用到 HorizontalScrollView控件,同时要实现
    (1)初始状态是显示内容,隐藏菜单
    (2)当滑动超过屏幕一般的响应。(隐藏菜单or显示菜单)
    (3)滑动过程中内容区域的缩放,菜单的偏移和缩放以及透明度变化。
    (4)点击按钮,切换菜单的显示和隐藏

(1)初始状态是显示内容,隐藏菜单

 第一步:重写了自定义控件的两个参数的构造方法,主要目的是得到屏幕宽度和菜单的右边距
 //未定义属性时使用
    public SlidingMenu(Context context, AttributeSet attrs) {
        super(context, attrs);

//        //获取屏幕宽度
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        mScreenWidth = outMetrics.widthPixels;

        //menu右边距,转化为像素值
        mMenuRight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics());
//        this(context, attrs, 0);
    }
第二步:重写onMeasure(),决定菜单区域和内容区域的宽高,这样自定义视图的宽高就决定了
<pre name="code" class="html">//重写onMeasure(),决定内部子view的宽高,并决定该控件的宽高

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!once) {
            //获取LinearLayout
            mWapper = (LinearLayout) getChildAt(0);
            //获取菜单和内容
            mMenu = (ViewGroup) mWapper.getChildAt(0);
            mContent = (ViewGroup) mWapper.getChildAt(1);

            //设置菜单和内容的宽高
            mMenu.getLayoutParams().width = mScreenWidth - mMenuRight;
            mMenuWidth = mScreenWidth - mMenuRight;
            mContent.getLayoutParams().width = mScreenWidth;

            //决定子view视图的宽高后该视图的宽高就决定了,无需再设定
            once = true;
        }

    }

第三步:重写onLayout()方法,决定控件的布局,即初始状态只显示内容区域隐藏菜单区域
//重写onLayout(),决定视图的位置
    @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(mMenuWidth, 0);
        }

    }
这里使用到scrollTo()方法,设置scrollView的偏移,实现隐藏菜单。
 
 
第四步:重写OnTouchEvent()方法,实现滑动之后如果菜单显示超过屏幕一般则全部显示,否则隐藏
  //重写TouchEvent(),监听手抬起时的动作
    @Override
    public boolean onTouchEvent(MotionEvent ev) {

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

                //判断此时scrollview滚动的距离与menu宽度比较,如果大于一般则隐藏,如果小于一半则显示
                int scrollX = this.getScrollX();
                int helfMenuWidth = mMenuWidth / 2;
                if (scrollX >= helfMenuWidth) {
                    //隐藏menu
                    this.smoothScrollTo(mMenuWidth, 0);
                    isOpen = false;
                } else {
                    //显示menu
                    this.smoothScrollTo(0, 0);
                    isOpen = true;
                }
                return true;
            }


        }
        return super.onTouchEvent(ev);
    }

这样基本的功能已经实现,再添加上按钮实现

(2)点击按钮,菜单显示隐藏的切换

第一步:按钮设置监听事件
 btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //切换菜单
                slideMenu.changeMenu();

            }
        });
第二步:实现切换:
 public void changeMenu() {
        if(isOpen){//如果是开启则关闭
            closeMenu();
        }else{
            openMenu();
        }

    }

    //打开菜单,设置标志
    private void openMenu() {
        this.smoothScrollTo(0,0);
        isOpen = true;
    }

    //关闭菜单,设置标志
    private void closeMenu() {
        this.smoothScrollTo(mMenuWidth,0);
        isOpen = false;
    }
菜单的显示隐藏就是使用的scrollView的smoothSrollTo()这样的函数,让滑动更加平滑。切换是定义了一个标志,状态改变时修改标志,同时注意要在onTouchEvent()中显示隐藏也要修改标志。

(3)实现自定义属性

<com.example.chengyajun.slidingmenu.view.SlidingMenu
        android:id="@+id/slidemenu"
        android:layout_width="match_parent"
        test:rightPadding="80dp"
        android:layout_height="match_parent">

如何实现在xml布局文件中动态修改菜单的右边距呢? test:rightPadding="80dp"
第一步:values文件夹下创建attr.xml文件,设置属性的基本信息
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="rightPadding" format="dimension"></attr>

    <declare-styleable name="SlidingMenu">
        <attr name="rightPadding"></attr>
    </declare-styleable>

</resources>

第二步:布局文件中,声明xmlns,在eclipse和android studio中优点不一样,实现的时候要注意一下。
eclipse : xmlns: test = "http://schemas.android.com/apk/res/ com.example.chengyajun. slidingmenu "
android studio : xmlns: test = "http://schemas.android.com/apk/res-auto"

第三步:重写自定义视图的构造方法,获取自定义属性的值
  //一个参数的构造方法,代码中只用new来创建对象
    public SlidingMenu(Context context) {
//        super(context);
        this(context, null);
    }


    //未定义属性时使用
    public SlidingMenu(Context context, AttributeSet attrs) {
//        super(context, attrs);

//        //获取屏幕宽度
//        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//        DisplayMetrics outMetrics = new DisplayMetrics();
//        wm.getDefaultDisplay().getMetrics(outMetrics);
//        mScreenWidth = outMetrics.widthPixels;
//
//        //menu右边距,转化为像素值
//        mMenuRight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics());
        this(context, attrs, 0);
    }


    //三个参数的构造方法,使用了自动以属性
    public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //对右边距进行设置


        // 获取我们定义的属性
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.SlidingMenu, defStyleAttr, 0);

        int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case R.styleable.SlidingMenu_rightPadding:
                    mMenuRight = a.getDimensionPixelSize(attr,
                            (int) TypedValue.applyDimension(
                                    TypedValue.COMPLEX_UNIT_DIP, 50, context
                                            .getResources().getDisplayMetrics()));
                    break;
            }
        }
        a.recycle();


        //获取屏幕宽度
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        mScreenWidth = outMetrics.widthPixels;


    }
分别重写一个参数,两个参数,三个参数的构造方法,
一个参数:new一个对象时候
两个参数:没有自定义控件
三个参数:使用了自定义控件
注意:自定义属性值得获取,需要及时的释放资源。

(4)实现属性动画:内容区域的缩放,菜单的偏移、缩放和透明度变化
此时需要引入一个jar包 :nineoldandroids-2.4.0.jar,android3.0以下的需要这个jar包
需要重写scrollView的 onScrollChanged(),在滑动过程中实现动画效果
@Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {//l=getScrollX()
        float scale = l*1.0f/mMenuWidth;   //隐藏的比例1.0~0

        //抽屉菜单的实现,菜单在拉动过程中偏移
        //当滑动过程中,移动menu,menu的偏移
//        ViewHelper.setTranslationX(mMenu,l);//隐藏多少,就让menu偏移多少,这样保证menu的x起点始终在屏幕x起点


        //QQ菜单实现
        /**
         * 内容区域的缩放:1.0~0.7   0.7+0.3*scale
         * 菜单区域的缩放:0.7~1.0    1.0-0.3*scale
         * 菜单区域的透明度:0.7~1.0  1.0-0.3*scale
         *
         */

        //内容区域   缩放
        ViewHelper.setScaleX(mContent,0.7f+0.3f*scale);
        ViewHelper.setScaleY(mContent,0.7f+0.3f*scale);
        //移动到最后发现全部隐藏了,因为当到达最后的80dp后,区域缩放且默认缩放点在中心,导致看不见
        //修改区域的缩放点
        ViewHelper.setPivotX(mContent,0);
        ViewHelper.setPivotY(mContent,mContent.getHeight()/2);

        //菜单区域

        //偏移
        ViewHelper.setTranslationX(mMenu,l*0.8f);

//        //缩放
        ViewHelper.setScaleX(mMenu, 1.0f-0.3f*scale);
        ViewHelper.setScaleY(mMenu,1.0f-0.3f*scale);

        //透明度
        ViewHelper.setAlpha(mMenu,1.0f-0.3f*scale);




    }
通过ViewHelper来实现动画,缩放的比例控制需要根据scrollView的滑动比例来决定。
在实现过程中开始没有修改内容区域的缩放中心点,发现直接隐藏了,后面才知道原来到最后的80dp后,区域还要缩放,而且默认缩放中心点在区域中心,这样就看不见了,后来修改区域缩放的中心点未左边中间点,这样就是到最后也是停留在80dp的位置。
这样整个自定义的视图就写完了,技术难点也解释了。









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值