Android UI學習和佈局優化

FrameLayout

先來看官方文檔的定義:FrameLayout是最簡單的一個佈局對象。它被定製爲你屏幕上的一個空白備用區域,以後你能夠在其中填充一個單一對象 — 好比,一張你要發佈的圖片。全部的子元素將會固定在屏幕的左上角;你不能爲FrameLayout中的一個子元素指定一個位置。後一個子元素將會直接在前 一個子元素之上進行覆蓋填充,把它們部份或所有擋住(除非後一個子元素是透明的)。 java

個人理解是,把FrameLayout看成畫布canvas,固定從屏幕的左上角開始填充圖片,文字等。看看示例,原來能夠利用 android:layout_gravity來設置位置的:

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout 
   xmlns:android="http://schemas.android.com/apk/res/android" 
   android:layout_width="fill_parent" 
   android:layout_height="fill_parent" > 
  
    <ImageView 
        android:id="@+id/image" 
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent" 
        android:scaleType="center" 
        android:src="@drawable/candle" 
        /> 
    <TextView 
        android:id="@+id/text1" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="center" 
        android:textColor="#00ff00" 
        android:text="@string/hello" 
        /> 
    <Button 
        android:id="@+id/start" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="bottom" 
        android:text="Start" 
        /> 
</FrameLayout>



效果圖

FrameLayout          hierarchyviewer       hierarchyviewer

佈局優化

使用tools裏面的hierarchyviewer.bat來查看layout的層次。在啓動模擬器啓動所要分析的程序,再啓動 hierarchyviewer.bat,選擇模擬器以及該程序,點擊「Load View Hierarchy」,就會開始分析。能夠save as png。  

<merge> 減小視圖層級結構

從上圖能夠看到存在兩個FrameLayout,紅色框住的。若是能在layout文件中把FrameLayout聲明去掉就能夠進一步優化佈局代碼了。 可是因爲佈局代碼須要外層容器容納,若是直接刪除FrameLayout則該文件就不是合法的佈局文件。這種狀況下就可使用<merge> 標籤了。

修改成以下代碼就能夠消除多餘的FrameLayout了:
<?xml version="1.0" encoding="utf-8"?> 
<merge  xmlns:android="http://schemas.android.com/apk/res/android"> 
    <ImageView 
        android:id="@+id/image" 
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent" 
        android:scaleType="center" 
        android:src="@drawable/candle" 
        /> 
    <TextView 
        android:id="@+id/text1" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="center" 
        android:textColor="#00ff00" 
        android:text="@string/hello" 
        /> 
    <Button 
        android:id="@+id/start" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_gravity="bottom" 
        android:text="Start" 
        /> 
</merge>



<merge>也有一些使用限制: 只能用於xml layout文件的根元素;在代碼中使用LayoutInflater.Inflater()一個以merge爲根元素的佈局文件時候,須要使用 View inflate (int resource, ViewGroup root, boolean attachToRoot)指定一個ViewGroup 做爲其容器,而且要設置attachToRoot 爲true。

<include> 重用layout代碼

若是在某個佈局裏面須要用到另外一個相同的佈局設計,能夠經過<include> 標籤來重用layout代碼:
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"> 
        
    <include android:id="@+id/layout1" layout="@layout/relative" /> 
    <include android:id="@+id/layout2" layout="@layout/relative" /> 
    <include android:id="@+id/layout3" layout="@layout/relative" /> 

</LinearLayout>



效果圖

include

這裏要注意的是, "@layout/relative"不是引用Layout的id,而是引用res/layout/relative.xml,其內容是前面文章介紹RelativeLayout的佈局代碼。

另外,經過<include>,除了能夠覆寫id屬性值,還能夠修改其餘屬性值,例如android:layout_width,android:height等。

<viewstub> 延遲加載

 
ViewStub 是一個不可見的,大小爲0的View,最佳用途就是實現View的延遲加載,在須要的時候再加載View,可Java中常見的性能優化方法延遲加載同樣。
 
當調用ViewStub的setVisibility函數設置爲可見或則調用 inflate初始化該View的時候,ViewStub引用的資源開始初始化,而後引用的資源替代ViewStub本身的位置填充在ViewStub的 位置。所以在沒有調用setVisibility(int) 或則 inflate()函數以前 ViewStub一種存在組件樹層級結構中,可是因爲ViewStub很是輕量級,這對性能影響很是小。 能夠經過ViewStub的inflatedId屬性來從新定義引用的layout id。 例如:
<ViewStub android:id="@+id/stub" 
          android:inflatedId="@+id/subTree" 
          android:layout="@layout/mySubTree" 
          android:layout_width="120dip" 
          android:layout_height="40dip" />



上面定義的ViewStub ,能夠經過id 「stub」來找到,在初始化資源「mySubTree」後,stub從父組件中刪除,而後"mySubTree"替代stub的位置。初始資 源"mySubTree"獲得的組件能夠經過inflatedId 指定的id "subTree"引用。 而後初始化後的資源被填充到一個120dip寬、40dip高的地方。 
 
推薦使用下面的方式來初始化ViewStub:
ViewStub stub = (ViewStub) findViewById(R.id.stub); 
View inflated = stub.inflate();



當調用inflate()函數的時候,ViewStub 被引用的資源替代,而且返回引用的view。 這樣程序能夠直接獲得引用的view而不用再次調用函數 findViewById()來查找了。
 
ViewStub目前有個缺陷就是還不支持 <merge /> 標籤。

layoutopt (Layout Optimization工具)

這工具能夠分析所提供的Layout,並提供優化意見。在tools文件夾裏面能夠找到layoutopt.bat。
用法
layoutopt  <list of xml files or directories>
參數
一個或多個的Layout xml文件,以空格間隔;或者多個Layout xml文件所在的文件夾路徑
例子
layoutopt  G:\StudyAndroid\UIDemo\res\layout\main.xml
layoutopt  G:\StudyAndroid\UIDemo\res\layout\main.xml G:\StudyAndroid\UIDemo\res\layout\relative.xml
layoutopt  G:\StudyAndroid\UIDemo\res\layout

GridView

GridView: A view that shows items in two-dimensional scrolling grid. The items in the grid come from the ListAdapter associated with this view. 簡單說,GridView就是咱們資源管理器日常見到的一個個文件的icon顯示方式。

上面說起到了,GridView的Item是來自ListAdapter的,因此通常在Activity的onCreate使用GridView的代碼:
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.grid_2); 
 
    GridView g = (GridView) findViewById(R.id.myGrid); 
    g.setAdapter(new ImageAdapter(this)); 
}
而ImageAdapter通常是extends BaseAdapter。BaseAdapter是implements ListAdapter SpinnerAdapter,但不少時候自定義的Adapter都是override ListAdapter的父類Adapter接口裏面的方法:
    int     getCount()                   獲取當前Adapter的Items數目
    Object getItem(int position)     獲取相應position的Item
    long     getItemId(int position)  獲取相應position的Item在List中的row id
    View    getView(int position, View convertView, ViewGroup parent) 獲取在指定position所要顯示的data的View
 
這些方法函數和swing的差很少,都是基於MVC。大概原理過程是這樣的:程序須要顯示GridView,那麼要把data一個一個地顯示出來是經過 一個for循環,首先call Adapter.getCount()獲得有多少個data,而後position++地getItem,getView獲得要顯示的view,這樣子逐 一地顯示出來!
 
下面是官方sample裏面的Photo Grid的例子,本人省略了某些代碼:
public class ImageAdapter extends BaseAdapter { 
    public ImageAdapter(Context c) { 
        mContext = c; 
    } 
 
    public int getCount() { 
        return mThumbIds.length; 
    } 
  
    public Object getItem(int position) { 
        return position; 
    } 

    public long getItemId(int position) { 
        return position; 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 
        ImageView imageView; 
        if (convertView == null) { 
            imageView = new ImageView(mContext); 
            imageView.setLayoutParams(new GridView.LayoutParams(45, 45));//設置ImageView寬高 
            imageView.setAdjustViewBounds(false); 
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 
            imageView.setPadding(8, 8, 8, 8); 
        } else { 
            imageView = (ImageView) convertView; 
        } 

       imageView.setImageResource(mThumbIds[position]); 

       return imageView; 
    } 

    private Context mContext; 

    private Integer[] mThumbIds = { 
            R.drawable.sample_thumb_0, R.drawable.sample_thumb_1, 
            R.drawable.sample_thumb_2, R.drawable.sample_thumb_3, 
            R.drawable.sample_thumb_4, R.drawable.sample_thumb_5, 
            R.drawable.sample_thumb_6, R.drawable.sample_thumb_7
    }; 
}
留意getView裏面的代碼,要判斷convertView是否爲null,以便重用,減小對象的建立,減小內存佔用。
 
XML佈局文件內容,原來就只是指明GridView:
<?xml version="1.0" encoding="utf-8"?> 
<GridView xmlns:android="http://schemas.android.com/apk/res/android"  
    android:id="@+id/myGrid" 
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent" 
    android:padding="10dp" 
    android:verticalSpacing="10dp" 

    android:horizontalSpacing="10dp" 
    android:numColumns="auto_fit" 
    android:columnWidth="60dp" 
    android:stretchMode="columnWidth" 

    android:gravity="center" 
    />

能夠看到getView,和ImageView是重點,影響圖片的顯示效果。並且發現列數是不肯定的,取決於每一個ImageView的寬度和屏幕的寬度。接下來看看ImageView。

ImageView

ImageView:Displays an arbitrary image, such as an icon. The ImageView class can load images from various sources (such as resources or content providers), takes care of computing its measurement from the image so that it can be used in any layout manager, and provides various display options such as scaling and tinting。 ImageView就是用來顯示Image,icon的。

這裏咱們重點理解ImageView的屬性android:scaleType,即 ImageView.setScaleType(ImageView.ScaleType)。android:scaleType是控制圖片如何 resized/moved來匹對ImageView的size。ImageView.ScaleType / android:scaleType值的意義區別:
 
CENTER /center  按圖片的原來size居中顯示,當圖片長/寬超過View的長/寬,則截取圖片的居中部分顯示
 
CENTER_CROP / centerCrop  按比例擴大圖片的size居中顯示,使得圖片長(寬)等於或大於View的長(寬)
 
CENTER_INSIDE / centerInside  將圖片的內容完整居中顯示,經過按比例縮小或原來的size使得圖片長/寬等於或小於View的長/寬
 
FIT_CENTER / fitCenter  把圖片按比例擴大/縮小到View的寬度,居中顯示
 
FIT_END / fitEnd    把圖片按比例擴大/縮小到View的寬度,顯示在View的下部分位置
 
FIT_START / fitStart   把圖片按比例擴大/縮小到View的寬度,顯示在View的上部分位置
 
FIT_XY / fitXY  把圖片 不按比例 擴大/縮小到View的大小顯示
 
MATRIX / matrix 用矩陣來繪製
 
一開始我不明白MATRIX矩陣,網上搜索後發現原來MATRIX矩陣能夠動態縮小放大圖片來顯示,這裏不展開深刻的瞭解,只是貼出相關語句,縮小圖片:
 
//得到Bitmap的高和寬 
int bmpWidth=bmp.getWidth(); 
int bmpHeight=bmp.getHeight(); 

//設置縮小比例 
double scale=0.8; 
//計算出此次要縮小的比例 
scaleWidth=(float)(scaleWidth*scale); 
scaleHeight=(float)(scaleHeight*scale); 

//產生resize後的Bitmap對象 
Matrix matrix=new Matrix(); 
matrix.postScale(scaleWidth, scaleHeight); 
Bitmap resizeBmp=Bitmap.createBitmap(bmp, 0, 0, bmpWidth, bmpHeight, matrix, true);
應用ImageView的例子不少,看看上次FrameLayout裏面的:
<ImageView 
    android:id="@+id/image" 
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent" 
    android:scaleType="center" 
    android:src="@drawable/candle" 
    />

TableLayout

TableLayout和咱們平時在網頁上見到的Table有所不一樣,TableLayout沒有邊框的,它是由多個TableRow對象組成, 每一個TableRow能夠有0個或多個單元格,每一個單元格就是一個View。這些TableRow,單元格不能設置layout_width,寬度默認是fill_parent的,只有高度layout_height能夠自定義,默認是wrap_content。

單元格能夠爲empty,而且經過android:layout_column能夠設置index值實現跳開某些單 元格。在TableRow之間,添加View,設置layout_height以及背景色,就能夠實現一條間隔線。 android:layout_span能夠設置合併幾個單元格

<?xml version="1.0" encoding="utf-8"?> 
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"> 
  
    <TableRow> 
        <TextView 
            android:text="column1" 
            android:padding="3dip"  /> 
        <TextView 
            android:text="column2" 
            android:padding="3dip"  /> 
        <TextView 
            android:text="column3" 
            android:padding="3dip"  /> 
    </TableRow> 
 
    <TableRow> 
        <TextView 
          android:text="column11" 
          android:visibility="invisible"/> //cell不見了 
        <TextView 
            android:text="左邊的invisible" 
            android:gravity="right" 
            android:padding="3dip" /> 
        <Button 
            android:id="@+id/go" 
            android:text="go"  
            android:padding="3dip" /> 
        <Button 
            android:text="cancel" 
            android:padding="3dip" /> 
    </TableRow> 
 
    <View                               //間隔線 
        android:layout_height="2dip" 
        android:background="#F00" /> 
 
    <TableRow> 
        <TextView 
           android:text="右邊的cell empty" /> 
        <TextView 
            android:layout_column="2" 
            android:text="跳開empty cell" 
            android:padding="3dip" /> 
    </TableRow> 
     
    <TableRow> 
        <TextView 
            android:text="合併3個單元格" 
            android:layout_span="3" 
            android:gravity="center_horizontal" 
            android:background="#FFC0C0C0" 
            android:textColor="#f00" 
            android:padding="3dip" /> 
    </TableRow> 
</TableLayout>
沒有設置收縮/伸展效果

Table1

注意,原來沒有添加 android:padding="3dip" 的,發現那些column會湊在一塊兒的,沒有空白間隔!明顯看到,那個cancel按鈕被擠到幾乎看不見了!這時候須要使用 android:shrinkColumns="可收縮的column",android:stretchColumns="可伸展的column"。

android:shrinkColumnsandroid:stretchColumns的值都是以0開始的index,但必須是string值,即用"1,2,5"來表示。能夠用"*"來表示all columns。並且同一column能夠同時設置爲shrinkable和stretchable。

若是使用TableLayout類的 setColumnShrinkable/setColumnStretchable (int columnIndex, boolean isShrinkable)就麻煩些了,須要一個一個column來設置。也可使用TableLayout的 setShrinkAllColumns/setStretchAllColumns來設置all columns。

判斷這些column是否shrinkable或stretchable,能夠調用 isColumnShrinkable/isColumnStretchable(int columnIndex),isShrinkAllColumns()/isStretchAllColumns()
<?xml version="1.0" encoding="utf-8"?> 
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:shrinkColumns="0" > // 設置第一個column可收縮 
 
    <TableRow> 
        <TextView 
            android:text="column1" 
            android:padding="3dip"  /> 
        <TextView 
            android:text="column2" 
            android:padding="3dip"  /> 
        <TextView 
            android:text="column3" 
            android:padding="3dip"  /> 
    </TableRow> 
 
    <TableRow> 
        <TextView 
          android:text="column11" 
          android:visibility="invisible"/> 
        <TextView 
            android:text="左邊的invisible" 
            android:gravity="right" 
            android:padding="3dip" /> 
        <Button 
            android:id="@+id/go2" 
            android:text="go2"  
            android:padding="3dip" /> 
        <Button 
            android:text="cancel" 
            android:padding="3dip" /> 
    </TableRow> 
 
    <View 
        android:layout_height="2dip" 
        android:background="#F00" /> 
 
    <TableRow> 
        <TextView 
          android:text="右邊的cell empty" /> 
        <TextView 
            android:layout_column="2" 
            android:text="跳開empty cell" 
            android:padding="3dip" /> 
        <TextView 
            android:text="123456789" 
            android:padding="3dip" /> 
    </TableRow> 
</TableLayout>
可收縮column效果

Table2

如今能夠看到第一個column爲了讓第4個column完整顯示,而收縮得內容分爲幾行顯示!
 
可伸展column的效果就是在其餘column能夠完整顯示時,該column就會伸展,佔最多空間:
<?xml version="1.0" encoding="utf-8"?> 
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:stretchColumns="1"> // 設置第二個column可伸展
 
   <TableRow> 
        <TextView 
            android:text="column1" 
            android:padding="3dip" /> 
        <TextView 
            android:text="column2" 
            android:gravity="right" 
            android:padding="3dip" /> 
        <TextView 
            android:text="column3" 
            android:padding="3dip"  /> 
    </TableRow> 

    <TableRow> 
        <TextView 
            android:text="column1" 
            android:padding="3dip" /> 
        <TextView 
            android:text="column2" 
            android:gravity="right" 
            android:padding="3dip" /> 
        <TextView 
            android:text="column3" 
            android:padding="3dip"  /> 
    </TableRow> 
</TableLayout>
可伸展column效果

Table3

 
而動態隱藏column,能夠調用TableLayout.setColumnCollapsed (int columnIndex, boolean isCollapsed)來指定相應的column。另外TableLayout類的boolean isColumnCollapsed (int columnIndex)可以判斷指定的column是否隱藏。
 
TableLayout能夠用來作網頁上的Form顯示效果,看看官方的sample:

<?xml version="1.0" encoding="utf-8"?> 
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 
   android:layout_width="fill_parent" 
   android:layout_height="fill_parent" 
   android:stretchColumns="1"> 

   <TableRow> 
       <TextView 
           android:text="@string/table_layout_10_user" 
           android:textStyle="bold" 
           android:gravity="right" 
           android:padding="3dip" /> 
 
       <EditText android:id="@+id/username" 
           android:text="@string/table_layout_10_username_text" 
           android:padding="3dip" 
           android:scrollHorizontally="true" /> 
   </TableRow> 

   <TableRow> 
       <TextView 
           android:text="@string/table_layout_10_password" 
           android:textStyle="bold" 
           android:gravity="right" 
           android:padding="3dip" /> 

       <EditText android:id="@+id/password" 
           android:text="@string/table_layout_10_password_text" 
           android:password="true" 
           android:padding="3dip" 
           android:scrollHorizontally="true" /> 
   </TableRow> 
 
   <TableRow 
       android:gravity="right"> 

       <Button android:id="@+id/cancel" 
           android:text="@string/table_layout_10_cancel" /> 
 
       <Button android:id="@+id/login" 
           android:text="@string/table_layout_10_login" /> 
   </TableRow> 
</TableLayout>
Form效果

Table4

Tab的學習和使用

本文是參考Android官方提供的sample裏面的ApiDemos的學習總結。

TabActivity

首先Android裏面有個名爲TabActivity來給咱們方便使用。其中有如下能夠關注的函數:
public TabHost getTabHost ()  得到當前TabActivity的TabHost
public TabWidget getTabWidget () 得到當前TabActivity的TabWidget
 
public void setDefaultTab (String tag) 這兩個函數很易懂,就是設置默認的Tab
public void setDefaultTab (int index)  經過tab名——tag或者index(從0開始)
  
protected void onRestoreInstanceState (Bundle state) 這兩個函數的介紹能夠
protected void onSaveInstanceState (Bundle outState) 參考 Activity的生命週期
 

TabHost

那麼咱們要用到的Tab載體是TabHost,須要從TabActivity.getTabHost獲取。
如今看看TabHost類,它有3個內嵌類:1個類TabHost.TabSpec,2個接口TabHost.TabContentFactory和TabHost.OnTabChangeListener。後面會介紹這些類和接口。
 
TabHost類的一些函數:
public void addTab (TabHost.TabSpec tabSpec) 添加tab,參數TabHost.TabSpec經過下面的函數返回獲得
public TabHost.TabSpec newTabSpec (String tag) 建立TabHost.TabSpec
  
public void clearAllTabs () remove全部的Tabs
public int getCurrentTab ()
public String getCurrentTabTag ()
public View getCurrentTabView ()
public View getCurrentView ()
public FrameLayout getTabContentView () 返回Tab content的FrameLayout
 
public TabWidget getTabWidget ()
public void setCurrentTab (int index)       設置當前的Tab by index
public void setCurrentTabByTag (String tag) 設置當前的Tab by tag
public void setOnTabChangedListener (TabHost.OnTabChangeListener l) 設置TabChanged事件的響應處理
public void setup () 這個函數後面介紹

TabHost.TabSpec

從上面的函數能夠知道如何添加tab了,要注意,這裏的Tag(標籤),不是Tab按鈕上的文字。
而要設置tab的label和content,須要設置TabHost.TabSpec類。 引用SDK裏面的話——「A tab has a tab indicator, content, and a tag that is used to keep track of it.」,TabHost.TabSpec就是管理這3個東西:
public String getTag ()
public TabHost.TabSpec setContent
public TabHost.TabSpec setIndicator
 
我理解這裏的 Indicator就是Tab上的label,它能夠
設置label:  setIndicator (CharSequence label)
或者同時 設置label和iconsetIndicator (CharSequence label, Drawable icon)
或者直接 指定某個view:  setIndicator (View view)
  
對於 Content,就是Tab裏面的內容,能夠
設置View的id:  setContent(int viewId)
或者 TabHost.TabContentFactory的createTabContent(String tag)來處理:
setContent(TabHost.TabContentFactory contentFactory)
或者用 new Intent來引入其餘Activity的內容: setContent(Intent intent)
  
如今來看官方的Views/Tabs/Content By Id例子:

TabHost

代碼

public class Tabs1 extends TabActivity { 
 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        TabHost tabHost = getTabHost(); 
         
        LayoutInflater.from(this).inflate(R.layout.tabs1, tabHost.getTabContentView(), true); 
 
        tabHost.addTab(tabHost.newTabSpec("tab1") 
                .setIndicator("tab1") 
                .setContent(R.id.view1)); 
        tabHost.addTab(tabHost.newTabSpec("tab3") 
                .setIndicator("tab2") 
                .setContent(R.id.view2)); 
        tabHost.addTab(tabHost.newTabSpec("tab3") 
                .setIndicator("tab3") 
                .setContent(R.id.view3)); 
    } 
}
原來在獲取TabHost後,須要用LayoutInflater來獲得Layout,LayoutInflater在後面就詳細介紹。R.layout.tabs1的內容:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"> 
 
    <TextView android:id="@+id/view1" 
        android:background="@drawable/blue" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
        android:text="@string/tabs_1_tab_1"/> 
 
    <TextView android:id="@+id/view2" 
        android:background="@drawable/red" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
        android:text="@string/tabs_1_tab_2"/> 
  
    <TextView android:id="@+id/view3" 
        android:background="@drawable/green" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
        android:text="@string/tabs_1_tab_3"/> 
 
</FrameLayout> 
 
<! -- strings.xml 
     <string name="tabs_1_tab_1">tab1</string> 
     <string name="tabs_1_tab_2">tab2</string> 
     <string name="tabs_1_tab_3">tab3</string> 
-->
原來是用FrameLayout的!

而讓Tab1的內容顯示tab1且背景爲Blue,是setContent(R.id.view1)這裏引用了TextView1。如今就基本明白如何添加tab以及如何設置label和content了。
 
接下來看看Views/Tabs/Content By Factory的例子:

TabHost2

代碼

public class Tabs2 extends TabActivity implements TabHost.TabContentFactory { 
 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
 
        final TabHost tabHost = getTabHost(); 
        tabHost.addTab(tabHost.newTabSpec("tab1") 
                .setIndicator("tab1", getResources().getDrawable(R.drawable.star_big_on)) 
                .setContent(this)); 
        tabHost.addTab(tabHost.newTabSpec("tab2") 
                .setIndicator("tab2") 
                .setContent(this)); 
        tabHost.addTab(tabHost.newTabSpec("tab3") 
                .setIndicator("tab3") 
                .setContent(this)); 
    } 
 
    public View createTabContent(String tag) { 
        final TextView tv = new TextView(this); 
        tv.setText("Content for tab with tag " + tag); 
        return tv; 
    } 
}
能夠看到經過override重寫(從新實現)父類TabHost.TabContentFactory中的方法View createTabContent(String tag)來實現不一樣tab的不一樣content。同時在setContent的參數設置爲相應的TabContentFactory。

原來createTabContent是在每一個tab第一次顯示時才調用的,隨後再次顯示該tab就不會再次調用的, 我本身用Logcat查看到的!這一點很關鍵,就是說在createTabContent是在tab沒有徹底建立前調用的,這意味在 createTabContent裏面是不能調用getCurrentTabView等之類的函數的,不然就出錯!

至於Views/Tabs/Content By Intent例子,就只是貼出代碼,不給截圖了:
public class Tabs3 extends TabActivity { 
 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
 
        final TabHost tabHost = getTabHost(); 
 
        tabHost.addTab(tabHost.newTabSpec("tab1") 
                .setIndicator("list") 
                .setContent(new Intent(this, List1.class))); 

        tabHost.addTab(tabHost.newTabSpec("tab2") 
                .setIndicator("photo list") 
                .setContent(new Intent(this, List8.class))); 
         
        // This tab sets the intent flag so that it is recreated each time 
        // the tab is clicked. 
        tabHost.addTab(tabHost.newTabSpec("tab3") 
                .setIndicator("destroy") 
                .setContent(new Intent(this, Controls2.class) 
                        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))); 
    } 
}
效果:Tab1的內容是List1的Activity,Tab2的是List8的Activity,Tab3的是controls2.Activity。

TabHost.OnTabChangeListener

TabHost.OnTabChangeListener接口只有一個抽象方法onTabChanged(String tagString),明顯地,在 onTabChanged(String tagString)方法裏面swtich..case..來判斷tagString分別處理就好了。

TabHost.setup()

在此貼出SDK doc裏面的相關解釋:
public void setup ()         Since: API Level 1
Call setup() before adding tabs if loading TabHost using findViewById(). However, You do not need to call setup() after getTabHost() in TabActivity. Example:
 
     mTabHost = (TabHost)findViewById(R.id.tabhost);
     mTabHost.setup();
     mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
 
//個人理解是,若是要用到findViewById來獲取TabHost,而後add tabs的話,須要在addTab前call setup();

public void setup (LocalActivityManager activityGroup)         Since: API Level 1
If you are using setContent(android.content.Intent), this must be called since the activityGroup is needed to launch the local activity. This is done for you if you extend TabActivity.
 
Parameters
activityGroup Used to launch activities for tab content.
相關文章
相關標籤/搜索