Android UI学习和布局优化

…衆ロ難τιáo~ 提交于 2019-11-27 00:34:28

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>



效果图

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.


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!