tabhost基本用法(一)

TabHost基本用法(一)

接下来将会介绍两种TabHost的基本使用,虽然现在已经很少用(被废弃),但是要学习的话还是要认真从最基础的开始研究。

一、TabHost的组成

TabHosat组件包含两部分:
1. TabWidget 其中tabwidget就是选项卡部分,有图标的部分,按下就可以跳到响应的页面。
2. FrameLayout
而FrameLayout就是页面内容部分,显示内容数据。

二、TabHost的xml用法(静态加载)

xml布局:

<RelativeLayout 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"
    tools:context="${relativePackage}.${activityClass}" >

   <TabHost 
       android:id="@+id/myFirst_TabHost"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       >
       <LinearLayout 
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:orientation="vertical"
           >
           <!-- 切换栏 -->
           <!-- 系统默认的id,不然会报错 -->
           <TabWidget 
               android:id="@android:id/tabs"   
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               >
           <!-- 内容栏 -->
           <FrameLayout 
               android:id="@android:id/tabcontent"
               android:layout_width="match_parent"
               android:layout_height="0dp"
               android:layout_weight="1"
               >
              <!-- 第一个layout --> 
              <LinearLayout 
                  android:id="@+id/tab1"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                   android:gravity="center"
                  >
                 <TextView 
                     android:text="我是第一个"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:gravity="center"
                     /> 
              </LinearLayout>
              <!-- 第二个layout -->
                            <LinearLayout 
                  android:id="@+id/tab2"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                   android:gravity="center"
                  >
                 <TextView 
                     android:text="我是第二个"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:gravity="center"
                     /> 
              </LinearLayout>
              <!-- 第三个layout -->
                            <LinearLayout 
                  android:id="@+id/tab3"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:gravity="center"
                  >
                 <TextView 
                     android:text="我是第三个"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:gravity="center"
                     /> 
              </LinearLayout>
           </FrameLayout>
           </TabWidget>
       </LinearLayout>
   </TabHost>
</RelativeLayout>

可以看到Tabhost布局是有一个LinearLayout布局,其中有一个TabWidget选项卡部件布局,FrameLayout内容区布局。其中FrameLayout有三个linearlayout线性布局。 注意:tabwidget的id必须是系统的id tabs,framelayout的id也必须是系统的id tabcontent,因为其内部帮我们初始化了这些控件。不然会报错。

public class MainActivity extends Activity implements OnTabChangeListener {
    private TabHost th;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        th=(TabHost) findViewById(R.id.myFirst_TabHost);
        //Call setup() before adding tabs if loading TabHost using findViewById(). 
        //However: You do not need to call setup() after getTabHost() in TabActivity. 
        th.setup();
        //4.1之后getResources().getDrawble(id)方法已废弃,使用Context.getDrawble(id)或者getResources().getDrawble(int id,int Theme);
        th.addTab(th.newTabSpec("tab1").setIndicator("first page").setContent(R.id.tab1));
        th.addTab(th.newTabSpec("tab2").setIndicator("second page")).setContent(R.id.tab2));
        th.addTab(th.newTabSpec("tab3").setIndicator("third page").setContent(R.id.tab3));

        TabWidget tw=th.getTabWidget();
        //分割线去掉,自定义view时没有
        tw.setDividerDrawable(null);
    }

在以上代码中首先得到tabhost的对象,然后调用==setup==()方法初始化,然后再调用addTab()方法来进行添加内容。

  • 首先必须调用setup()方法,其内部是进行tabwidget和FrameLayout的初始化。源码如下:
    public void setup() {
        mTabWidget = (TabWidget) findViewById(com.android.internal.R.id.tabs);
        if (mTabWidget == null) {
            throw new RuntimeException(
                    "Your TabHost must have a TabWidget whose id attribute is 'android.R.id.tabs'");
        }
   .     .......
        mTabContent = (FrameLayout) findViewById(com.android.internal.R.id.tabcontent);
        if (mTabContent == null) {
            throw new RuntimeException(
                    "Your TabHost must have a FrameLayout whose id attribute is "
                            + "'android.R.id.tabcontent'");
        }
    }

可以看到在setup中做了TabWidget,TabContent的初始化操作以及其他操作,这里也就明白了为什么xml里为什么要使用系统的id。

  • 其次必须调用addTab方法。
    • 其中参数为TabSpec对象,我们可以通过Tabhost对象调用newTabSpec(String tag)得到,其中tag标签必须唯一,我们后面可以用来查找。
    • 然后通过调用setIndicator()方法来添加我们的指示器。其有三种方法重载setIndicator(CharSequence label),setIndicator(CharSequence label, Drawable icon),setIndicator(View view),分别为添加标签,添加标签和图片,添加自定义的view。
    • setContent()方法来添加内容,其中id为我们xml中的linearlayout中的id。
三、TabHost的动态加载

xml布局:

<RelativeLayout 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"
    tools:context="${relativePackage}.${activityClass}" >

   <TabHost 
       android:id="@+id/myFirst_TabHost"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       >
       <LinearLayout 
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:orientation="vertical"
           >
           <!-- 内容栏 -->
           <FrameLayout 
               android:id="@android:id/tabcontent"
               android:layout_width="match_parent"
               android:layout_height="0dp"
               android:layout_weight="1"
               >

           </FrameLayout>
           <!-- 切换栏 -->
           <!-- 系统默认的id,不然会报错 -->
           <TabWidget 
               android:id="@android:id/tabs"   
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:background="@drawable/tabweiget"
               >
           </TabWidget>
       </LinearLayout>
   </TabHost>
</RelativeLayout>

就是FrameLayout布局里面的内容区域先不填充,后面代码里动态添加,其中内容区域的xml和静态加载一样,只不过布局分开写了。

public class MainActivity extends Activity {
    private TabHost th;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        th=(TabHost) findViewById(R.id.MyTabHost);
        th.setup();//初始化
        LayoutInflater lay=LayoutInflater.from(this);
        lay.inflate(R.layout.tab_content1, th.getTabContentView());
        lay.inflate(R.layout.tab_content2, th.getTabContentView());
        lay.inflate(R.layout.tab_content3, th.getTabContentView());
        //setContent()源码调用的是FrameLayout中的findviewByid 所以要先把布局添加到TabContentView()中去
        th.addTab(th.newTabSpec("tab1").setIndicator("first page").setContent(R.id.tab1));
        th.addTab(th.newTabSpec("tab2").setIndicator("second page").setContent(R.id.tab2));
        th.addTab(th.newTabSpec("tab3").setIndicator("third page").setContent(R.id.tab3));
    }
}

代码中我们也只需要将我们的内容tab_content1.xml使用inflate添加到通过getTabContentView()得到的内容viewgroup中即可。这样才可以再setcontent的时候找到id。因为setcontent的时候,源码中调用mTabcontent.findviewById(id),不把布局添加到内容view中是找不到id的。

运行结果:
这里写图片描述

四、setIndicator问题解析

看到上图的运行结果的标签Indicator是只有一个字符,因为我们用了setIndicator(CharSequence label)。但是当我们想要添加图片时,使用setIndicator(CharSequence label, Drawable icon),但是运行后并无图片,图片不能加载出来。只有将第一个参数label设为null时才可以显示图片,我去网上查原因好像是讲版本问题,于是带着这个问题我去查了一下源码,终于明白了原因。

参数label设为null时:
这里写图片描述


源码分析:

        /**
         * Specify a label and icon as the tab indicator.
         */
        public TabSpec setIndicator(CharSequence label, Drawable icon) {
            mIndicatorStrategy = new LabelAndIconIndicatorStrategy(label, icon);
            return this;
        }
  • setIndicator(CharSequence label, Drawable icon)跟进,可以看到调用了LabelAndIconIndicatorStrategy,传入了label和icon,继续跟进。
  /**
     * How we create a tab indicator that has a label and an icon
     */
    private class LabelAndIconIndicatorStrategy implements IndicatorStrategy {

        private final CharSequence mLabel;
        private final Drawable mIcon;

        private LabelAndIconIndicatorStrategy(CharSequence label, Drawable icon) {
            mLabel = label;
            mIcon = icon;
        }

        public View createIndicatorView() {
            final Context context = getContext();
            LayoutInflater inflater =
                    (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View tabIndicator = inflater.inflate(mTabLayoutId,
                    mTabWidget, // tab widget is the parent
                    false); // no inflate params

            final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
            final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);

  // when icon is gone by default, we're in exclusive mode
            final boolean exclusive = iconView.getVisibility() == View.GONE;
            final boolean bindIcon = !exclusive || TextUtils.isEmpty(mLabel);

            tv.setText(mLabel);

            if (bindIcon && mIcon != null) {
                iconView.setImageDrawable(mIcon);
                iconView.setVisibility(VISIBLE);
            }

            if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
                // Donut apps get old color scheme
                tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
                tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
            }

            return tabIndicator;
        }
}
  • 可以看到这个类实现了IndicatorStrategy接口,用来告诉我们一个Indicator指示器是如何创建的,其中可以看到系统先是实例化了一个Textview和一个ImageView,接着ImageView的icon默认是Gone不可见的,所以exclusive为true。而bindIcon则是由TextUtils.isEmpty(mLabel)来决定的,我们传入的mLabel是null时,为true,反之为false。接着向下看textView直接设置传入的mlable,而ImageView的设置则加了一层判断,bindIcon为true且我们传入的mIcon不为null时才可以设置ImageView的图片并且可见。

  • 至此我们得知,为什么设置图片了却不显示,textview设置为null时,图片才可以显示。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值