仿QQ5.0侧滑菜单效果_ayuc0048的博客-程序员秘密

技术标签: 移动开发  

慕课网视频

 

今天我们来继承 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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ayuc0048/article/details/101587414

智能推荐

TypeError: __init__() got an unexpected keyword argument ‘maxlength’_luoleicn的博客-程序员秘密

Django下出现这个错误,记录一下,貌似以前的版本是应该用maxlength,但是新版本里面使用max_length

计算机视觉和模式识别领域SCI期刊介绍_weixin_30613727的博客-程序员秘密

原帖地址:http://blog.sciencenet.cn/blog-370458-750306.html关于计算机视觉和模式识别领域的期刊并不是很多,下面我收集了一些该领域的代表性期刊,并介绍了他们的影响因子以及投稿难度和审稿周期。希望对大家有帮助吧,后期大家还有发现的可以留言,补充哦。首先介绍计算机视觉领域的4个顶级代表性期刊吧。(1) IEEE Transactions...

用IntellIDEA开发JSP的一些总结_t_larry的博客-程序员秘密_idea使用jsp总结

如何在Tomcat5.5中调试JavaBean代码在Run|Edit Configurations中,添加一个model,选择tomcat server中的local。    1、在server选项页中设置startup page为:http://localhost:8080/项目名称    2、在Depolyment选项页中选择Depoly Web Module,在Depolyment So

ReactiveCocoa之基本运算符_chunqingtai2922的博客-程序员秘密

基本运算符 本篇文档阐述了一些在RAC中经常使用的运算符,也包含了一些例子以说明他们的用法. 序列和信号所共同使用的运算符被称之为流运算符. 订阅信号执行自定义操作 订阅信号 注入自定义操作 转换流 映射 过滤 合并流 串联 降维 映...

VSCode--vue模板--ESLint格式_春tian的博客-程序员秘密

{ "生成vue模板": { "prefix": "vue", "body": [ "&lt;template&gt;" " &lt;div&gt;" " &lt;/div&gt;" "&lt;/template&gt; " "&lt;script&gt;" //这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等) //例如:import《组件名称》from'《组件路径》'; "expo...

MySQL 5 将数据库授权给用户访问 2021-09-27_逍遥在南方的博客-程序员秘密

新添加的用户aaa没有权限访问所有数据库,需要添加权限比如有个数据库名叫xyqas,需要授权给用户aaagrant all privileges on `pai-ica`.* to 'pai-ica'@'%';如果报错[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NKiazDy-1632706366572)(/uploads/mysqlinit/images/m_f872ea71942981f43828438dbba5c1f3_r.png)]说明当前登录的mys

随便推点

python 手势检测和追踪_python对肌电信号进行简单的手势识别_往里卷的博客-程序员秘密

获取平均值起始点,并将对应时间点作用于原始信号上,对四通道信号进行行动段提取,并将长度较小的部分过滤,视为噪音for i in range(1,5):names['period_%s'%i]=[]names['sta_filt_%s'%i]=[]names['end_filt_%s'%i]=[]for j in range(len(names['sta_%s'%i])):names['period...

18.12.17 DSA The xor-longest Path_dhc65376的博客-程序员秘密

描述In an edge-weighted tree, the xor-length of a pathpis defined as the xor sum of the weights of edges onp:⊕ is the xor operator.We say a path the xor-longest path if it has the large...

CentOS6下基于Nginx搭建mp4/flv流媒体服务器(可随意拖动)并支持RTMP/HLS协议(含转码工具)_你尽不知的博客-程序员秘密

1.先添加几个RPM下载源     1.1)安装RPMforge的CentOS6源     [[email protected] ~]# wget -c http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm     [[email protected]

ps aux命令显示的状态中D、R、S 等是什么意思_小新GSUNG0222的博客-程序员秘密

ps aux命令显示的状态列中的D 不可中断 Uninterruptible sleep (usually IO)R 正在运行,或在队列中的进程S 处于休眠状态&lt; 高优先级N 低优先级L 有些页被锁进内存s 包含子进程+ 位于后台的进程组;l 多线程,克隆线程 multi-threaded (using CLONE_THREAD, like NPTL pthreads do)T 停止或被追踪Z 僵尸进程W 进入内存交换(从内核2.6开始无效)X 死掉的进程UID.

python代码库-哪些 Python 库让你相见恨晚?_weixin_37988176的博客-程序员秘密

Awesome Python 中文版网站?jobbole.github.ioAwesome Python中文版来啦!本文由 伯乐在线 - 艾凌风 翻译,Namco 校稿。未经许可,禁止转载!英文出处:github.com。----------------这又是一个 Awesome XXX 系列的资源整理,由 vinta 发起和维护。内容包括:Web框架、网络爬虫、网络内容提取、模板引擎、数据库、数...

USB四种传输方式_无语僧314的博客-程序员秘密

原文地址:USB四种传输方式作者:蒙美麦兔  USB定义了4中传输类型  控制传输:可靠的、非周期的、由主机软件发起的请求或者回应的传输,通常用于命令事物和状态事物。  同步传输:在主机与设备之间的周期性的、连续的通信,一般用于传输与时间相关的信息。这种类型保留了将时间概念包含于数据总的能力。但这并不意味着传输这样的数据的时间总是很重要,基传输并不一定很紧急。  中断传输:小规模数据