首页 > 代码库 > 每日总结 - android布局优化

每日总结 - android布局优化

1,使用<include /> 标签避免代码重复

设想一种情况:我们需要为应用程序中的每个视图都添加一个标题。为了简化问题,我们假设标题是一个显示应用程序标题的TextView。通常多个Activity 会对应多个XML 文件。难道我们需要把这个TextView 复制到每个XML 文件中吗?如果以后需要修改这个TextView 会出现什么情况?“复制/ 粘贴”的方式固然能够解决这个问题,但并不是高效的方法。解决上述问题的最简单方法是使用<include /> 标签。

我们可以通过<include /> 标签把在其他XML文件中定义的布局插入当前布局文件中。以其中一个Activity 为例,其XML 布局文件如下所示:

 1 <RelativeLayout 2   xmlns:android="http://schemas.android.com/apk/res/android" 3   android:layout_width="fill_parent" 4   android:layout_height="fill_parent"> 5 <TextView 6  android:layout_width="fill_parent" 7  android:layout_height="wrap_content" 8  android:layout_centerInParent="true" 9  android:gravity="center_horizontal"10  android:text="@string/hello"/>11 <include layout="@layout/title"/>12 13 </RelativeLayout>

title布局如下所示:

1 <TextView xmlns:android="http://schemas.android.com/apk/res/android"2   android:layout_width="fill_parent"3   android:layout_height="wrap_content"4   android:layout_alignParentBottom="true"5   android:layout_marginBottom="30dp"6   android:gravity="center_horizontal"7   android:text="@string/title_text"/>

在上述示例代码中,我们使用了<include /> 标签,并且只需要指定该标签的layout 属性值。

但是也存在一个问题,这种方式之所以可行是因为Activity 在main 布局文件中使用的是RelativeLayout。如果其中一个Activity 的布局文件使用的是LinearLayout 呢?

虽然android:layout_alignParentBottom="true" 适用于RelativeLayout,但是并不适用于LinearLayout。”这个想法是正确的。接下来分析
使用<include /> 标签的第二种方法,在这种方法里,我们直接在<include /> 标签里使用android:layout_* 属性。

以下是修改后的main.xml 文件,其中使用了<include /> 标签的android:layout_* 属性,源码如下所示:

 1 <RelativeLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3   android:layout_width="fill_parent" 4   android:layout_height="fill_parent"> 5   <TextView 6     android:layout_width="fill_parent" 7     android:layout_height="wrap_content" 8     android:layout_centerInParent="true" 9     android:gravity="center_horizontal"10     android:text="@string/hello"/>11   <include12     layout="@layout/title"13     android:layout_width="fill_parent"14     android:layout_height="wrap_content"15     android:layout_alignParentBottom="true"16     android:layout_marginBottom="30dp"/>17 </RelativeLayout/>

修改后的标题布局文件如下:

1 <TextView  xmlns:android="http://schemas.android.com/apk/res/android"2   android:layout_width="0dp"3   android:layout_height="0dp"4   android:gravity="center"5   android:text="@string/title"/>

2,通过ViewStub 实现View 的延迟加载

设计布局的时候,读者可能想过根据上下文或者用户交互情况显示一个视图。如果想要一个视图只在需要的时候显示,可以使用ViewStub 这个类。

Android 开发文档中有关于ViewStub 的介绍,主要内容如下:

  “ViewStub 是一种不可视并且大小为0 的视图,可以延迟到运行时填充(inflate) 布局资源。当ViewStub 设置为可视或者inflate() 方法被调用后,就会填充布局资源,然后ViewStub 便会被填充的视图替代。”

既然已经清楚ViewStub 是什么, 接下来看看它能做什么。在下面的示例代码中,我们使用ViewStub 来延迟加载一个MapView。假设需要创建一个视图来显示地理位置的详细信息,先看两种可能情况:
? 一些场所没有 GPS 信息
? 用户可能并不需要地图信息
如果一个场所没有GPS 信息,开发者不需要在地图上显示标记信息。同样,如果用户不需要地图信息,也就无须加载地图。我们可以把MapView 放置在ViewStub 标签中,让用户自己决定是否显示地图信息。

要达到上述目的,需要使用下面的布局:

 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android" 3   android:layout_width="fill_parent" 4   android:layout_height="fill_parent"> 5   <Button 6     android:layout_width="fill_parent" 7     android:layout_height="wrap_content" 8     android:text="@string/show_map" 9     android:onClick="onShowMap"/>10   <ViewStub11     android:id="@+id/map_stub"12     android:layout_width="fill_parent"13     android:layout_height="fill_parent"14     android:layout="@layout/map"15     andorid:inflatedId="@+id/map_view" />16 </RelativeLayout>

很显然,需要通过map_stub这个id从Activity获得ViewStub。同时layout属性指定需要填充的布局文件。对于本例,需要填充的是map.xml文件,源码如下:

1 <?xml version="1.0" encoding="utf-8"?>2 <com.google.android.maps.MapView  xmlns:android="http://schemas.android.com/apk/res/android"3   android:layout_width="fill_parent"4   android:layout_height="fill_parent"5   android:clickable="true"6   android:apiKey="my_api_key"/>


最后一个需要说明的属性是inflatedId。inflatedId是调用ViewStub 的inflate() 方法或者setVisibility() 方法时返回的ID,这个ID 便是被填充的View 的ID。在本例中,我们不需要操作MapView,只需要调用setVisibility(View.VISIBLE) 方法即可。如果想获取被填充的视图的引用,inflate() 方法会直接返回该引用,这样避免了再次调用findViewById() 方法。Activity 的源码比较简单,如下所示:

 1 public class MainActivity extends MapActivity { 2 private View mViewStub; 3 @Override 4 public void onCreate(Bundle savedInstanceState) { 5     super.onCreate(savedInstanceState); 6     setContentView(R.layout.main); 7     mViewStub = findViewById(R.id.map_stub); 8 } 9 public void onShowMap(View v) {10     mViewStub.setVisibility(View.VISIBLE);11 }12 ...13 }

如上述代码所示,只需要改变ViewStub 的可视性便可控制map 的显示。

还可以通过View view = ((ViewStub) findViewById(R.id.map_stub)).inflate();

当调用inflate()函数的时候,ViewStub被引用的资源替代,并且返回引用的view。 这样程序可以直接得到引用的view而不用再次调用函数findViewById()来查找了。

每日总结 - android布局优化