自从Android3.0引入Fargment之后,在Activity中使用底部导航进行Fragment的切换已经越来越普遍,或者可以说已经成为了移动应用的标配,而本篇文章我总结了项目中常用的几种实现导航的方式,分别是RadioGroup、Tablayout、RadioGroup+反射和FragmentTabHost四种实现方式,包含底部和顶部的双导航界面的实现,实现的结果类似下图所示:
Github下载地址:https://github.com/huohaoliz/BottomNavigation
一、使用RadioGroup+Fragment实现底部导航,使用TabLayout+Fragment实现顶部导航
1,RadioGroup+Fragment的形式是之前开发中比较受欢迎,使用比较多的一种实现形式,所以把它排到第一位。好了不扯了,直接代码走起:
首先,在radiogroup.xml中的布局文件是:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.linktestproject.RadioGroupActivity"> <FrameLayout android:id="@+id/fl_radio_show" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" ></FrameLayout> <RadioGroup android:id="@+id/rg_radio_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="bottom"> <RadioButton android:id="@+id/rb_radio_homepage" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:drawableTop="@drawable/homepage" android:text="@string/homepage" android:button="@null" android:drawablePadding="5dp" android:textColor="@drawable/radio_button_selector" /> <RadioButton android:id="@+id/rb_radio_subscription" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:drawableTop="@drawable/subscription" android:text="@string/subscription" android:button="@null" android:drawablePadding="5dp" android:textColor="@drawable/radio_button_selector" /> <RadioButton android:id="@+id/rb_radio_find" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:drawableTop="@drawable/find" android:text="@string/find" android:button="@null" android:drawablePadding="5dp" android:textColor="@drawable/radio_button_selector" /> <RadioButton android:id="@+id/rb_radio_mine" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:drawableTop="@drawable/mine" android:text="@string/mine" android:button="@null" android:drawablePadding="5dp" android:textColor="@drawable/radio_button_selector" /> </RadioGroup></LinearLayout>
我们需要对每个RadioButton的图片资源做一个选择器,在drawable文件夹下添加四个选择器,内容是(这是首页的图片选择器,其他与此类似就不贴代码了):
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:drawable="@mipmap/tab4_down"></item> <item android:state_checked="false" android:drawable="@mipmap/tab4"></item></selector>之后我们还要对选中的RadioButton添加字体颜色的选择器:
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true" android:color="@color/textChecked"></item> <item android:state_checked="false" android:color="@color/textUnChecked"></item></selector>之后进入Actiivity中,实现代码:
public class RadioGroupActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener { private RadioGroup mRadioGroup; private Fragment[] mFragments; private FrameLayout mLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_radio_group); getSupportActionBar().hide(); initView(); initFragment(); setListener(); } private void setListener() { //对RadioGroup设置监听事件(监听点击选择) mRadioGroup.setOnCheckedChangeListener(this); } private void initFragment() { //初始化要显示的Fragment数组 mFragments=new Fragment[4]; mFragments[0]=new HomepageFragment(); mFragments[1]=new SubscriptionFragment(); mFragments[2]=new FindFragment(); mFragments[3]=new MineFragment(); //获取Fragment管理器 FragmentManager manager=getSupportFragmentManager(); //获取事物(使用v4包下) FragmentTransaction transaction=manager.beginTransaction(); //默认选中HomepageFragment替换Framelayout transaction.replace(R.id.fl_radio_show,mFragments[0]); //提交事物 transaction.commit(); //默认点击首页 mRadioGroup.check(R.id.rb_radio_homepage); } private void initView() { mRadioGroup= (RadioGroup) findViewById(R.id.rg_radio_navigation); mLayout= (FrameLayout) findViewById(R.id.fl_radio_show); } @Override public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) { //写法与默认点击页面的相同 FragmentManager manager=getSupportFragmentManager(); FragmentTransaction transaction=manager.beginTransaction(); switch(checkedId){ case R.id.rb_radio_homepage: transaction.replace(R.id.fl_radio_show,mFragments[0]); break; case R.id.rb_radio_subscription: transaction.replace(R.id.fl_radio_show,mFragments[1]); break; case R.id.rb_radio_find: transaction.replace(R.id.fl_radio_show,mFragments[2]); break; case R.id.rb_radio_mine: transaction.replace(R.id.fl_radio_show,mFragments[3]); break; } transaction.commit(); }}到此底部导航就已经完成,实现效果如下图所示:
2,在HomepageFragment中使用Tablayout+Fragment实现顶部导航
首先Tablayout是Android5.0发布的Design包中的组件,所以我们在使用之前必须加入Design包的依赖(这里不会就自己百度吧)。然后在Homepage的资源文件中的布局如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" xmlns:app="http://schemas.android.com/apk/res-auto" tools:context="com.example.linktestproject.fragments.HomepageFragment"> <android.support.design.widget.TabLayout android:id="@+id/tl_homepage_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabIndicatorColor="@color/colorPrimary" app:tabSelectedTextColor="@color/textChecked" app:tabTextColor="@color/textUnChecked" > </android.support.design.widget.TabLayout> <android.support.v4.view.ViewPager android:id="@+id/vp_homepage_show" android:layout_width="match_parent" android:layout_height="match_parent" > </android.support.v4.view.ViewPager></LinearLayout> Tablayout的属性(此处只是给出用到的三个属性,其他常用的属性会在Tablayout+Fragment中给出):
app:tabIndicatorColor="@color/colorPrimary" //下边指示横线的颜色 app:tabSelectedTextColor="@color/textChecked" //Text选中的文字颜色 app:tabTextColor="@color/textUnChecked" //没有选中的文字颜色在HomepageFragment中的代码是:
public class HomepageFragment extends Fragment { private ViewPager mViewPager; private TabLayout mTabLayout; private List<Fragment>mFragments; private List<String>mTitles; private HomepageAdapter mAdapter; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view=inflater.inflate(R.layout.fragment_homepage, container, false); initView(view); initData(view); setData(); return view; } private void setData() { mViewPager.setAdapter(mAdapter); //设置Viewpager和Tablayout进行联动 mTabLayout.setupWithViewPager(mViewPager);// //将标题设置可以左右摇动而不是移动// mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);// //设置预加载页数// mViewPager.setOffscreenPageLimit(3); } private void initData(View view) { //初始化导航标题,如果是title在json数据中,在初始化的时候可以使用异步任务加载的形式添加 mTitles=new ArrayList<>(); mTitles.add("热门"); mTitles.add("分类"); mTitles.add("榜单"); //初始化Fragment mFragments=new ArrayList<>(); for (int i = 0; i <mTitles.size() ; i++) { if(i==0){ mFragments.add(new HotFragment()); }else if(i==1){ mFragments.add(new ClassifyFragment()); }else if(i==2){ mFragments.add(new ListingFragment()); } } //getSupportFragmentManager()是Activity嵌套fragment时使用 //getChildFragmentManager()是Fragment嵌套Fragment时使用 mAdapter=new HomepageAdapter(getChildFragmentManager(),mFragments,mTitles); mAdapter.notifyDataSetChanged(); } private void initView(View view) { mViewPager= (ViewPager) view.findViewById(R.id.vp_homepage_show); mTabLayout= (TabLayout) view.findViewById(R.id.tl_homepage_navigation); }}用到的Viewpager的Adapter代码是:
public class HomepageAdapter extends FragmentPagerAdapter { private List<Fragment> mFragments; private List<String> mTitles; public HomepageAdapter(FragmentManager fm, List<Fragment> framents, List<String> titles) { super(fm); mFragments = framents; mTitles = titles; } @Override public Fragment getItem(int position) { return mFragments.get(position); } @Override public int getCount() { return mFragments == null ? 0 : mFragments.size(); } @Override public CharSequence getPageTitle(int position) { return mTitles.get(position); }}最后所实现的效果如下所示:
二、使用Tablayout+Fragment实现底部导航
注意:之后三种实现方式不再实现顶部导航,只实现底部导航,而在第二种方式中将详细介绍Tablyout的用法及属性。
1,首先我们先总结一下Tablayout的基本使用(和顶部导航不同的方式实现)
第一种使用先看布局文件:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.linktestproject.TablayoutActivity"> <android.support.design.widget.TabLayout android:id="@+id/tl_tablayout_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" > </android.support.design.widget.TabLayout></android.support.constraint.ConstraintLayout>在代码中:
mTabLayout.addTab(mTabLayout.newTab().setText("Title1")); mTabLayout.addTab(mTabLayout.newTab().setText("Title2")); mTabLayout.addTab(mTabLayout.newTab().setText("Title3")); mTabLayout.addTab(mTabLayout.newTab().setText("Title4"));第二种使用方式(布局):
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.linktestproject.TablayoutActivity"> <android.support.design.widget.TabLayout android:id="@+id/tl_tablayout_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" > <android.support.design.widget.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Title1"/> <android.support.design.widget.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Title2"/> <android.support.design.widget.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Title3"/> <android.support.design.widget.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Title4"/> </android.support.design.widget.TabLayout></android.support.constraint.ConstraintLayout>二种方式实现的结果相同,如下图所示:
下边我们总结一下Tablayout的属性用法:
app:tabSelectedTextColor=" " //改变选中字体的颜色
app:tabTextColor=" " //改变未选中字体的颜色
app:tabIndicatorColor=" " //改变指示器下标的颜色
app:tabBackground=" " //改变整个TabLayout的颜色
app:tabTextAppearance=" " //改变Tablayout的内部字体大小
app:tabPadding="xxdp" //内部子控件的Padding值
app:paddingEnd="xxdp" //设置整个Tablayout的Padding值
app:paddingStart="xxdp"
app:tabMaxWidth="xxdp" //设置最小和最大的tab宽度
app:tabMinWidth="xxdp"
app:tabContentStart="xxdp" //设置Tablayout开始位置的偏移量
tabLayout.addTab(tabLayout.newTab().setText("Tab 1").setIcon(R.mipmap.ic_launcher));//为Tablayout添加图片
app:tabMode=“scrollable” //这个属性用于tab比较多的情况下,实现的结果如下图:
app:tabIndicatorHeight=" "//改变指示器下标的高度
app:tabIndicatorHeight="0dp"//当设置为0时就会去掉指示器下标
2,通过Tablayout+Fragment实现底部导航
首先布局文件很简单:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.linktestproject.TablayoutActivity"> <android.support.v4.view.ViewPager android:id="@+id/vp_tablayout_show" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" ></android.support.v4.view.ViewPager> <android.support.design.widget.TabLayout android:id="@+id/tl_tablayout_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" style="@style/CustomTablayout" > </android.support.design.widget.TabLayout></LinearLayout> 给Tablayout设置style样式:
<style name="CustomTablayout" parent="Widget.Design.TabLayout"> <item name="tabIndicatorHeight">0dp</item> <item name="tabSelectedTextColor">@color/textChecked</item> <item name="tabTextColor">@color/textUnChecked</item> </style>切换页面使用了ViewPager和Fragment,所以我们的适配器Adapter的内容:
public class TablayoutAdapter extends FragmentPagerAdapter { private List<Fragment>mFragments; private List<String>mTitles; public TablayoutAdapter(FragmentManager fm, List<Fragment>mFragments, List<String>mTitles) { super(fm); this.mFragments=mFragments; this.mTitles=mTitles; } @Override public Fragment getItem(int position) { return mFragments.get(position); } @Override public int getCount() { return mFragments==null?0:mFragments.size(); } @Override public CharSequence getPageTitle(int position) { return mTitles.get(position); }}最后是TablayoutActivity中的代码:
public class TablayoutActivity extends AppCompatActivity { private TabLayout mTabLayout; private ViewPager mViewPager; private List<Fragment>mFragments; private TablayoutAdapter mAdapter; private List<String>mTitles; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tablayout); getSupportActionBar().hide(); initView(); initData(); setData(); setListener(); } private void setListener() { } private void setData() { mViewPager.setAdapter(mAdapter); mTabLayout.setupWithViewPager(mViewPager); for (int i = 0; i <mTabLayout.getTabCount() ; i++) { TabLayout.Tab tab=mTabLayout.getTabAt(i); Drawable drawable=null; switch(i){ case 0: //图片资源我们同样要使用选择器,选择器我们不能使用state_checked属性,而应该使用state_selected属性 drawable=getResources().getDrawable(R.drawable.hometablayout); break; case 1: drawable=getResources().getDrawable(R.drawable.subtablayout); break; case 2: drawable=getResources().getDrawable(R.drawable.findtablayout); break; case 3: drawable=getResources().getDrawable(R.drawable.minetablayout); break; } tab.setIcon(drawable); } } private void initData() { mTitles=new ArrayList<>(); mTitles.add("首页"); mTitles.add("订阅"); mTitles.add("发现"); mTitles.add("我的"); mFragments=new ArrayList<>(); for (int i = 0; i <mTitles.size(); i++) { if(i==0){ mFragments.add(new HomepageFragment()); }else if(i==1){ mFragments.add(new SubscriptionFragment()); }else if(i==2){ mFragments.add(new FindFragment()); }else if(i==3){ mFragments.add(new MineFragment()); } } mAdapter=new TablayoutAdapter(getSupportFragmentManager(),mFragments,mTitles); mAdapter.notifyDataSetChanged(); } private void initView() { mTabLayout= (TabLayout) findViewById(R.id.tl_tablayout_navigation); mViewPager= (ViewPager) findViewById(R.id.vp_tablayout_show); }}
drawable=getResources().getDrawable(R.drawable.hometablayout);//这是一个已经过时的方法,
//现在是最新的方法是传入两个参数,但需要的最低版本有限制(Call requires API level 21 (current min is 15)):drawable=getResources().getDrawable(R.drawable.hometablayout,null);所以自己根据需要自己选择
上边注释的图片选择器要根据控件选择对应的属性,选择器内容如下:
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true" android:drawable="@mipmap/tab4_down"></item> <item android:state_selected="false" android:drawable="@mipmap/tab4"></item></selector>以上就是Tablayout+ViewPager+Fragment实现底部导航的所有内容,实现的结果如下图所示:
本来准备四种方式实现底部导航,但篇幅较大所以把下边两种方式写到下一遍博客中,我在源码中添加各个Fragment的生命周期方法Log,可以自己运行比较这几种实现方式。从我自己的开发经历以及Fragment的生命周期的比较,我推荐使用反射机制实现底部导航,并且反射也是你进阶需要掌握的一个知识点。
源码下载地址(前两种方式):http://download.csdn.net/detail/huohao_blogs/9851738
另两种实现博客地址:http://blog.csdn.net/huohao_blogs/article/details/72725391
四种实现方式源码地址:http://download.csdn.net/detail/huohao_blogs/9852863