安卓常见布局
- FrameLayout 所有子元素都叠放在左上角
- LinearLayout 按照垂直和水平顺序依次排列子元素,尽量少用 layout_weight,会导致二次测量
- TableLayout 为表格布局,适用于 N 行 N 列的布局格式
- RelativeLayout 按照各个子元素之间相对位置完成布局
- ConstrainLayout 功能更强大,通过各种约束,可以实现更加扁平化的页面布局
ConstraintLayout 布局
基本属性
layout_constraintLeft_toLeftOf // 左边左对齐
layout_constraintLeft_toRightOf // 左边右对齐
layout_constraintRight_toLeftOf // 右边左对齐
layout_constraintRight_toRightOf // 右边右对齐
layout_constraintTop_toTopOf // 上边顶部对齐
layout_constraintTop_toBottomOf // 上边底部对齐
layout_constraintBottom_toTopOf // 下边顶部对齐
layout_constraintBottom_toBottomOf // 下边底部对齐
layout_constraintStart_toEndOf // 起始边向尾部对齐
layout_constraintStart_toStartOf // 起始边向起始边对齐
layout_constraintEnd_toStartOf // 尾部向起始边对齐
layout_constraintEnd_toEndOf // 尾部向尾部对齐
layout_constraintBaseline_toBaselineOf // 文字的底部线对齐,用于含文本的控件对齐基线
layout_constraintDimensionRatio // 宽高比"2:1"、"H,2:1"或"W,2:1"
Barriar
<androidx.constraintlayout.widget.Barrier
android:id="@+id/id_barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierAllowsGoneWidgets="true"
app:barrierDirection="right"
app:constraint_referenced_ids="flow1,flow2" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/id_barrier2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierAllowsGoneWidgets="true"
app:barrierDirection="left"
app:constraint_referenced_ids="flow1,flow2" />
<TextView
android:id="@+id/text"
android:layout_width="54dp"
android:layout_height="46dp"
android:background="@android:color/holo_red_dark"
android:gravity="center"
android:text="see"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="@+id/id_barrier1"
app:layout_constraintRight_toRightOf="@+id/id_barrier2"
app:layout_constraintTop_toTopOf="parent" />
Guideline
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="60dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/holo_red_dark"
android:gravity="center"
android:text="see"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="@+id/guideline1"
app:layout_constraintLeft_toLeftOf="@+id/guideline3"
app:layout_constraintRight_toRightOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline2"
app:layout_constraintVertical_bias="0.531" />
Chains & Weight & Bias
<TextView
android:id="@+id/tab1"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="@+id/guideline1"
app:layout_constraintRight_toLeftOf="@+id/tab2" />
<TextView
android:id="@+id/tab2"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toRightOf="@+id/tab1"
app:layout_constraintRight_toLeftOf="@+id/tab3" />
<TextView
android:id="@+id/tab3"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toRightOf="@+id/tab2"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/tab4"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintLeft_toRightOf="@+id/guideline1"
app:layout_constraintRight_toLeftOf="@+id/tab5"
app:layout_constraintTop_toBottomOf="@+id/tab1" />
<TextView
android:id="@+id/tab5"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintLeft_toRightOf="@+id/tab4"
app:layout_constraintRight_toLeftOf="@+id/tab6"
app:layout_constraintTop_toBottomOf="@+id/tab1" />
<TextView
android:id="@+id/tab6"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintLeft_toRightOf="@+id/tab5"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tab1" />
<TextView
android:id="@+id/tab7"
android:layout_width="0dp"
android:layout_height="40dp"
app:layout_constraintHorizontal_weight="3"
app:layout_constraintLeft_toRightOf="@+id/guideline1"
app:layout_constraintRight_toLeftOf="@+id/tab8"
app:layout_constraintTop_toBottomOf="@+id/tab4" />
<TextView
android:id="@+id/tab8"
android:layout_width="0dp"
android:layout_height="40dp"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/tab7"
app:layout_constraintRight_toLeftOf="@+id/tab9"
app:layout_constraintTop_toBottomOf="@+id/tab4" />
<TextView
android:id="@+id/tab9"
android:layout_width="0dp"
android:layout_height="40dp"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/tab8"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tab4" />
<TextView
android:id="@+id/tab10"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="@+id/guideline1"
app:layout_constraintRight_toLeftOf="@+id/tab11"
app:layout_constraintTop_toBottomOf="@+id/tab7" />
<TextView
android:id="@+id/tab11"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/tab10"
app:layout_constraintRight_toLeftOf="@+id/tab12"
app:layout_constraintTop_toBottomOf="@+id/tab7" />
<TextView
android:id="@+id/tab12"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/tab11"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tab7" />
<TextView
android:id="@+id/tab13"
app:layout_constraintHorizontal_bias="0.2"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="@+id/guideline1"
app:layout_constraintRight_toLeftOf="@+id/tab14"
app:layout_constraintTop_toBottomOf="@+id/tab10" />
<TextView
android:id="@+id/tab14"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/tab13"
app:layout_constraintRight_toLeftOf="@+id/tab15"
app:layout_constraintTop_toBottomOf="@+id/tab10" />
<TextView
android:id="@+id/tab15"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@+id/tab14"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tab10" />
Group
Group帮助你对一组控件进行设置。最常见的情况是控制一组控件的visibility。你只需把控件的id添加到Group,就能同时对里面的所有控件进行操作。使用方法见下面的例子:
<android.support.constraint.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="flow1,flow2" />
过度绘制
开发者选项(调试GPU过度绘制)
常用优化手段
- 移除 Window 默认背景:getWindow().setBackgroundDrawable(null)
- 移除不必要的背景:如果 ViewPager 中的每个页面都设置了背景,那就可以不用给 Activity 的根布局设置背景
- 减少透明度的使用:由于需要混色处理,至少渲染两次
- 扁平化布局层级:使用RelativeLayout、ConstraintLayout
- 使用优化标签:include、merge、viewstub
include 标签
常用于将布局中的公共部分提取出来供其他 layout 共用,以实现布局模块化
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--测试layout和include都设置ID的情况-->
<include
android:id="@+id/tb_toolbar"
layout="@layout/include_toolbar" />
<!--如果只有单个include这样写就可以,加载的布局的子View,直接findViewByID就能找到-->
<include layout="@layout/include_text" />
<!--如果有多个include,需要添加ID属性-->
<include
android:id="@+id/include_text1"
layout="@layout/include_text" />
<!--如果要使用layout_margin这样的属性,要同时加上layout_w/h属性,不然没反应-->
<include
android:id="@+id/include_text2"
layout="@layout/include_text_relative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="50dp" />
</LinearLayout>
Activity中实例化子View:
private void initView() {
//如果include布局根容器和include标签中的id设置的是不同的值,这里获取的mToolbar值将为null
Toolbar mToolbar = (Toolbar) findViewById(R.id.tb_toolbar);
setSupportActionBar(mToolbar);
//普通include标签用法,直接拿子View属性实现
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText("不加ID实现的include标签");
//多个include标签用法,添加ID,findViewByID找到layout,再找子控件
View view_include = findViewById(R.id.include_text1);
TextView view_include_textView = (TextView) view_include.findViewById(R.id.textView);
view_include_textView.setText("加了ID实现的include标签");
//多个include标签用法,需要为include标签添加ID,findViewByID找到layout,再找子控件
View view_include_Relative = findViewById(R.id.include_text2);
TextView view_textView_relative = (TextView) view_include_Relative.findViewById(R.id.textView);
view_textView_relative.setText("加了ID实现的include标签(RelaviteLayout)");
}
使用 include 标签注意事项:
- 如果 XML 布局文件有多个 include 标签,那么需要为每个 include 标签设置 ID,这样才能找到相应子 View 的控件
- include 标签如果使用 layout_xx 属性,会覆盖待加载布局文件的根节点对应的属性
- include 标签设置的 ID 会覆盖被 include 的 XML 文件根节点的 ID,建议设置同名的 ID,不然有可能会报空指针异常
- 如果要在 include 标签下使用 layout_margin 等其他属性,需要同时设置 layout_width 和 layout_height,不然属性不生效
merge 标签
主要用于辅助 include 标签,该标签可以消除视图层次结构中的冗余视图
merge 标签使用场景:
- 根布局是 FrameLayout 且不需要设置 background 或 padding 等属性,可以用 merge 代替,因为 mContentView 就是 FrameLayout
- 建议被 include 的布局使用 merge 作为该布局的顶节点,这样在被引入时顶结点会自动被忽略
- 自定义 View 如果继承自 ViewGroup,建议让自定义 View 的布局文件根布局设置成 merge,这样能少一层结点
使用merge标签注意事项:
- 通过 LayoutInflate.inflate 方法渲染时,第二个参数必须指定一个父容器,且第三个参数必须为 true,也就是必须为 merge 下的视图指定一个父亲节点
- 因为 merge 不是View,对 merge 标签设置的所有属性都是无效的
- merge 标签必须在根布局使用
- ViewStub 标签中的 layout 布局不能使用 merge 标签
viewstub 标签
最大的优点是当你需要时才会加载,使用它并不会影响UI初始化时的性能。各种不常用的布局像进度条、显示错误消息等可以使用 viewStub 标签,以减少内存使用量,加快渲染速度。viewStub 是一个不可见的,实际上是把宽高设置为0的 View,效果有点类似普通的 view.setVisible,但性能体验提高不少
XML标签如下:
<ViewStub
<!--android:id:重写ViewStub的父布局控件的Id-->
android:id="@+id/stub_import"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
<!--android:layout:设置ViewStub被inflate的布局-->
android:inflatedId="@+id/panel_import"
android:layout="@layout/progress_overlay" />
对应代码如下:
private void showNetError() {
// not repeated inflate
if (networkErrorView != null) {
//setVisibility()方式加载布局,加载次数不限
networkErrorView.setVisibility(View.VISIBLE);
return;
}
//inflate()方式加载布局,只能加载一次
ViewStub stub = (ViewStub) findViewById(R.id.network_error_layout);
stub.setOnInflateListener(this);
networkErrorView = stub.inflate();
Button networkSetting = (Button) networkErrorView.findViewById(R.id.network_miss);
networkSetting.setOnClickListener(this);
Button refresh = (Button) findViewById(R.id.network_refresh);
refresh.setOnClickListener(this);
}
ViewStub 标签注意事项:
- ViewStub 标签不支持 merge 标签
- ViewStub 的 inflate 只能被调用一次,第二次调用会抛出异常
- ViewStub 标签如果设置了 android:layout_XX 属性,会覆盖待加载布局文件的根节点对应的属性