Android优化布局的几种方式

优化布局的几种方式

1. include/merge

布局优化中常常用到include/merge标签,include的含义类似C代码中的include,意思是直接把指定布局片段包含进当前的布局文件。include适用于多个布局文件中存在相同的xml片段,比如说相同的标题栏、相同的广告栏、相同的进度栏等等。

如:

<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的加载效率。

2. 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张图片

横屏的网格方式界面截图
Android优化布局的几种方式_第2张图片

布局文件

<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();  
        }  
    }  

} 

3. 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="@style/middle_text_center"

4. 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优化布局的几种方式_第3张图片
从截图可以看到,windowFrame的覆盖区域包括窗口与边框,且窗口对内半透明、对外不透明,而边框对外半透明。

只有android:windowBackground设置为半透明红色的窗口截图:
Android优化布局的几种方式_第4张图片
从截图可以看到,windowBackground的覆盖区域只有窗口,且窗口对内对外都是半透明。

只有android:background设置为半透明红色的窗口截图:
Android优化布局的几种方式_第5张图片
从截图可以看到,background的覆盖区域只有窗口,且窗口对内半透明、对外不透明。

你可能感兴趣的:(android)