1. 基于平台版本的标签应用
2. 把视图设置为标签内容
3. 实现TabHost.TabContentFactory接口
1. 基于平台版本的标签应用
随着android API 标准的不断更新,创建标签式布局应用的方式也发生了变化。在android 1.6(API 4)版本以前(包括API 4),实现标签布局应用的代码需要扩展
TabActivity
。在android 1.6和android 3.0之间(不包括3.0),则需要引入静态库android.support.v4.app,扩展其中的
类。它也是那些想要使用支持基于FragmentActivity
Fragment
和 Loader
API的基类。在android 3.0以后,代码像创建普通活动一样,扩展Activity类,然后通过ActionBar创建和添加标签项。
下面,简单地介绍一下平台版本基于API 4(及以前)时创建标签式布局应用需要掌握的知识点。
标签布局主要由三部分组成构成,标签宿主,标签项和标签内容。标签宿主是持有标签项和标签内容的载体,也是创建标签项的发起者。在标签项上可以添加一个图标和标记用以追踪当前的焦点。不同的标签可以承载不同的内容。而标签内容则可以根据需要选择其实现的方式。具体来说,可以采用以下三种方式来实现标签内容:
1. 为标签内容引用视图id(setTabContent by view Id);
2. 通过实现标签宿主内的标签内容工厂类(implements TabHost.TabContentFactory);
3. 为标签内容设置意图来加载活动(setTabContent by intent);
无论采用哪种方式,都不得不说,实现标签内容才是标签式布局应用的本质所在。接下来,我们创建一个工程,并以此来逐一说明每种实现标签内容的方法。
1. 创建一个新的android工程,命名为HelloTabWidget。
2. 需要为每个标签准备状态小图标。一个是在标签被选中时显示,另一个则是在未被选中时显示。通常的设计建议是为选中的标签图标使用深色(灰色),而未被选中的标签图标则使用亮色(白色)。例如:
(未被选中) (被选中)
作为演示,我们为三个标签使用同样的上述图标(实际开发中应该为不同的标签使用不同的自定义图标)。
现在,创建一个state-list drawable 资源用来为每个标签的状态指定对应的图标。
res/drawable/
目录下;res/drawable/
目录下创建一个XML文件,并命为ic_tab_artists.xml
,且内容如下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 当标签被选中时使用灰色图标 --> <item android:drawable="@drawable/ic_tab_artists_grey" android:state_selected="true" /> <!-- 当标签未选中时使用白色图标--> <item android:drawable="@drawable/ic_tab_artists_white" /> </selector>
关于state-list drawable 资源的创建可以参考:http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList
准备工作已经做好了。接下来就逐一地使用不同的方法来为标签设置内容。
2. 把视图设置为标签内容
首先,修改res/layout/下面的main.xml文件,修改后内容如下:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tab_view1" android:background="@drawable/blue" android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/tabs_1_tab_1"/> <TextView android:id="@+id/tab_view2" android:background="@drawable/red" android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/tabs_1_tab_2"/> <TextView android:id="@+id/tab_view3" android:background="@drawable/green" android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/tabs_1_tab_3"/> </FrameLayout>
在上述的布局文件里,包括了三个文本视图,目的是用他们来作为三个标签项的内容。注意,这三个视图的位置并没有指定,因此在切换标签时,他们的内容将会相互覆盖。
然后,实现HelloWidget类,内容如下:
public class HelloTabWidget extends TabActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @SuppressWarnings("deprecation") TabHost tabHost = getTabHost(); Resources res = getResources(); LayoutInflater.from(this).inflate(R.layout.main, tabHost.getTabContentView(), true); tabHost.addTab(tabHost.newTabSpec("tab1") .setIndicator("tab1",res.getDrawable(R.drawable.ic_tab_artists)) .setContent(R.id.view1)); tabHost.addTab(tabHost.newTabSpec("tab3") .setIndicator("tab2" ,res.getDrawable(R.drawable.ic_tab_artists)) .setContent(R.id.view2)); tabHost.addTab(tabHost.newTabSpec("tab3") .setIndicator("tab3",res.getDrawable(R.drawable.ic_tab_artists)) .setContent(R.id.view3)); tabHost.setCurrentTab(1); } }
在HelloWidget类内,创建了三个标签,并为每个标签内容引用了三个不同的视图id,当切换标签时,他们引用的视图将出现在标签项的下方。这一点可以从main.xml布局文件里看的出来。同时,我们还为三个标签设置了相同的小图标,用来凸显标签当前是否被选中。
作为演示,该类内只重写了onCreate方法。该方法内的主要工作包括如下方面:
1. 获取用来持有标签项的宿主(TabHost),因为接下来的操作都是通过宿主来完成的;
2. 创建标签项(TabHost.TabSpec );
3. 为每个标签项设置属性,主要包括:
3.1 设置标记;
3.2 指定用于在标签上显示的图标和文本;
3.3 为每个标签设置内容,当该标签被选中时将会打开内容所指定的活动;
4. 将创建的标签加入到宿主内;
运行后的结果如下图:
3. 实现TabHost.TabContentFactory接口
TabHost类的内部接口TabContentFactory只拥有一个成员函数createTabContent(String tag),它是用来创建标签内容的回调函数。因此,实现TabContentFactory接口时需要实现createTabContent方法。
现在,修改HelloWidget类,让它实现TabHost.TabContentFactory接口,并实现createTabContent方法。修改后的内容如下:
@SuppressWarnings("deprecation") public class HelloTabWidget extends TabActivity implements TabHost.TabContentFactory { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final TabHost tabHost = getTabHost(); Resources res = getResources(); tabHost.addTab(tabHost.newTabSpec("tab1") .setIndicator("tab1", res.getDrawable(R.drawable.ic_tab_artists)) .setContent(this)); tabHost.addTab(tabHost.newTabSpec("tab2") .setIndicator("tab2", res.getDrawable(R.drawable.ic_tab_artists)) .setContent(this)); tabHost.addTab(tabHost.newTabSpec("tab3") .setIndicator("tab3", res.getDrawable(R.drawable.ic_tab_artists)) .setContent(this)); } /** {@inheritDoc} */ public View createTabContent(String tag) { final TextView tv = new TextView(this); tv.setText("I am " + tag); return tv; } }
在新代码里需要注意以下几个方面:
1. 代码中没有使用res/layout/下的布局文件;
2. 为标签设置内容的方法使用的参数是"this",因为HelloWidget实现了TabHost.TabContentFactory;
3. createTabContent方法来自于TabHost.TabContentFactory接口,此方法返回一个View,恰是标签的内容;
最后,运行程序的结果如下:
4. 把意图设置为标签内容
首先,修改res/layout
目录下main.xml文件。内容如下:
<?xml version="1.0" encoding="utf-8"?> <TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="5dp"> <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="5dp" /> </LinearLayout> </TabHost>
为创建一个标签式布局,我们需要一个TabHost
和TabWidget
。TabHost
则必须是布局的根节点,该布局包括了一个用于显示标签的TabWidget
和一个用于显示标签所承载内容的FrameLayout
。FrameLayout
是每个标签内容显示的地方,现在是空的,因为TabHost
会自动地把每个Activity
嵌入其内。
注意, TabWidget
和FrameLayout
属性分别引用"@android:id/tabs"和"@android:id/tabcontent"作为他们的id。这两个平台内建组件id是为标签和标签内容专门准备的,目的是为了TabHost在需要时可以通过确切名字来取出TabWidget
和FrameLayout
,因此 ,这里必须这样写。
同时,把LinearLayout属性里的orientation设置为“vertical”是为了将标签和其承载的内容“上下”排列。如果将orientation设置为“horizontal”,当切换标签时并不会看到它们所承载的内容。
接下来, 在工程里创建三个独立的Activity类, 分别是ArtistsActivity
, AlbumsActivity
和SongsActivity
。这些类分别代表了不同的三个标签。每个类的实现都很简单,只是显示一个简单的信息。例如:
public class ArtistsActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView textview = new TextView(this); textview.setText("This is the Artists tab"); setContentView(textview); } }
当切换到该类所代表的标签时,则显示“This is the Artists tab”。还有,不要忘记将这三个类加入到清单文件里。
接着,修改HelloWidget类,修改后如下:
@SuppressWarnings("deprecation") public class HelloTabWidget extends TabActivity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Resources res = getResources(); // Resource object to get Drawables TabHost tabHost = getTabHost(); // The activity TabHost TabHost.TabSpec spec; // Resusable TabSpec for each tab Intent intent; // Reusable Intent for each tab // Create an Intent to launch an Activity for the tab (to be reused) intent = new Intent().setClass(this, ArtistsActivity.class); // Initialize a TabSpec for each tab and add it to the TabHost spec = tabHost.newTabSpec("artists").setIndicator("Artists", res.getDrawable(R.drawable.ic_tab_artists)) .setContent(intent); tabHost.addTab(spec); // Do the same for the other tabs intent = new Intent().setClass(this, AlbumsActivity.class); spec = tabHost.newTabSpec("albums").setIndicator("Albums", res.getDrawable(R.drawable.ic_tab_artists)) .setContent(intent); tabHost.addTab(spec); intent = new Intent().setClass(this, SongsActivity.class); spec = tabHost.newTabSpec("songs").setIndicator("Songs", res.getDrawable(R.drawable.ic_tab_artists)) .setContent(intent); tabHost.addTab(spec); tabHost.setCurrentTab(2); } }
在经过修改后,我们创建三个intent,并为每个intent关联了一个活动, 然后把intent设置为标签的内容。当在切换标签时,意图内关联的活动会被加载。值得注意的是,代码里可以不使用main.xml布局文件,程序的运行结果也不会有所差异。
最后, 运行的结果如下:
好了,在基于API 4(或以前)的平台版本上创建简单标签式布局的方法就介绍到这里。
2012年4月30日 晚毕。