仿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
    评论
实现Android侧滑菜单有多种方式,其中一种比较常见的实现方式是使用DrawerLayout和NavigationView。 步骤如下: 1. 在XML布局文件中添加DrawerLayout和NavigationView,其中NavigationView中可以添加菜单项。 ``` <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 主界面内容 --> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- 侧滑菜单 --> <android.support.design.widget.NavigationView android:id="@+id/navigation_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header" app:menu="@menu/nav_menu" /> </android.support.v4.widget.DrawerLayout> ``` 2. 在Activity中设置DrawerLayout和NavigationView的监听器,并在onOptionsItemSelected方法中处理菜单项点击事件。 ``` public class MainActivity extends AppCompatActivity { private DrawerLayout mDrawerLayout; private NavigationView mNavigationView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDrawerLayout = findViewById(R.id.drawer_layout); mNavigationView = findViewById(R.id.navigation_view); mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { // 处理菜单项点击事件 return false; } }); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, mDrawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); mDrawerLayout.addDrawerListener(toggle); toggle.syncState(); } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == android.R.id.home) { mDrawerLayout.openDrawer(GravityCompat.START); return true; } return super.onOptionsItemSelected(item); } } ``` 3. 在NavigationView中添加菜单项,并为菜单项设置图标和标题。 ``` <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> <item android:id="@+id/nav_home" android:icon="@drawable/ic_home" android:title="Home" /> <item android:id="@+id/nav_gallery" android:icon="@drawable/ic_gallery" android:title="Gallery" /> <item android:id="@+id/nav_slideshow" android:icon="@drawable/ic_slideshow" android:title="Slideshow" /> </group> </menu> ``` 至此,实现了一个简单的Android侧滑菜单。如果要实现仿QQ侧滑删除功能,可以在ListView或RecyclerView中添加滑动删除的功能,并在删除时更新侧滑菜单中的未读消息数等信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值