仿QQ5.0侧滑菜单效果

慕课网视频

 

今天我们来继承 HorizontalScrollView 实现比较炫酷的侧滑菜单效果

继承HorizontalScrollView的好处有两点:

1、不用在写 MOVE 事件

2、不用解决 和 ListView 的冲突

 

以下是实现步骤:

自定义 ViewGroup,实现它的几个方法

1、onMeasure       决定内部 View (子 View) 的宽和高,以及呢,自己的宽和高

    /**
     * 设置 自己的宽和高  子 View  的宽和高
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!once) {
            mWapper = (LinearLayout) getChildAt(0);
            mMenu = (ViewGroup) mWapper.getChildAt(0);
            mContent = (ViewGroup) mWapper.getChildAt(1);

            // 子 View 的宽和高
            mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding;
            mMenuWidth = mMenu.getLayoutParams().width;
            mContent.getLayoutParams().width = mScreenWidth;

            once = true;
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

 

2、onLayout         决定子 View 的放置位置

    /**
     * 通过设置 偏移量 将 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) {
            // 开始时隐藏菜单
            this.scrollTo(mMenuWidth, 0);
            isOpen = false ;
        }
    }

 

3、onTouchEvent  决定菜单是否打开

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        int action = ev.getAction();
        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);
    }

 

自定义属性:

允许用户设置菜单离屏幕右侧的边距

1、书写 xml 文件  values/attr.xml

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

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

2、在布局文件中进行使用,特别注意 xmlns

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:negro="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:background="@drawable/img_frame_background">

    <com.negro.qq_50_slidingmenu.view.SlidingMenu
        android:id="@+id/id_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        negro:rightPadding="70dp">

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

            <!--左侧菜单-->
            <include layout="@layout/left_menu"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/qq">

                <Button
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="切换菜单"
                    android:onClick="toggleMenu"/>

            </LinearLayout>

        </LinearLayout>

    </com.negro.qq_50_slidingmenu.view.SlidingMenu>

</RelativeLayout>

3、在构造方法中(3个参数的构造方法)获得我们设置的值

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

        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrice = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrice);

        // 计算屏幕的宽度
        mScreenWidth = outMetrice.widthPixels;

        // 菜单的右边距为 整个屏幕宽的三分之一
        mMenuRightPadding = mScreenWidth / 3 ;

        // 获取我们定义的属性
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyleAttr, 0) ;
        int indexCount = a.getIndexCount();
        for(int i = 0; i < indexCount; i ++) {
            int attr = a.getIndex(i) ;
            switch (attr) {
                case R.styleable.SlidingMenu_rightPadding:
                    mMenuRightPadding = a.getDimensionPixelSize(attr, mMenuRightPadding) ;
                    break ;
            }
        }
        a.recycle();
    }

------------------------------------------

抽屉式侧滑

区别:菜单仿佛在内容区域底下

实现原理:

当 getScrollX 为 0 的时候,左侧菜单的偏移量为 0 (也就是菜单完全的显示出来)

当 getScrollX 为 mMenuWidth 的时候,左侧菜单的偏移量为 mMenuWidth (也就是菜单隐藏的时候,菜单的位置应该在内容区的下面)

偏移量可以用属性动画 TraslationX 来实现。

设置调用动画的时机: onScrollChanged

---------------------------------

设置一个变量,当菜单完全隐藏的时候,为0   当菜单完全显示出来的时候,为1

这个变量为 scale 计算方法为: (mMenuWidth - getScrollX) / mMenuWidth ;

当菜单完全隐藏的时候,getScrollX 为 mMenuWidth , 所以 scale 为 0

当菜单完全显示出来的时候,getScrollX 为 0 , 所以 scale 为 1

菜单从不显示到显示时 scale 的变化趋势: 0.0~1.0

现在在现实菜单的时候需要实现以下几个动画效果:

一、内容区域 缩放效果(内容区域的缩放需要注意 设置缩放的中心点位置为 左侧的中点位置)

缩放效果:1.0~0.7                       计算方法 1.0 - 0.3 * scale

二、菜单的偏移量需要修改

偏移量:0.7~0.0 * mMenuWidth    计算方法 (0.7 - 0.7 * scale)* mMenuWidth   

三、菜单的显示时有缩放,以及透明度的变化

缩放:0.7~1.0                             计算方法 0.7 + 0.3 * scale

透明度:0.6~1.0                          计算方法 0.6 + 0.4 * scale

实现代码如下:

    /**
     * 滚动发生的时候,会调用此方法
     * @param l
     * @param t
     * @param oldl
     * @param oldt
     */
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        float scale = (mMenuWidth - l) * 1.0f / mMenuWidth ;

        float rightScale = 1.0f - 0.3f * scale ;

        float leftScale = 0.7f + 0.3f * scale ;

        float leftTrans = (0.7f - 0.7f * scale) * mMenuWidth ;

        float leftAlpha = 0.6f + 0.4f * scale ;

        ViewHelper.setTranslationX(mMenu, leftTrans);
        ViewHelper.setScaleX(mMenu, leftScale);
        ViewHelper.setScaleY(mMenu, leftScale);
        // 设置内容区域的缩放中心点
        ViewHelper.setPivotX(mContent, 0);
        ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
        ViewHelper.setScaleX(mContent, rightScale);
        ViewHelper.setScaleY(mContent, rightScale);
        ViewHelper.setAlpha(mMenu, leftAlpha);
    }

 

然后再写几个操控菜单打开或者关闭的功能:

    /**
     * 打开菜单
     */
    public void openMenu() {
        if(isOpen) return ;

        this.smoothScrollTo(0, 0);
        isOpen = true ;
    }

    /**
     * 关闭菜单
     */
    public void closeMenu() {
        if(!isOpen) return ;

        this.smoothScrollTo(mMenuWidth, 0);
        isOpen = false ;
    }

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

 

然后就大功告成了!

想直接运行代码卡效果的童鞋,可以点这里下载源代码

------------------------------------

总结

一、自定义ViewGroup

1、构造方法的选择,获得一些需要用到的值

2、onMeasure 计算子 View 的宽和高,以及设置自己的宽和高

3、onLayout 决定子 View 的布局的位置

4、onTouchEvent

二、构造方法

1、context   用户在代码中 new 的时候调用的方法

2、context, attr 布局文件中声明(没有用到自定义属性时,调用此方法)

3、context,attr,defStyle (有用到自定义属性时调用此方法)

三、自定义属性

1、attr.xml

2、布局文件中 xmlns

3、在三个参数的构造方法中,获得我们自定义属性的值

四、属性动画

Android 3.0才有

向下兼容,请用 nineoldanimation.jar

转载于:https://www.cnblogs.com/niulinguo/p/4697349.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值