想要制作出拥有多个Tab相互切换的界面,需要用到几个主要的组件:

1. android.app.TabActivity;

2. android.widget.TabHost;

3. android.view.LayoutInflater;

4. android.widget.TabWidget

5. android.widget.FrameLayout


本文描述了制作Tabs界面的三种常用方法,参考来自于Android官方文档及ApiDemo。

在此需要特别说明:android.app.TabActivity;在api13已经被视为废弃组件,官方推荐使用android.app.Fragment,但是如果为了兼容低版本设备,则可以使用v4 support library提供的fragment api。

即便如此,预先学习一下旧式的TabActivity,也能更好地学习Fragment。


效果图和示例代码:

1.主界面

104449279.png

activity_main.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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
    <ListView
        android:id="@+id/lstv_demos"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

MainActivity.java

package com.panny.tabsdemo;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
                                                                                                                   
    private ListView lstvDemos;
                                                                                                                   
    private String[] contents = new String[]{
            "Content By Id",
            "Content By Factory",
            "Content By Intent",
            "Scrollable"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_main);
                                                                                                                       
        this.lstvDemos = (ListView) findViewById(R.id.lstv_demos);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                getCurrentContext(),
                android.R.layout.simple_list_item_1,
                contents);
        this.lstvDemos.setAdapter(adapter);
                                                                                                                       
        this.lstvDemos.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                                                                                                                               
                switch (position) {
                case 0:
                    startActivity(new Intent(getCurrentContext(), ContentByIdActivity.class));
                    break;
                case 1:
                    startActivity(new Intent(getCurrentContext(), TabByContentFactoryActivity.class));
                    break;
                case 2:
                    startActivity(new Intent(getCurrentContext(), TabByIntentActivity.class));
                    break;
                case 3:
                    startActivity(new Intent(getCurrentContext(), ScrollableActivity.class));
                    break;
                default:
                    break;
                }
                                                                                                                               
            }
        });
    }
                                                                                                                   
    private MainActivity getCurrentContext() {
        return MainActivity.this;
    }
}

2.Content By Id

104701970.png

ContentByIdActivity.java

package com.panny.tabsdemo;
import android.os.Bundle;
import android.app.TabActivity;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.TabHost;
public class ContentByIdActivity extends TabActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                                                                                                             
        /*
         * TabHost作为标签窗体试图的容器。
         * 这个对象包含两个元素,第一个是一个包含了若干个标签名称的集合,用户通过点击它来切换标签视图;
         * 第二个是一个帧布局对象,用于显示被选中标签的内容。
         * 实际上它们是相互映射,并且相互独立的,但是它们的内容均由TabHost容器控制,而非它们自身。
         */
        TabHost tabHost = getTabHost(); // 如果用户没有自定义id为@android:id/tabhost的<TabHost/>,那么将自动从系统获取。
                                                                                                             
        /*
         * 首先通过系统服务获取已经在当前上下文挂载并且与设备配置好的LayoutInflater实例
         * 这一点必须讲究一下,因为某些文档会直接使用:LayoutInflater.from(this).inflate(...)
         * 虽然这并不影响运行效果,但是可能会有部分性能消耗,即使不是很大,但我们也应该注意。
         */
        LayoutInflater inflter = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        /*
         * LayoutInflater的作用是将layout布局文件转换成为View实例,正如第一个参数所为;
         * 第二个参数的作用是把tabHost.getTabContentView()得到的FrameLayout实例作为root;
         * 第三个参数的作用是将第一个参数的View挂载在root下。
         */
        inflter.from(getCurrentContext()).inflate(R.layout.activity_content_by_id, tabHost.getTabContentView(), true);
                                                                                                             
        /*
         * TabHost实例负责控制tab增减
         * (1)创建一个标签空间,名称为tab01,但它不用于显示, 但它有自己的用处,可参看TabByContentFactoryActivity
         * (2)设置标签显示的名称,或许也可以添加一个背景图
         * (需要注意的是:在添加背景图使,笔者在emulator-api10 和 emulator-api17上的运行效果不同,emulator-api10可以允许同时添加label和drawable,
         * 而emulator-api17需要如此.setIndicator("", getResources().getDrawable(R.drawable.ic_launcher)))
         * (3)设置标签对应的内容
         */
        tabHost.addTab(
                tabHost.newTabSpec("tab01") // (1)
                .setIndicator("one", getResources().getDrawable(R.drawable.ic_launcher))// (2)
                .setContent(R.id.tv_view01)); // (3)
        tabHost.addTab(tabHost.newTabSpec("tab02").setIndicator("two").setContent(R.id.tv_view02));
        tabHost.addTab(tabHost.newTabSpec("tab03").setIndicator("three").setContent(R.id.tv_view03));
                                                                                                             
    }
                                                                                                         
    private ContentByIdActivity getCurrentContext() {
        return ContentByIdActivity.this;
    }
}

activity_content_by_id.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
                                                                                           
    <TextView
        android:id="@+id/tv_view01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="This is the view01" />
                                                                                           
    <TextView
        android:id="@+id/tv_view02"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="This is the view02" />
                                                                                           
    <TextView
        android:id="@+id/tv_view03"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="This is the view03" />
                                                                                           
</FrameLayout>


3.Content By Factory

105149632.png

TabByContentFactoryActivity.java

package com.panny.tabsdemo;
import android.app.TabActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TabHost;
import android.widget.TabHost.TabContentFactory;
import android.widget.TextView;
public class TabByContentFactoryActivity extends TabActivity implements TabContentFactory {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                                                                                      
        TabHost tabHost = getTabHost();
        tabHost.addTab(
                tabHost.newTabSpec("tab01")
                .setIndicator("one")
                .setContent(this));
                                                                                      
        tabHost.addTab(
                tabHost.newTabSpec("tab02")
                .setIndicator("two")
                .setContent(this));
                                                                                      
        tabHost.addTab(
                tabHost.newTabSpec("tab03")
                .setIndicator("three")
                .setContent(this));
    }
    @Override
    public View createTabContent(String tag) {
        TextView tv = new TextView(this);
        tv.setText("This is the " + tag); // 这个参数来自于:tabHost.newTabSpec("tab01")
        return tv;
    }
}


4.Content By Intent

104913302.png

TabByIntentActivity.java

package com.panny.tabsdemo;
import android.app.TabActivity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TabHost;
public class TabByIntentActivity extends TabActivity {
                                                                        
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                                                                            
        TabHost tabHost = getTabHost();
                                                                            
        tabHost.addTab(
                tabHost.newTabSpec("list")
                .setIndicator("list")
                .setContent(new Intent(this, Cities.class)));
                                                                            
        tabHost.addTab(
                tabHost.newTabSpec("photos")
                .setIndicator("photos")
                .setContent(new Intent(this, Phones.class)));
                                                                            
    }
}

Cities.java

package com.panny.tabsdemo;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
public class Cities extends ListActivity {
                                                            
    private String[] cities = new String[]{
            "ShangHai", "BeiJin", "GuangZhou",
            "ChongQing", "WuHan", "FuJian",
            "XiAn", "HeiLongJiang", "QingDao"
    };
                                                            
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                                                                
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, cities);
        getListView().setAdapter(adapter);
    }
}

104916876.png

Phones.java

package com.panny.tabsdemo;
import java.util.ArrayList;
import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
public class Phones extends ListActivity {
                                                   
    private Button btnAdd;
    private Button btnClear;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.photos_list);
                                                       
        this.btnAdd = (Button) findViewById(R.id.btn_add_photo);
        this.btnClear = (Button) findViewById(R.id.btn_clear_photos);
                                                       
        // 设置当listview为空时,应该显示什么视图,在此显示预先定义的TextView
        getListView().setEmptyView(findViewById(R.id.tv_empty));
                                                       
        final PhotoAdapter adapter = new PhotoAdapter(this);
        setListAdapter(adapter);
                                                       
        this.btnAdd.setOnClickListener(new View.OnClickListener() {
                                                           
            @Override
            public void onClick(View v) {
                adapter.add();
                                                               
            }
        });
        this.btnClear.setOnClickListener(new View.OnClickListener() {
                                                           
            @Override
            public void onClick(View v) {
                adapter.clear();
                                                               
            }
        });
                                                       
    }
                                                   
    private class PhotoAdapter extends BaseAdapter {
                                                       
        private Context mContext;
        private int[] photosPool = new int[]{
                R.drawable.sample_1, R.drawable.sample_2, R.drawable.sample_3,
                R.drawable.sample_4, R.drawable.sample_5, R.drawable.sample_6,
                R.drawable.sample_7
        };
        // 为了动态添加图片,它才是adapter真正的内容提供者
        private ArrayList<Integer> photos = new ArrayList<Integer>();
                                                       
        public PhotoAdapter(Context mContext) {
            this.mContext = mContext;
        }
        @Override
        public int getCount() {
            return photos.size();
        }
        @Override
        public Object getItem(int position) {
            return photos.get(position);
        }
        @Override
        public long getItemId(int position) {
            return position;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView i = new ImageView(mContext);
                                                           
            i.setImageResource(photos.get(position));
            i.setAdjustViewBounds(true);
            i.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            i.setBackgroundResource(R.drawable.picture_frame);
            return i;
        }
                                                       
        private void add() {
            int wicthPic = (int) Math.round(Math.random() * (photosPool.length - 1));
            photos.add(photosPool[wicthPic]);
            notifyDataSetChanged();
        }
                                                       
        private void clear() {
            photos.clear();
            notifyDataSetChanged();
        }
    }
                                                   
                                                   
}

R.layout.photos_list.xml

<?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" >
                                             
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
                                                 
        <Button
            android:id="@+id/btn_add_photo"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Add Photo"/>
                                                 
        <Button
            android:id="@+id/btn_clear_photos"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Clear Photos"/>
                                                 
    </LinearLayout>
                                             
    <!-- The frame layout is here since we will do showing either
    the list view or the text view when the list view is empty.  -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
                                                 
        <!-- Here is the list. Since we are using a ListActivity, we
             have to identity it as "@android:id/list" so ListActivity will
             find it -->
        <ListView
            android:id="@android:id/list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:drawSelectorOnTop="false"/>
                                                 
        <!-- Here is the view to show if the list is emtpy -->
        <TextView
            android:id="@+id/tv_empty"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="No photos" />
                                                 
    </FrameLayout>
</LinearLayout>


4.Scrollable

105319748.png

ScrollableActivity.java

package com.panny.tabsdemo;
import android.app.TabActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TabHost;
import android.widget.TabHost.TabContentFactory;
import android.widget.TextView;
public class ScrollableActivity extends TabActivity implements TabContentFactory {
                                     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tab_scroll); // 不要忘记加载自定义的布局文件
                                         
        TabHost tabHost = getTabHost();
                                         
        for(int i = 0; i < 30; i ++) {
            tabHost.addTab(
                    tabHost.newTabSpec("tab" + i)
                    .setIndicator("tab_" + i)
                    .setContent(this));
        }
    }
    @Override
    public View createTabContent(String tag) {
        TextView tv = new TextView(this);
        tv.setText("This is " + tag);
        return tv;
    }
}

R.layout.tab_scroll.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- Here is the root and it must be identified as @android:id/tabhost
     so that it can be found by getTabHost()-->
<TabHost
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@android:id/tabhost" >
                               
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="5dip">
                                   
        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="none">
                                       
            <!-- Here is the tab label container and it must be identified as @android:id/tabs
                 so that it can be found by tabhost when addTab() -->
            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_horizontal|center_vertical" />
                                       
        </HorizontalScrollView>
        <!-- Here is the tab content container and it must be indentified as @android:id/tabcontent
             so that it could be found by tabhost when setContent() -->
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="5dip" />
                                   
    </LinearLayout>
                               
</TabHost>


Best Regards,