文章內容
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日 晚畢。