FragmentTabHost FrameLayout实现底部导航栏
app经常用到底部导航栏,早前使用过radiogroup+framelayout实现或者radiogroup+viewpager实现,现在基本使用fragmenttabhost+framelayout来实现,因为使用起来简单易用。下面写一个小例子简要地总结一下这个组合。
首先,看一下例子的最终运行效果图
这5个图标的效果其实都是一样的,只要做出来一个,以此类推就可以写出其他几个
第一步, fragmenttabhost+framelayout布局,先看一下代码:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <framelayout android:id="@+id/realtabcontent" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:background="@color/bg_color"/> <android.support.v4.app.fragmenttabhost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white"> <framelayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="0" /> </android.support.v4.app.fragmenttabhost> </linearlayout>
布局大体分为两部分,上面的framelayout代表是显示内容部分,下面的fragmenttabhost代表是导航栏部分。注意: fragmenttabhost的id和其内部的framelayout的id必须是系统的id。
第二步, fragmenttabhost+framelayout代码实现连接,fragmenttabhost使用,可以记住三个步骤:(1)setup(…)可以理解为,初始化底部导航和内容页面连接,(2)新建tabspec可以理解为初始化底部菜单项,(3)addtab(…)可以理解为把菜单和内容添加到控件中。下面看一下代码:
public class mainactivity extends appcompatactivity { private layoutinflater minflater; private fragmenttabhost mtabhost; private arraylist<tabdatabean> tabdatalist = new arraylist<>(5); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); minflater = layoutinflater.from(this); mtabhost = (fragmenttabhost) this.findviewbyid(android.r.id.tabhost); //第一步,初始化ftabhost, 第三个参数为内容容器 mtabhost.setup(this, getsupportfragmentmanager(), r.id.realtabcontent); //第二步,初始化菜单项 tabhost.tabspec tabspec = mtabhost.newtabspec("主页"); /*添加菜单项布局*/ view view = minflater.inflate(r.layout.tab_indicator, null); imageview icontab = (imageview) view.findviewbyid(r.id.iv_tab_icon); textview tvtab = (textview) view.findviewbyid(r.id.tv_tab_text); icontab.setimageresource(r.drawable.tab_home_normal); tvtab.settext("主页"); tabspec.setindicator(view); //第三步,添加菜单项和内容 mtabhost.addtab(tabspec, homefragment.class, null); // inittabhost(); } }
其中涉及了菜单项布局tab_indicator.xml,内容页布局homefragment.java文件,代码如下:
tab_indicator.xml
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingtop="3dp" android:paddingbottom="3dp" android:layout_gravity="center" android:gravity="center"> <imageview android:id="@+id/iv_tab_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <textview android:id="@+id/tv_tab_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textcolor="@color/tabtextcolor" android:layout_margintop="2dp"/> </linearlayout>
homefragment.java
public class homefragment extends fragment { @nullable @override public view oncreateview(layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { return inflater.inflate(r.layout.fragment_home, null); } @override public void onactivitycreated(@nullable bundle savedinstancestate) { super.onactivitycreated(savedinstancestate); toast.maketext(getcontext(), r.string.tabhome, toast.length_short).show(); } }
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <textview android:layout_width="wrap_content" android:layout_height="wrap_content" android:textcolor="@color/themecolor" android:text="@string/tabhome"/> </linearlayout>
写完如上代码的运行效果如下:
可以看到一个菜单项已经显示出来,照葫芦画瓢,我们就可以吧其他四个菜单项写出来,既然其他四项和这个代码雷同,所以说肯定有公共部分可以抽取出来,减少代码量和代码整洁度。我们发现,有三个变量随着菜单变化的,如:菜单图标,菜单名称,菜单对应的内容。所以我们写一个类封装一下,代码如下:
tabdatabean.java
public class tabdatabean { private int tabname; private int tabicon; private class content; //对应的内容类 public tabdatabean(int tabname, int tabicon, class content) { this.tabname = tabname; this.tabicon = tabicon; this.content = content; } public int gettabname() { return tabname; } public void settabname(int tabname) { this.tabname = tabname; } public int gettabicon() { return tabicon; } public void settabicon(int tabicon) { this.tabicon = tabicon; } public class getcontent() { return content; } public void setcontent(class content) { this.content = content; } }
有了这个实体类,我们就可以把上面的第一步和第二步骤抽取出来封装一下了,代码如下:
private void inittabhost() { //初始化ftabhost, 第三个参数为内容容器 mtabhost.setup(this, getsupportfragmentmanager(), r.id.realtabcontent); /*初始化数据源*/ tabdatabean bean = new tabdatabean(r.string.tabhome, r.drawable.tab_home_normal, homefragment.class); //添加底部菜单项-tabspec tabhost.tabspec tabspec = mtabhost.newtabspec(getstring(bean.gettabname())); //给菜单项添加内容,indicator,其中indicator需要的参数view即为菜单项的布局 tabspec.setindicator(getindicatorview(bean)); //第二参数就是该菜单项对应的页面内容 mtabhost.addtab(tabspec, bean.getcontent(), null) } private view getindicatorview(tabdatabean bean){ view view = minflater.inflate(r.layout.tab_indicator, null); imageview icontab = (imageview) view.findviewbyid(r.id.iv_tab_icon); textview tvtab = (textview) view.findviewbyid(r.id.tv_tab_text); icontab.setimageresource(bean.gettabicon()); tvtab.settext(bean.gettabname()); return view; }
把其他四项添加入后,代码如下:
public class mainactivity extends appcompatactivity { private layoutinflater minflater; private fragmenttabhost mtabhost; private arraylist<tabdatabean> tabdatalist = new arraylist<>(5); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); minflater = layoutinflater.from(this); mtabhost = (fragmenttabhost) this.findviewbyid(android.r.id.tabhost); inittabhost(); } /** * 初始化底部导航栏 */ private void inittabhost() { //初始化ftabhost, 第三个参数为内容容器 mtabhost.setup(this, getsupportfragmentmanager(), r.id.realtabcontent); /*初始化数据源*/ tabdatabean tabhome = new tabdatabean(r.string.tabhome, r.drawable.tab_home_normal, homefragment.class); tabdatabean tabhot = new tabdatabean(r.string.tabhot, r.drawable.tab_life_normal, hotfragment.class); tabdatabean tabcategory = new tabdatabean(r.string.tabcategory, r.drawable.tab_service_normal, categoryfragment.class); tabdatabean tabcart = new tabdatabean(r.string.tabcart, r.drawable.tab_order_normal, cartfragment.class); tabdatabean tabmine = new tabdatabean(r.string.tabmine, r.drawable.tab_mine_normal, minefragment.class); tabdatalist.add(tabhome); tabdatalist.add(tabhot); tabdatalist.add(tabcategory); tabdatalist.add(tabcart); tabdatalist.add(tabmine); //添加底部菜单项-tabspec for (tabdatabean bean : tabdatalist) { tabhost.tabspec tabspec = mtabhost.newtabspec(getstring(bean.gettabname())); //给菜单项添加内容,indicator,其中indicator需要的参数view即为菜单项的布局 tabspec.setindicator(getindicatorview(bean)); //第二参数就是该菜单项对应的页面内容 mtabhost.addtab(tabspec, bean.getcontent(), null); } } /** * 初始化indciator的内容 * @param bean */ private view getindicatorview(tabdatabean bean){ view view = minflater.inflate(r.layout.tab_indicator, null); imageview icontab = (imageview) view.findviewbyid(r.id.iv_tab_icon); textview tvtab = (textview) view.findviewbyid(r.id.tv_tab_text); icontab.setimageresource(bean.gettabicon()); tvtab.settext(bean.gettabname()); return view; } }
运行效果如下:
如上结果,已经离我们的目标很近了。
第三步,给图标和文字添加变色selector
首先,给图标变色,在drawable文件夹下新建selector_tab_home.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true" android:drawable="@drawable/tab_home_selected"/> <item android:state_pressed="true" android:drawable="@drawable/tab_home_selected"/> <item android:drawable="@drawable/tab_home_normal"/> </selector>
接下来把
tabdatabean tabhome = new tabdatabean(r.string.tabhome, r.drawable.tab_home_normal, homefragment.class); 改为
tabdatabean tabhome = new tabdatabean(r.string.tabhome, r.drawable.selector_tab_home, homefragment.class);
以此类推,剩下的四项也是如此处理
然后,菜单名称变色,如果在res文件夹下没有color资源文件夹,新建color资源文件夹,然后在color文件夹下新建selector_tab_text.xml文件,代码如下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="@color/themecolor" android:state_selected="true"/> <item android:color="@color/themecolor" android:state_active="true"/> <item android:color="@color/tabtextcolor" android:state_selected="false"/> <item android:color="@color/tabtextcolor" android:state_active="false"/> </selector>
接下来把tab_indicator.xml文件中textview的android:textcolor="@color/tabtextcolor" 修改为
android:textcolor="@color/selector_tab_text"
最后运行一下就和文章开头的运行效果一致了,有疑问或者是文章有不对的地方欢迎评论和指正^_^。
问题: 我们在每个fragment的onactivitycreated(…)方法中都写了
toast.maketext(getcontext(), r.string.tabhome, toast.length_short).show();
运行程序,你会发现,无论是第一次点击还是再次进入此菜单项时,都会弹出toast对话框。如果我们在每个页面中都写入了网络请求,相当于每次进入都会进行一次请求。但是项目需求只要求我们第一进入该页面时请求,所以我们应该如何处理呢?有几种处理方式,大家可以思考一下,下一篇文章,我们重写fragmenttabhost来处理这个问题。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
FragmentTabHost FrameLayout实现底部导航栏
-
Android自定义ViewPagerIndicator实现炫酷导航栏指示器(ViewPager+Fragment)
-
iOS实现知乎和途家导航栏渐变的文字动画效果
-
android底部菜单栏实现原理与代码
-
ANDROID BottomNavigationBar底部导航栏的实现示例
-
详解Vue底部导航栏组件
-
Android仿UC底部菜单栏实现原理与代码
-
Android实现顶部导航栏可点击可滑动效果(仿微信仿豆瓣网)
-
Android实现简单底部导航栏 Android仿微信滑动切换效果
-
Android 状态栏虚拟导航键透明效果的实现方法