美文网首页Android实用控件Android
Android流行UI布局——底部导航(BottomNaviga

Android流行UI布局——底部导航(BottomNaviga

作者: brucevanfdm | 来源:发表于2016-12-11 11:40 被阅读23527次

    前言:
    Android端采用底部导航栏的APP非常多,比如微信、微博、支付宝...等等,这也不能说是盲目学习iOS,毕竟好东西大家都可以用,各家操作系统也都在博采众长,互相学习。Android端的底部导航栏有着一套规范,详情

    这篇文章为大家带来
    1.官方BottomNavigationView的使用方法
    2.结合ViewPager、Fragment实现一个流行UI布局!
    3.并用反射解决ViewPager与BottomNavigationView侧滑联动时的一个小问题。

    最终实现效果:


    BottomNavigationView.gif

    在谷歌官方发布BottomNavigationView控件之前我们可以自己组合控件实现,比如LinearLayout + TextView(使用android:drawableTop属性+selector状态切换)、RadioGroup + RadioButton等等组合控件的方法自定义实现复杂效果。除了第三方外,现在我们多了一个选择。

    开始
    1.新建project然后导入以下support:design library,BottomNavigationView就在这个design库中。顺带导入这个V4包,因为待会要使用到ViewPager(这里其实不用记这么麻烦的库跟版本怎么写,直接在AS 的design面板把控件拖进来就会自动导入了。)

    compile'com.android.support:design:25.0.1'
    compile'com.android.support:support-v4:25.0.1'
    

    2.在res下新建menu文件夹,新建一个menu菜单,多少个自己看着办吧:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:id="@+id/item_news"
            android:icon="@mipmap/ic_news_gray"
            android:title="新闻" />
        <item
            android:id="@+id/item_lib"
            android:icon="@mipmap/ic_library_gray"
            android:title="图书" />
        <item
            android:id="@+id/item_find"
            android:icon="@mipmap/ic_discovery_gray"
            android:title="发现" />
        <item
            android:id="@+id/item_more"
            android:icon="@mipmap/ic_more_gray"
            android:title="更多" />
    </menu>
    

    3.接着是布局文件主要代码如下(最下面的View效果是加一个阴影):

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottom_navigation" />
    
    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
       android:layout_alignParentBottom="true" 
       app:itemIconTint="@drawable/bottom_navigation_selector"
       app:itemTextColor="@drawable/bottom_navigation_selector"
       app:menu="@menu/menu_bottom_navigation" />
    
    <View
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:layout_above="@id/bottom_navigation"
        android:background="@drawable/bottom_shadow" />
    

    app:itemIconTint="@drawable/bottom_navigation_selector" 为icon着色,写个selector即可。
    app:itemTextColor="@drawable/bottom_navigation_selector"该属性为文字着色,同样写个selector实现点击颜色切换的效果。

    4.写完布局写代码,完整activity文件如下:
    实例化控件后为NavigationView添加监听事件即可,代码如下:

    package com.fedming.bottomnavigationdemo;
    
    import android.os.Bundle;
    import android.support.annotation.NonNull;
    import android.support.design.widget.BottomNavigationView;
    import android.support.v4.view.ViewPager;
    import android.support.v7.app.AppCompatActivity;
    import android.view.MenuItem;
    
    /**
     * Created by bruce on 2016/11/1.
     * HomeActivity 主界面
     */
    
    public class HomeActivity extends AppCompatActivity {
    
        private ViewPager viewPager;
        private MenuItem menuItem;
        private BottomNavigationView bottomNavigationView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_home);
    
            viewPager = (ViewPager) findViewById(R.id.viewpager);
            bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
            //默认 >3 的选中效果会影响ViewPager的滑动切换时的效果,故利用反射去掉
            BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
            bottomNavigationView.setOnNavigationItemSelectedListener(
                    new BottomNavigationView.OnNavigationItemSelectedListener() {
                        @Override
                        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                            switch (item.getItemId()) {
                                case R.id.item_news:
                                    viewPager.setCurrentItem(0);
                                    break;
                                case R.id.item_lib:
                                    viewPager.setCurrentItem(1);
                                    break;
                                case R.id.item_find:
                                    viewPager.setCurrentItem(2);
                                    break;
                                case R.id.item_more:
                                    viewPager.setCurrentItem(3);
                                    break;
                            }
                            return false;
                        }
                    });
    
            viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
                }
    
                @Override
                public void onPageSelected(int position) {
                    if (menuItem != null) {
                        menuItem.setChecked(false);
                    } else {
                        bottomNavigationView.getMenu().getItem(0).setChecked(false);
                    }
                    menuItem = bottomNavigationView.getMenu().getItem(position);
                    menuItem.setChecked(true);
                }
    
                @Override
                public void onPageScrollStateChanged(int state) {
                }
            });
    
            //禁止ViewPager滑动
    //        viewPager.setOnTouchListener(new View.OnTouchListener() {
    //            @Override
    //            public boolean onTouch(View v, MotionEvent event) {
    //                return true;
    //            }
    //        });
    
            setupViewPager(viewPager);
        }
    
        private void setupViewPager(ViewPager viewPager) {
            ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    
            adapter.addFragment(BaseFragment.newInstance("新闻"));
            adapter.addFragment(BaseFragment.newInstance("图书"));
            adapter.addFragment(BaseFragment.newInstance("发现"));
            adapter.addFragment(BaseFragment.newInstance("更多"));
            viewPager.setAdapter(adapter);
        }
    }
    

    可以看到BottomNavigationView+ViewPager+Fragment可以实现流行的布局框架,可以像微信那样左右滑动,也可以像支付宝那样点击切换。

    截图20161211104920.png

    细心的朋友可能发现了,上面的代码为什么用了反射呢?
    原因就是官方的BottomNavigationView默认有个放大的ShiftingMode效果,但是尚未支持代码层级的切换。在3个menu item及以下时默认关闭,而到了4个及以上时就懵逼了,因为我们是要做ViewPager的侧滑啊!

    20161211105617.png

    滑动时,ViewPager是切换过去了,下面这item也切换过去了,但是你说说这效果是不是坑爹!!?

    没办法了,查了一圈资料,发现官方这个控件还不支持代码层级的切换选项(如果你发现了,请告诉我)。迫不得已,只能看源码,开启上帝(反射)模式了!

    mShiftingMode.png
    public class BottomNavigationViewHelper {
    
        public static void disableShiftMode(BottomNavigationView navigationView) {
    
            BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.getChildAt(0);
            try {
                Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
                shiftingMode.setAccessible(true);
                shiftingMode.setBoolean(menuView, false);
                shiftingMode.setAccessible(false);
    
                for (int i = 0; i < menuView.getChildCount(); i++) {
                    BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
                    itemView.setShiftingMode(false);
                    itemView.setChecked(itemView.getItemData().isChecked());
                }
    
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    

    我们通过反射拿到了BottomNavigationMenuView,至于为什么是BottomNavigationView 的第一个子View这就要看源码了,AS中直接可以点进去看。然后我们知道了这个效果是由mShiftingMode来决定的,那么上面的代码就好理解了。

    在实例化BottomNavigationView后调用一次这行代码即可:

    BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
    

    如果不需要配置侧滑切换的话,直接默认效果就好,不要去反射修改了。禁止滑动时,为ViewPager添加setOnTouchListener接口,在onTouch下直接消费掉点击事件。添加下面这段代码:

    //禁止ViewPager滑动
    viewPager.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    });
    

    最后来看下效果:

    BottomNavigationView.gif

    大功告成!附上源码:这里。希望能跟大家成为点赞之交,哈哈。

    相关文章

      网友评论

      • 5ddf69c8c4c2:能不能setupViewPager()方法里面做跳转,就是点击底部导航栏的是调到一个activity中显示这个activity的内容
      • 5ddf69c8c4c2:能不能setupViewPager()方法里面做跳转,就是点击底部导航栏的是调到一个activity中显示这个activity的内容
      • 初心_d439:怎么在界面里添加内容呀? 为什么代码中没有这些layout? 可以点拨一下吗 大神?
      • 禹兮_4518:有一点不理解,bottom_shadow这个背景效果不是给阴影部分了吗,那么点击tab切换时,他的水波纹效果是从哪来的?
      • 7c43181c05c7:ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
        我没有啊
        brucevanfdm:@难得_a291 看源码
        7c43181c05c7:这是自定义的 adapter 吗
      • 背锅TV丶伴奏大师:能不能增加一个功能,就是已经实例化的fragment在切换fragment时不再实例化,使用之前的实例对象。
      • 爱红旗渠:……导航栏有着一套规范,详情。 这篇文章为大家带来1.官方BottomNavigationView的使用方法2.结合ViewPager、Fragment实现一个流行UI布局!3.并用反射解决……ViewPager与BottomNavigationView侧滑联动时的一个小问题。 最终实现效果: 在谷歌官方发布BottomNavigationView控件之前我们可以自己组合控件实现,比如……方外,现在我们多了一个选择。 开始1.新建project然后导入以下support:design library,BottomNavigationView就在这个design库中。顺带导入这个V4包…… - 爱红旗渠不知道是否是沙发
      • 彼岸青园:如何想中间凸起该怎么办呢?
        brucevanfdm:那就不能用官方这个方案,只能自己定义
      • 934460fc36f4:请问这个能加上小红点还有就是文字和图标的距离怎么控制
        brucevanfdm:只能自己定义,或者用第三方库
      • jihf: itemView.setShiftingMode(false);
        itemView.setChecked(itemView.getItemData().isChecked());
        这句有异常提示BottomNavigationItemView.setShiftingMode can only be called from within the
        1ac768b37aee:在方法上面添加 @SuppressLint("RestrictedApi") 就能解决了
        xiaowen_2010:你那个报错的问题解了吗
      • 四喜汤圆:我用你的代码试了下,发现BottomNavigationVieew的setOnNavigationItemSelectedListener事件最后要返回true才能有颜色切换的效果。不知道是什么原因?
        brucevanfdm:这个的话就应该是控件设定了,可以看源码
      • 48d1f73337f3:同时按底部Tab,快速切换,会出现异常,文字会变成白底,这是系统bug吗?你试试
      • 9ca4292efcd5:必须点赞啊,写的很好 学习了
      • linceln:代码层级的选项卡切换好象也没有:fearful:
      • 依旧慕慕:哈..学习了..唯一不好用到了反射..
        进击的NickMao:@brucevanfdm 其实直接不用侧滑,模仿iOS效果也蛮好的
        brucevanfdm:谷歌规范不建议侧滑切换,所以没有这个接口,只能用黑科技来了。
      • 萝神赋: :+1: 赞一个

      本文标题:Android流行UI布局——底部导航(BottomNaviga

      本文链接:https://www.haomeiwen.com/subject/cpiumttx.html