Android UI學習和布局優化

jopen 11年前發布 | 91K 次閱讀 Android開發 移動開發 Android UI

FrameLayout

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

我的理解是,把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>



效果圖

Android UI學習和布局優化          Android UI學習和布局優化       Android UI學習和布局優化

布局優化

使用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>



效果圖

Android UI學習和布局優化

這里要注意的是, "@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>
沒有設置收縮/伸展效果

Android UI學習和布局優化

注意,原來沒有添加 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效果

Android UI學習和布局優化

現在可以看到第一個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效果

Android UI學習和布局優化

 
而動態隱藏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效果

Android UI學習和布局優化

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例子:

Android UI學習和布局優化

代碼

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的例子:

Android UI學習和布局優化

代碼

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.

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!