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.
相关文章
相关标签/搜索