FragmentTabHost + Fragment 实现底部菜单

一、实现效果

图一

利用FragmentTabHost实现底部菜单,在该底部菜单中,包括了5个TabSpec,每个TabSpec中包含了一个View,而View中包含了一个ImageView和一个TextView。而要在TabSpec中显示这个View,就需要同过indicator来设置,eg:TabSpec.setIndicator(view);
二、动手实现

1、首先什么都不用说,先将准备好的5个TabSpec中的图片复制到AS中Android目录下的mipmap的文件夹中xxhdpi的文件夹下。这里需要注意的是,每个TabSpec中应该都包含两个图标,一个是正常状态的图标(灰色的),一个是该TabSpec被选中后的图标(红色的)。另外,为什么把图片放在xxhdpi文件夹下而不是hdpi文件夹下?因为放在xxhdpi文件下的图片显示效果会比较细腻些, 图标也显得小一些。如果是把图片放到hdpi文件夹下显示出来的图标就会比较粗糙,显得比较大一个,显示效果不够好。

2、写布局文件。在布局中需要使用到FragmentTabHost,在Android开发者文档中搜索FragmentTabHost,显示两个,一个是android.support.v4.app.FragmentTabHost,另外一个是android.support.v13.app.FragmentTabHost。这里使用的是v4包下的FragmentTabHost。在使用前,需要添加相关的依赖,这里导入的是com.android.support:support-v4:24.2.0。如果在就可以在actiivity-main布局文件中写布局文件。其中需要注意的是,FragmentTabHost的id是需要使用安卓自带的id,“@android:id/tabhost”,而FragmentTabHost中的FrameLayout也需要使用安卓自带的id,tabcontent。另外一点是,这个文件中有两个FrameLayout,因为实现的是底部菜单,所以在FragmentTabHost中的FrameLayout是设置为0的,而FragmentTabHost真正需要显示的内容是在上面的FrameLayout中进行显示的。布局文件如下所示:

<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"              
              tools:context=".MainActivity">    

   <FrameLayout        
              android:id="@+id/realcontent"        
              android:layout_width="match_parent"        
              android:layout_height="0dp"        
              android:layout_weight="1">    
    </FrameLayout>    

    <android.support.v4.app.FragmentTabHost        
              android:id="@android:id/tabhost"          
              android:layout_width="match_parent"        
              android:layout_height="wrap_content"        
              android:background="#ffffff" >      
 
         <FrameLayout         
               android:id="@android:id/tabcontent"            
               android:layout_width="0dp"            
               android:layout_height="0dp"            
               android:layout_weight="0" >
         </FrameLayout>    
     </android.support.v4.app.FragmentTabHost></LinearLayout>

3、在MainActivity中编写逻辑。首先因为我们是使用FragmentTabHost+Fragment来实现的,所以需要让MainActivity继承FragmentActivity,注意继承的这个FragmentActivity需要实在v4包下的。然后需要定义个FragmentTabHost,mTabHost,然后通过findViewById在视图中找到这个FragmentTabHost。然后需要调用 mTabHost的setup()方法,该方法一般带有3个参数,
mTabHost.setup(this, getSupportFragmentManager(), R.id.realcontent);,其中注意的是第三个参数,是我需要真正使用的FrameLayout的id。该方法的详细介绍可以查阅Android开发者网站。接下来的步骤就是利用mTabHost new一个TabSpec出来。然后利用TabSpec,setIndicator,最后就是讲这个TabSpec加到mTabHost中。
其中setIncator方法中需要一个View,因此需要编写一个tab_indicator的xml文件,根据图一发现,其实只是一个ImageView,然后在ImageView下放一个TextView。编写起来也相对简单。然后回到MainActivity中,利用LayoutInflater,将tab_indicator这个布局文件引入进来,View view = mInflater.inflate(R.layout.tab_indicator,null),然后通过view.findViewById 找到其中的ImageView和TextView,接着就对这两个View进行赋值。
此外,将TabSpec加到TabHost中是调用mTabHost.addTab(tabSpec,HomeFragment.class, null);
由此可以看出,还需要一个HomeFragment类,此外这个类还需要一个布局文件,这里主要是实现底部菜单,因此这个布局文件只是写了个TextView。这个文件和这个代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              
              android:layout_width="match_parent"                      
              android:layout_height="match_parent"              
              android:gravity="center"              
              android:orientation="vertical">   

         <TextView        
              android:layout_width="wrap_content"            
              android:layout_height="wrap_content"        
              android:text="Home"       
              android:textSize="23sp"      
              android:textStyle="bold" />

</LinearLayout>
public class HomeFragment extends Fragment {    
      @Nullable    
      @Override    
       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {       
             return inflater.inflate(R.layout.fragment_home,container,false);  
  }
}

3、对代码进行简单的优化
通过以上步骤就可以在底部菜单中实现一个图标,但是现在是要实现5个图标的菜单。比较简单、冗余的方法是,将刚才Activity中的代码适当的复制4份。但是这样会让代码显得冗余,而且不利于后期的维护,因此这里我们利用面向对象思想,对其中的一些经常使用到的,会发生改变的内容进行封装,将其封装成一个类,然后实例化这个类的对象,再通过调用这类的方法,获取相应的变量。这里我们定义一个Tab类。

public class Tab {   
      private int Image;    
      private int Text;    
      private Class Fragment;    

      public Tab(int image, int text, Class fragment) {        
              Image = image;       
              Text = text;       
              Fragment = fragment;    }    

      public int getImage() {        return Image;    }    
      public void setImage(int image) {        Image = image;    }          
      public int getText() {        return Text;    }    
      public void setText(int text) {        Text = text;    }    
      public Class getFragment() {        return Fragment;    }    
      public void setFragment(Class fragment) {        Fragment = fragment;    }
}

然后修改MainActivity中的内容

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTabHost;
import android.view.LayoutInflater;import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TabHost;
import android.widget.TextView;
import com.lyh.mymalll5.bean.Tab;
import com.lyh.mymalll5.fragment.CartFragment;
import com.lyh.mymalll5.fragment.DiscoverFragment;
import com.lyh.mymalll5.fragment.HomeFragment;
import com.lyh.mymalll5.fragment.HotFragment;
import com.lyh.mymalll5.fragment.UserFragment;
import java.util.ArrayList;

public class MainActivity extends FragmentActivity {    

      private FragmentTabHost mTabHost;    
      private LayoutInflater mInflater;    
      private ArrayList<Tab> mTabs= new ArrayList<Tab>(5);   

 @Override   
      protected void onCreate(Bundle savedInstanceState) {              
          super.onCreate(savedInstanceState);        
          setContentView(R.layout.activity_main);       
          initTab();   
 }    
      
      private void initTab() {        
          //实例化5个Tab类的对象        
          Tab Tab_home = new Tab(R.drawable.selector_home,R.string.home,HomeFragment.class);        
          Tab Tab_hot = new Tab(R.drawable.selector_hot,R.string.hot, HotFragment.class);        
          Tab Tab_discover = new Tab(R.drawable.selector_discover,R.string.discover, DiscoverFragment.class);        
          Tab Tab_cart = new Tab(R.drawable.selector_cart,R.string.cart, CartFragment.class);          
          Tab Tab_user = new Tab(R.drawable.selector_user,R.string.user, UserFragment.class);      

           //将这5个对象加到一个List中        
          mTabs.add(Tab_home);        
          mTabs.add(Tab_hot);        
          mTabs.add(Tab_discover);        
          mTabs.add(Tab_cart);        
          mTabs.add(Tab_user);        

          mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);        
          mTabHost.setup(this, getSupportFragmentManager(), R.id.realcontent);        
          mInflater = LayoutInflater.from(this);        

          //通过循环实例化一个个TabSpec        
          //并调用其中setIndicator方法        
          //然后将TabSpec加到TabHost中        
          for (Tab tab  :mTabs) {            
              TabHost.TabSpec tabSpec = mTabHost.newTabSpec(String.valueOf(tab.getText()));                  
              tabSpec.setIndicator(buildView(tab));            
              mTabHost.addTab(tabSpec,tab.getFragment(), null);        
            }        

           //通过这行代码可以去除掉底部菜单5个图表之间的分割线
           mTabHost.getTabWidget().setShowDividers(LinearLayout.SHOW_DIVIDER_NONE);    }    

          //设置Indicator中的View    
          private View buildView(Tab tab) {        
          View view = mInflater.inflate(R.layout.tab_indicator,null);        
          ImageView Tab_img = (ImageView) view.findViewById(R.id.tab_img);        
          TextView Tab_txt = (TextView) view.findViewById(R.id.tab_txt);        
          
          Tab_img.setBackgroundResource(tab.getImage());        
          Tab_txt.setText(tab.getText());        
          return view;    
        }
      }

4、简单的优化。通过上面步骤后,基本上可以实现底部菜单的功能,但是仅仅如此的话,还是不够全面,因为当点击图标的时候,图标和文字都还不会变成红色。首先设置图标。
在Drawable文件下编写5个xml文件,这5个文件基本上是一样,都是设置成当被点的时候改变图标,使用准备好的红色的图标。这里只给出其中的一个例子。
selector_home.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">    
          //被选中的时候的图标
          <item android:state_selected="true" android:drawable="@mipmap/icon_home_press"/>    
          //正常状态下的图标
          <item android:drawable="@mipmap/icon_home"/>
</selector>

然后在实例化Tab类的对象的时候,在其函数中Image参数栏使用R.drawable.selector_home.xml文件就可。

而文字的该变更为简单,在res目录下,新增一个color文件夹,在该文件夹下增添一个text_color.xml文件,在其中编写被选中时的颜色的变化,这里只需编写一个即可。

<selector xmlns:android="http://schemas.android.com/apk/res/android">    
    //被选中时候为红色    
     <item android:color="#eb4f38" android:state_selected="true"/>    
    //正常情况下为灰色    
     <item android:color="#a9b7b7" android:state_selected="false"/>
</selector>

然后在tab_indicator.xml文件中的TextView控件中声明textColor属性为@color/text_color即可。

最终实现的效果如下:

图二
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容