Android开发笔记(七十四)布局文件优化

include/merge

布局优化中常常用到include/merge标签,include的含义类似C代码中的include,意思是直接把指定布局片段包含进当前的布局文件。include适用于多个布局文件中存在相同的xml片段,比如说相同的标题栏、相同的广告栏、相同的进度栏等等。
include的用法很简单,只有下面一句话:
<include layout="@layout/common_title" />
这时必定有个common_title.xml的布局文件,它用于在各页面展示相同的标题区域。


include子布局文件的根节点可以是LinearLayout或RelativeLayout或FrameLayout,可是上级布局文件往往已经有了相同的视图节点,这时子布局的根节点就变成冗余的了,但是布局文件又必须有根节点,着实矛盾。不要急,merge标签便是处理这个问题的,merge要和include配合使用,也就是说,merge只能是include子布局文件的根节点,且merge无需设置额外的属性。merge标签代替了根节点LinearLayout、RelativeLayout和FrameLayout原来的位置,只是告诉编译器:我是个占位的合并标签,不需要对我做布局处理;这样app在渲染UI时,只是简单合并merge标签下的内容,但不做布局计算和调整,从而提高了UI的加载效率。


ViewStub

在一个页面上根据不同条件展示不同的控件,我们常常会设置控件的可视属性,比如调用指定控件的setVisibility方法,若需展示则设置View.VISIBLE,若需隐藏则设置View.GONE。不过gone的控件只是看不到罢了,实际UI渲染时还是会被加载。要想事先不加载,在条件符合时才加载,就得用到标签ViewStub。


ViewStub类似一个简单的View,但具体布局由属性layout指定,并且在app加载UI时,ViewStub不显示界面内容,只有在代码中调用该控件的inflate方法,layout指定的布局才会展示。基于以上特性,ViewStub在提高布局性能上有几个特点:
优点:ViewStub在加载时只占用大约一个View控件的内存,不占用layout整个布局需要的内存;
缺点:ViewStub一旦调用inflate方法,页面内容就显示出来,之后就没有对应的方法再缩回去。如果还想再次隐藏或显示布局,只能通过setVisibility来实现。


举个ViewStub实际运用的场景,手机屏幕在竖屏和横屏切换时,有时希望显示不同的布局,比如竖屏显示列表,横屏则显示网格,横竖屏的截图如下:
竖屏的列表方式界面截图



横屏的网格方式界面截图
Android开发笔记(七十四)布局文件优化_第1张图片


下面是具体实现的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="#aaaaff"
        android:paddingLeft="10dp"
        android:paddingRight="10dp" >

        <include layout="@layout/common_title" />
    </RelativeLayout>

    <ViewStub
        android:id="@+id/vs_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/viewstub_list" />

    <ViewStub
        android:id="@+id/vs_grid"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout="@layout/viewstub_grid" />

</LinearLayout>


下面是具体实现的代码示例:
import com.example.exmlayout.adapter.ContentGridAdapter;
import com.example.exmlayout.adapter.TitleListAdapter;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewStub;
import android.widget.GridView;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;

public class PlanetActivity extends Activity implements OnClickListener {

	private static final String TAG = "PlanetActivity";
	private String[] mStrList = {"水星", "金星", "地球", "火星", "木星", "土星"
			, "天王星", "海王星", "冥王星"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_planet);

		ImageButton ib_back = (ImageButton) findViewById(R.id.ib_back);
		ib_back.setOnClickListener(this);
		TextView tv_title = (TextView) findViewById(R.id.tv_title);
		tv_title.setText("九大行星介绍");
		
		Bundle bundle = getIntent().getExtras();
		int type = bundle.getInt("type");
		if (type == 0) {
			Configuration config = getResources().getConfiguration();
			if(config.orientation == Configuration.ORIENTATION_PORTRAIT){
				showList();
			} else {
				showGrid();
			}
		} else if (type == 1) {
			showList();
		} else {
			showGrid();
		}
	}
	
	private void showList() {
		ViewStub vs_list = (ViewStub) findViewById(R.id.vs_list);
		vs_list.inflate();
		ListView lv_hello = (ListView) findViewById(R.id.lv_hello);
		TitleListAdapter adapter = new TitleListAdapter(this, mStrList);
		lv_hello.setAdapter(adapter);
		lv_hello.setOnItemClickListener(adapter);
		lv_hello.setOnItemLongClickListener(adapter);
	}

	private void showGrid() {
		ViewStub vs_grid = (ViewStub) findViewById(R.id.vs_grid);
		vs_grid.inflate();
		GridView gv_hello = (GridView) findViewById(R.id.gv_hello);
		ContentGridAdapter adapter = new ContentGridAdapter(this, mStrList);
		gv_hello.setAdapter(adapter);
		gv_hello.setOnItemClickListener(adapter);
		gv_hello.setOnItemLongClickListener(adapter);
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.ib_back) {
			finish();
		}
	}
	
}


样式style

样式在res/values/styles.xml中定义,它适用于下面几种情况:
1、布局文件中存在多个具有相同风格的控件,比如说统一的文本框TextView,都是白底黑字、中号字体、居中显示,这时我们便可在styles.xml定义一种文本样式,然后在各文本框处声明它的style属性。好处一个是减少了布局文件的大小,另一个是方便以后统一修改风格。
2、某些控件在代码中声明时需要手工指定style,例如自定义对话框需要在构造函数中指定样式,参见《 Android开发笔记(六十六)自定义对话框》;另一个例子是弹窗PopupWindow在设置伸缩动画方法setAnimationStyle时需要指定动画样式,参见《 Android开发笔记(六十五)多样的菜单》。
3、定义页面的主题风格,然后应用到Activity页面。代码中设置主题可通过“setTheme(R.style.***)”完成,布局中设置可在AndroidManifest.xml的activity节点下添加theme属性,如“android:theme="@style/***"”。


下面是在styles.xml中自定义样式的一个例子
    <style name="middle_text">
        <item name="android:textColor">#000000</item>
        <item name="android:textSize">17sp</item>
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
    </style>
    
    <style name="middle_text_center" parent="@style/middle_text">
        <item name="android:layout_gravity">center_horizontal</item>
        <item name="android:gravity">center</item>
    </style>

布局文件中给控件声明风格属性:
        style="@style/middle_text_center"


主题Theme

主题是一种特殊的样式,主题专用于页面,而样式一般运用于控件。主题定义一般放在themes.xml,样式定义一般放在styles.xml。
Android定义了一些系统主题,完整定义的参见sdk自带的themes.xml,常用的几种说明如下:
Theme.NoTitleBar : 不显示标题栏,即隐藏ActionBar
Theme.Light : 白色背景
Theme.Holo : 浅灰背景
Theme.Black : 黑色背景
Theme.Wallpaper : 壁纸
Theme.Translucent : 透明背景
Theme.Dialog : 对话框
Theme.Panel : 平板
Theme.InputMethod : 输入法
Theme.SearchBar : 搜索框


在代码中给页面运用主题需要在所有视图初始化之前进行,也就是说,setTheme方法必须在setContentView方法之前执行。下面是个代码中设置主题的例子:
setTheme(android.R.style.Theme_Light_NoTitleBar);
在布局中运用主题,只需在activity界面下添加theme属性即可,下面是个布局中添加主题的例子:
android:theme="@android:style/Theme.Dialog"


除了系统自带的主题样式,我们也可以在themes.xml中自定义主题,具体步骤与自定义样式类似。下面是自定义主题时可能变更的窗口属性:
android:windowFrame : 窗口框架图像
android:windowBackground : 窗口背景
android:windowNoTitle : 窗口是否不要标题,即不带ActionBar
android:windowFullscreen : 窗口是否全屏
android:windowIsTranslucent : 窗口是否半透明
android:windowIsFloating : 窗口是否悬浮
android:windowAnimationStyle : 窗口切换动画的样式
android:windowEnterAnimation : 进入窗口的动画
android:windowExitAnimation : 退出窗口的动画
注意:windowFrame并不只是边框区域,还包括内部窗口,所以如果windowFrame设置为不透明的图像,那么内部窗口也将只显示这幅不透明的图像。


确实这三个属性容易混淆:android:windowFrame、android:windowBackground、android:background,文字描述感觉都说的不清楚,下面针对三个属性分别测试一下,看看究竟都是什么效果:
只有android:windowFrame设置为半透明红色的窗口截图
Android开发笔记(七十四)布局文件优化_第2张图片
从截图可以看到,windowFrame的覆盖区域包括窗口与边框,且窗口对内半透明、对外不透明,而边框对外半透明。


只有android:windowBackground设置为半透明红色的窗口截图

从截图可以看到,windowBackground的覆盖区域只有窗口,且窗口对内对外都是半透明。


只有android:background设置为半透明红色的窗口截图
Android开发笔记(七十四)布局文件优化_第3张图片
从截图可以看到,background的覆盖区域只有窗口,且窗口对内半透明、对外不透明





点此查看Android开发笔记的完整目录

你可能感兴趣的:(android,style,theme,ViewStub,include)