android导航栏相关控件使用学习笔记

来源:互联网 发布:js移除最后一个子元素 编辑:程序博客网 时间:2024/06/04 21:13

顶部导航栏效果图:

这里写图片描述

底部导航栏效果图:

这里写图片描述

关于android导航栏的实现方法有很多,像TabHost+TabWidget,RadioButton+RadioGroup,FragmentTabHost等等。随着androidapi的更新,个人认为,目前实现导航栏最易用且美观的方式是TabLayout和BottomNavigationView,TabLayout实现顶部导航栏,BottomNavigationView实现底部导航栏。这两个控件都是位于android.support.design.widget包中,TabLayout是android5.0(api21)添加的,而BottomNavigationView是android7.1.1(api25)添加的,目前已发布的android官方开发文档中还没有关于BottomNavigationView的介绍,需要去官网文档才能看到。下面就对这些控件实现android导航栏时的使用方法做一些记录,方便以后查阅。

首先,添加对design包的依赖,如下所示:

compile 'com.android.support:design:25.3.1'

实际根据项目不同,后面的版本号可能需要变动。

底部导航栏的使用

直接在布局中添加BottomNavigationView,如下所示:

<android.support.design.widget.BottomNavigationView        android:id="@+id/navigation"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_gravity="bottom"        android:background="?android:attr/windowBackground"        app:menu="@menu/navigation" />

其中,注意android:layout_gravity和app:menu两个属性,不指定layout_gravity属性为bottom导航栏可能会飞到上面。飞到上面不久成了顶部导航栏了么?在逻辑上来讲确实没有问题,但是通过观察可以发现,顶部导航栏和底部导航栏的样式还是有差别的,如果反过来用看着就很别扭。app:menu属性就是为导航栏设置每个item了,看一下navigation.xml中的内容,如下所示:

<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android">    <item        android:id="@+id/navigation_home"        android:icon="@drawable/ic_home_black_24dp"        android:title="@string/title_home" />    <item        android:id="@+id/navigation_dashboard"        android:icon="@drawable/ic_dashboard_black_24dp"        android:title="@string/title_dashboard" />    <item        android:id="@+id/navigation_notifications"        android:icon="@drawable/ic_notifications_black_24dp"        android:title="@string/title_notifications" /></menu>

menu文件很简单,其中为每个item指定的icon和title在底部导航栏显示时就是每一项的图标和文字描述了。现在BottomNavigationView就已经添加好了,下面怎么在代码中使用他呢,也很简单,BottomNavigationView中目前提供了两个接口,如下图所示:

这里写图片描述

和给大部分控件设置点击方法一样,对应的方法就是这两个啦,如下图所示:

这里写图片描述

在代码中通过上述的两个方法给BottomNavigationView设置点击事件,在接口中判断MenuItem的id就可以执行相应的逻辑啦,本例中仅仅是改变了一个TextView中的文字,相关代码如下所示:

private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener            = new BottomNavigationView.OnNavigationItemSelectedListener() {        @Override        public boolean onNavigationItemSelected(@NonNull MenuItem item) {            switch (item.getItemId()) {                case R.id.navigation_home:                    mTextMessage.setText(R.string.title_home);                    return true;                case R.id.navigation_dashboard:                    mTextMessage.setText(R.string.title_dashboard);                    return true;                case R.id.navigation_notifications:                    mTextMessage.setText(R.string.title_notifications);                    return true;            }            return false;        }    };
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

上面两片代码在程序结构上并不位于同一个变量作用域中,所以分成了两个代码片。其实细心的朋友可以发现mOnNavigationItemSelectedListener接口是类成员变量,而navigation呢就是BottomNavigationView啦。在接口中可以看到,通过判断点击的item的id给TextView设置了不同的文字。

下面问题来了,介绍了OnNavigationItemSelectedListener接口,那么OnNavigationItemReselectedListener接口又是干什么的呢?当时也是看了半天文档没想明白,不过没关系,做个实验就知道了。我给navigation又添加了OnNavigationItemReselectedListener接口,代码如下所示:

private BottomNavigationView.OnNavigationItemReselectedListener itemReselectedListener=new BottomNavigationView.OnNavigationItemReselectedListener() {        @Override        public void onNavigationItemReselected(@NonNull MenuItem item) {            String msg=null;            switch (item.getItemId()) {                case R.id.navigation_home:                    msg="home";                    break;                case R.id.navigation_dashboard:                    msg="dashboard";                    break;                case R.id.navigation_notifications:                    msg="notifications";                    break;            }            Toast.makeText(MainActivity.this, "item "+msg+" is reselected", Toast.LENGTH_SHORT).show();        }    };
navigation.setOnNavigationItemReselectedListener(itemReselectedListener);

下面来看看实验效果吧:

这里写图片描述

很明显吧,再次点击已选中的item就会触发setOnNavigationItemReselectedListener,那么这玩意有什么用呢?当时马上想到了新浪微博,相信各位都有一个已经习以为常的操作,就是点击home button刷新微博。剩下的不用多说了,嘿嘿。。。

在生活中更常见到的是点击底部导航栏的图标,上面的界面随之滑动切换。这里并没有实现这样的效果,但是思路也并不难,在处理点击事件的接口中设置viewPager的currentItemId就好了。但是还要注意一个地方,就是在用户手动滑动viewPager时底部导航栏的选中图标也该随之切换。是不是感觉有点麻烦,嗯。在顶部导航栏控件TabLayout中有这样一个方法,如下所示:

这里写图片描述

这个方法就是专门用来设置TabLayout与ViewPager协同工作的。在用户手动滑动ViewPager时TabLayout中的选中项也会随之切换,甚至还有动画。讲道理BottomNavigationView中也应该提供这样的方法,因为这些导航控件大多数时候都是要和ViewPager一起工作的,然而目前BottomNavigatoinView中并没有这样的方法,不过我相信未来会有的。

底部导航栏的基本使用大概就这些,下面开始说顶部导航栏。

顶部导航栏的使用

在顶部导航栏的例子中,是让TabLayout和ViewPager协同工作的。首先在布局文件中添加这两个控件就非常简单啦,代码如下所示:

<android.support.design.widget.TabLayout        android:id="@+id/tab_top"        android:layout_width="match_parent"        android:layout_height="wrap_content"></android.support.design.widget.TabLayout>    <android.support.v4.view.ViewPager        android:id="@+id/viewpager"        android:layout_width="match_parent"        android:layout_height="match_parent">    </android.support.v4.view.ViewPager>

官方文档中还提供了另外一种写法,如下所示:

<android.support.v4.view.ViewPager     android:layout_width="match_parent"     android:layout_height="match_parent">     <android.support.design.widget.TabLayout         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_gravity="top" /> </android.support.v4.view.ViewPager>

下面开始说在代码中的使用,关于ViewPager及其Fragment适配器和Fragment的创建这里就不说了,我们将重点放在TabLayout的使用上。TabLayout的使用也很简单,如下所示,注意看注释:

//获取TabLayout实例tabTop=(TabLayout)findViewById(R.id.tab_top);//设置TabLayout中tabs的行为模式tabTop.setTabMode(TabLayout.MODE_FIXED);//设置协同工作的ViewPagertabTop.setupWithViewPager(viewPager);

以上并称TabLayout使用三部曲,是不是很简单。那么TabLayout的标题是从哪来的呢?这一点可以从官方文档中得到答案,TabLayout会自动从ViewPager的适配器获取标题,在FragmentPagerAdapter中有这样一个方法,如下图所示:

这里写图片描述

实际上这个方法继承自PagerAdapter,TabLayout正是通过这个方法来获取每一个tab的标题,在本例中,FragmentPagerAdapter中的getPageTitle方法是这样写的:

@Override    public CharSequence getPageTitle(int position) {        return "title"+(position+1);    }

所以就有了示例顶部导航栏中每一项的标题。还有一个问题,那就是TabLayout中的tabs的行为模式是个什么意思?不急给出答案,做个实验,实验做完自然就有答案了。setTabMode方法的参数可选值只有两个,如下所示:

这里写图片描述

也就是MODE_FIXED和MODE_SCROLLABLE,我们将行为模式设置为MODE_SCROLLABLE,ViewPager适配器的getCount方法返回值改为8,也就是TabLayout中有8个Tab,然后运行一下。其实从参数名字上也能猜出一二,还是看实验结果吧,这样更直观更形象,实验结果请看下图:

这里写图片描述

怎么样,很直观很形象吧。但是改了之后这些tab变小了啊,没关系,在布局中可以给TabLayout指定app:tabMinWidth属性来规定tab的最小宽度,这里没有指定tab的最小宽度,使用的是默认最小宽度。还有很多属性可以去看官方文档。总之,如果需要很多tab的话就使用MODE_SCROLLABLE并为tab设置合适的宽度,使用MODE_FIXED模式的话不管有多少tab,这些tab都会蜷缩在一屏内并平分屏幕的宽度,这样如果tab很多就会很挤而且使用也不方便,一般来说tab在5个以内可以使用MODE_FIXED,再多就改用MODE_SCROLLABLE。

OK,TabLayout的基本使用方法大概也就这些。

如果将顶部导航栏和底部导航栏结合到一起,且都使用ViewPager的话,需要考虑的一个严重问题就是滑动冲突。

好了,今天就记录到这里。