欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

FragmentTabHost FrameLayout实现底部导航栏

程序员文章站 2023-12-10 17:57:52
app经常用到底部导航栏,早前使用过radiogroup+framelayout实现或者radiogroup+viewpager实现,现在基本使用fragmenttabho...

app经常用到底部导航栏,早前使用过radiogroup+framelayout实现或者radiogroup+viewpager实现,现在基本使用fragmenttabhost+framelayout来实现,因为使用起来简单易用。下面写一个小例子简要地总结一下这个组合。

首先,看一下例子的最终运行效果图

FragmentTabHost FrameLayout实现底部导航栏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>

写完如上代码的运行效果如下:

FragmentTabHost FrameLayout实现底部导航栏

可以看到一个菜单项已经显示出来,照葫芦画瓢,我们就可以吧其他四个菜单项写出来,既然其他四项和这个代码雷同,所以说肯定有公共部分可以抽取出来,减少代码量和代码整洁度。我们发现,有三个变量随着菜单变化的,如:菜单图标,菜单名称,菜单对应的内容。所以我们写一个类封装一下,代码如下:

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;
 }

}

运行效果如下:

FragmentTabHost FrameLayout实现底部导航栏FragmentTabHost FrameLayout实现底部导航栏

如上结果,已经离我们的目标很近了。

第三步,给图标和文字添加变色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来处理这个问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。