Android技术集锦Fragment

FragmentTabHost + Fragment 实现底部菜

2017-03-07  本文已影响1401人  Alex_717

一、实现效果

图一

利用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即可。

最终实现的效果如下:

图二
上一篇下一篇

猜你喜欢

热点阅读