Android 侧滑菜单栏SlidingMenu优化实现(SlidingMenu + FragmentTabHost)

        Android项目中使用侧滑菜单栏功能的有很多量级很大的APP,比如QQ、OFO、Mobike等,所以该功能属于较为常见的、用户熟悉的功能,因此可以在任意的项目中使用。我之前就用开源库SlidingMenu实现过侧滑菜单栏,尽管SlidingMenu已经很久没有维护了,我还是打算用它来实现,毕竟轻车熟路能避免很多坑,同时网上的经验也能帮助我使用的更加轻松。

        使用SlidingMenu开源库的第一步肯定是下载该开源库,但是不推荐直接下载该原始开源库。因为很久没有维护了,互联网发展又很迅速,该库已经有些不满足日新月异的需求,所以需要我们自己修改源代码,满足需求。

        优化后的开源库下载地址(https://download.csdn.net/download/qq941263013/10551527)

        注:文章末尾附项目源码下载地址(内含优化后的开源库)

      优化的功能:

        1.实现沉浸式状态栏;

        (方法:https://blog.csdn.net/qq941263013/article/details/81112192

        2.为侧滑剩余部分添加淡入淡出的阴影;

        (方法:https://blog.csdn.net/qq941263013/article/details/81112095

        注:①关于网上说的按钮点击无效问题,亲测不存在此类问题,需要注意的是

                 传入的View和实现点击事件的View要使用相同的实例。

               ②导入报错等问题,百度解决即可,就不在这里一一赘述了。

      效果展示

        主要展示了通过手势左滑(右滑)展示菜单栏、通过点击按钮展示左滑(右滑)菜单栏、FragmentTabHost的切换、侧滑菜单栏的点击事件(切换Fragment)、返回键关闭侧滑菜单栏、侧滑菜单栏的沉浸式效果、侧滑剩余部分的阴影效果等。

      SlidingMenu的使用

        1.导入优化后的开源库SlidingMenuLibrary;

        2.因为侧滑菜单栏需要实现沉浸式效果,所以首先需要SlidingMenu依赖的Activity也实现

           沉浸式状态栏效果(4.4以上);在onCreate()中添加以下代码即可:

//判断当前设备版本号是否为4.4以上,如果是,则通过调用setTranslucentStatus让状态栏变透明
        //透明状态栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Window window = getWindow();
            window.setFlags(
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }

      3.初始化SlidingMenu实现侧滑菜单栏,代码中的注释非常详细,在这里直说几个重要的点。

           ①菜单栏的呈现模式分为三种:左、右、左右;如果设置为左右模式,则必须实现setSecondaryMenu();

           ②setOffsetFadeDegree()实现了为侧滑剩余部分添加淡入淡出的阴影,该方法原始的开源库是没有的;

           ③setTouchModeBehind()实现菜单栏内部可以滑动,但是会导致菜单栏内的点击事件无效;

           ④attachToActivity()中的第二个入参传入WINDOW为沉浸式状态栏,而传入CONTENT为非沉浸式;

           ⑤setMenu()中传入的View,要与点击事件使用同一个View,否则点击事件无效;

/**
     * 初始化SlidingMenu实现侧滑菜单栏
     */
    private void initSlidingMenu() {
        slidingMenu = new SlidingMenu(this);
        //设置呈现模式(分为左、右、左右三种)
        slidingMenu.setMode(SlidingMenu.LEFT_RIGHT);
        //设置侧滑界面偏移出的尺寸(剩余部分)
        slidingMenu.setBehindOffset(DensityUtil.dip2px(100, context));
        //设置剩余部分的阴影
        slidingMenu.setOffsetFadeDegree(0.4f);
        //设置全屏都可以触摸
        slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
        //设置菜单内可以滑动(会导致菜单内的点击事件无效)
//        slidingMenu.setTouchModeBehind(SlidingMenu.TOUCHMODE_FULLSCREEN);
        //添加到当前Activity当中
        //(SLIDING_WINDOW配合沉浸式状态栏,SLIDING_CONTENT配合非沉浸式状态栏)
        slidingMenu.attachToActivity(this, SlidingMenu.SLIDING_WINDOW);
        //给侧滑界面传入View,与点击事件使用同一个View
        //如果给侧滑界面引用一个布局资源作为所展示的界面,点击事件无效,
        //因为侧滑界面和点击事件是不同的View
        View view = View.inflate(context, R.layout.my_sliding_menu, null);
        slidingMenu.setMenu(view);
        slidingMenu.setSecondaryMenu(R.layout.my_sliding_menu_right);

        initSlidingMenuButton(view);

        //openListener仅在侧滑菜单栏开启的时候调用;
        //openedListener在侧滑菜单栏开启后调用(open之后);
        //      并且在滑动且未关闭的时候再次调用。
        //      (也就是滑动未关闭然后又回到开启的状态时再次调用)
        slidingMenu.setOnOpenedListener(new SlidingMenu.OnOpenedListener() {
            @Override
            public void onOpened() {
                slidingMenuToggleStatus = true;
            }
        });

        //closeListener仅在侧滑菜单栏开启的时候调用;
        //closedListener在侧滑菜单栏开启后调用(close之后);
        //      并且在滑动且未关闭的时候再次调用。
        //      (也就是滑动未关闭然后又回到开启的状态时再次调用)
        slidingMenu.setOnClosedListener(new SlidingMenu.OnClosedListener() {
            @Override
            public void onClosed() {
                slidingMenuToggleStatus = false;
            }
        });
    }

       4.初始化侧滑菜单栏中按钮的点击事件:

           通过setCurrentTab()实现Fragment的切换,通过toggle()实现菜单栏的开启与关闭;

@Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.ll_home://首页
                tabHost.setCurrentTab(0);
                slidingMenu.toggle();
                break;
            case R.id.ll_order://订单
                tabHost.setCurrentTab(1);
                slidingMenu.toggle();
                break;
            case R.id.ll_statistic://统计
                tabHost.setCurrentTab(2);
                slidingMenu.toggle();
                break;
            case R.id.ll_refresh://检查更新
                ToastUtil.makeText(context,"点击了检查更新");
                slidingMenu.toggle();
                break;
            case R.id.ll_logout://切换用户/注销
                ToastUtil.makeText(context,"点击了切换用户/注销");
                slidingMenu.toggle();
                break;
            default:
                break;
        }
    }

        5.返回键关闭处于开启状态的菜单栏:

          首先通过3中的SlidingMenu开启、关闭的监听,时时改变状态,然后通过状态判断是否处于开启状态,

          如果是开启状态,就关闭SlidingMenu,再执行退出程序的操作。时间的判断可以防止频繁点击返回键,

          导致菜单栏一直处于滑动状态的问题,看需求吧,可以不管它也挺好玩的。

private long mExitTime;
    private long mSlidingMenuTime;

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            //防止频繁点击返回键,导致侧滑一直在滑动状态
            //可以放弃时间的判断,当成个小玩意
            if (slidingMenuToggleStatus == true) {
                if ((System.currentTimeMillis() - mSlidingMenuTime) > 1000){
                    mSlidingMenuTime = System.currentTimeMillis();
                    slidingMenu.toggle();
                }
                return true;
            }

            if ((System.currentTimeMillis() - mExitTime) > 2000) {
                ToastUtil.makeText(context, "再按一次退出程序");
                mExitTime = System.currentTimeMillis();
            } else {
                //模拟Home键操作
                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.addCategory(Intent.CATEGORY_HOME);
                startActivity(intent);
            }
            return true;
        }

        //拦截MENU按钮点击事件,让它无任何操作
        if (keyCode == KeyEvent.KEYCODE_MENU) {
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

        6.在Fragment中开启侧滑菜单栏:

           showMenu()开启左侧滑菜单栏,showSecondaryMenu()开启右侧滑菜单栏

@OnClick({R.id.iv_titlebar_left, R.id.iv_titlebar_right})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.iv_titlebar_left://左侧滑菜单栏
                if (MainActivity.slidingMenu != null) {
                    MainActivity.slidingMenu.showMenu();
                }
                break;
            case R.id.iv_titlebar_right://右侧滑菜单栏
                if (MainActivity.slidingMenu != null) {
                    MainActivity.slidingMenu.showSecondaryMenu();
                }
                break;
        }
    }

      FragmentTabHost的使用

        1.页面布局:主要通过FragmentTabHost+FrameLayout实现底部导航栏,用View当做分割线。   

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/fl_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <View
        android:layout_width="match_parent"
        android:layout_height="0.4dp"
        android:background="#99cccccc"
        android:visibility="visible" />

    <android.support.v4.app.FragmentTabHost
        android:id="@+id/tabHost"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="2dp"
        android:paddingTop="2dp"
        android:visibility="visible" />
</LinearLayout>

             2.初始化FragmentTabHost

/**
     * 初始化FragmentTabHost
     */
    private void initFragmentTabHost() {
        //初始化tabHost
        FragmentTabHost tabHost = (FragmentTabHost) findViewById(R.id.tabHost);
        //将tabHost和FragmentLayout关联
        tabHost.setup(context, getSupportFragmentManager(), R.id.fl_content);

        //去掉分割线
        if (Build.VERSION.SDK_INT > 10) {
            tabHost.getTabWidget().setShowDividers(0);
        }
        //添加tab和其对应的fragment
        MainTabs[] tabs = MainTabs.values();
        for (int i = 0; i < tabs.length; i++) {
            MainTabs mainTabs = tabs[i];
            TabHost.TabSpec tabSpec = tabHost.newTabSpec(mainTabs.getName());

            View indicator = View.inflate(context, R.layout.tab_indicator, null);
            TextView tv_indicator = (TextView) indicator.findViewById(R.id.tv_indicator);
            Drawable drawable = context.getResources().getDrawable(mainTabs.getIcon());

            tv_indicator.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
            tv_indicator.setText(mainTabs.getName());
            tabSpec.setIndicator(indicator);
            tabHost.addTab(tabSpec, mainTabs.getCla(), null);
        }
    }

       小结

        以上就是侧滑菜单栏功能的实现过程了,其它具体的实现细节,文章末尾的源码中都有详细的注解,在这里就不一一赘述了。本博客主要是对SlidingMenu的使用做了比较详细的讲解,关于FragmentTabHost的部分比较粗糙,并不是懒呦,而是因为FragmentTabHost的知识点也有很多,将会在下一篇的博客中做成详细的讲解和demo,届时也会将博客地址添加到本博客中。

      附:

        项目源码下载地址(https://download.csdn.net/download/qq941263013/10551541)

        GitHub项目地址(https://github.com/wangyang0313/SlidingMenuBestImplementation)

        优化后的开源库地址(https://download.csdn.net/download/qq941263013/10551527)

猜你喜欢

转载自blog.csdn.net/qq941263013/article/details/81116930