Android常见布局解析
本文概述:
-
本文为Android布局系列文章第一篇,本系列以常见Android布局为主题进行全面深入的探讨;
-
本文重点:线性布局基本使用,线性布局中权重使用,特殊情况下子View宽/高计算公式,如何通过代码设置控件属性;线性布局分割线的两种设置方法;线性布局的局限性(引出相对布局)
-
参考链接:
常见布局种类:
布局细节:
-
布局可以嵌套布局:
- 所有的UI元素都是通过View与ViewGroup构建的。而ViewGroup可作为容器盛装界面中的控件,可包含普通的View控件,也可包含ViewGroup,即ViewGroup可嵌套ViewGroup.
-
View视图嵌套示例
-
从上面的层次结构图,可以看出这些布局都直接或者间接继承自ViewGroup,因此它们也支持在ViewGroup中定义的属性,这些属性可以看作是布局的通用属性。
-
在View里面有个surfaceView,这个东西就不是继承自View的,那么它就不具有View的共有属性,但是也不受因继承自View而带来的限制
-
补充:surfaceView
-
surfaceView效率高使用于动画效果较多的地方,其内部拥有独立的surface(不共享的)
- 内部存在双缓冲模型,使用两张Canvas进行处理
- 其内部mSurface不属于View体系,因此不受ViewRootImpl的影响,但其也不具备View系中的缩放等功能,并且surfaceView不能放在ViewGroup内
-
View绘图效率不高,适用于动画较少
-
-
布局共性:
线性布局:LinearLayout
概述:
此布局方式使用频繁,由orientation可分为水平线性(内部元素水平摆放)与垂直线性(内部元素垂直摆放)
整体思维导图:
线性布局重要属性:权重
定义与使用细节:
-
定义:定义了子View在父容器内所占宽/高比例
-
使用细节:将子View的宽/高设置为0dp,根据业务需要设置gravity属性(整数值A)
- 当前子View所在宽/高(value)计算公式:value = (父容器宽/高)/所有子View权重 * 当前子View所在权重
具体示例一:基本使用
-
业务需求:线性布局A包含线性布局B、线性布局C,水平摆放且B、C各占A的一半
-
实现效果:
-
细节:
-
若需要指定某一子LinearLayout id属性,那么需要指定其orientation 属性
-
为什么会这样:先mark,后面再来查资料
- 猜测:可能与布局解析流程有关,今天晚上回去整理setContentView流程的时候记到看一下这个东西
-
-
-
代码示例:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="ui.LineraLayoutBasic" android:orientation="horizontal" android:id="@+id/layoutA"> <LinearLayout android:id="@+id/layoutB" android:layout_width="0dp" android:layout_height="20dp" android:background="#ADFF2F" android:layout_weight="1" android:orientation="horizontal" /> <LinearLayout android:id="@+id/layoutC" android:layout_width="0dp" android:layout_height="20dp" android:background="#DA70D6" android:layout_weight="2" android:orientation="horizontal" /> </LinearLayout>
具体示例二:当子View均为(match_parent)引出权重计算方法
-
业务需求:线性布局A以水平排列三个子线性布局B、C、D且子布局占比分为1:2:3
-
代码效果:
-
布局代码展示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="ui.LineraLayoutBasic" android:orientation="horizontal" android:id="@+id/layoutA"> <TextView android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" android:text="子线性布局A" android:textSize="35sp" android:background="#98FB98" /> <TextView android:layout_weight="2" android:layout_width="match_parent" android:layout_height="match_parent" android:text="子线性布局B" android:textSize="35sp" android:background="#FFFF00" /> <TextView android:layout_weight="3" android:layout_width="match_parent" android:layout_height="match_parent" android:text="子线性布局C" android:textSize="35sp" android:background="#FF00FF" /> </LinearLayout>
-
出现问题:线性布局C消失了,并且线性布局A :线性布局B = 2 :1
-
计算公式:
-
因为三个子线性布局均为match_parent--->意味着子View共需要3块屏幕,但实际上就只有一块
- 那么 1 - 3 = -2
-
子View比例为:1:2:3
- 计算线性布局A占比: 1 - 2 * (1/6) = 2/3
- 计算线性布局B占比: 1 - 2 * (2/6) = 1/3
- 计算线性布局C占比: 1 - 2 * (3/6) = 0/3
-
-
备注:在日常开发中不会这样写,若需要以权重限定子view宽/高,需结合父容器orientation属性,设置0dp(ex:父容器水平摆放时,子view宽度设为0dp)
-
为什么会出现这个问题?
-
具体示例三:通过 Java代码设置控件属性
-
运行结果:可以观察到xml预设(1:1),实际运行时(2:1)
-
Java 代码展示
package ui; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.LinearLayout; import android.widget.TextView; import com.cdqiantuo.layoutest.R; public class LineraLayoutSuper extends AppCompatActivity { private TextView textViewA; private TextView textViewB; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_linera_layout_super); textViewA = findViewById(R.id.textviewA); textViewB = findViewById(R.id.textviewB); //设置权重 setWeight(textViewA,2); setWeight(textViewB,1); } public void setWeight(TextView textView,int weight){ LinearLayout.LayoutParams text =new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,weight); textView.setLayoutParams(text); //此处我需要均分高度就在heignt处设0,1.0f即设置权重是1,页面还有其他一个控件,1:1高度就均分了 } }
线性布局重要属性:分割线
定义与用途:
- 常用于分割子View,使得界面美观
具体示例一:以view 标签充当子View分割
-
业务需求:在线性布局中水平摆放两个控件,以view 标签作为二者分割符
-
实现效果:
-
代码展示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="ui.LineraLayoutSuper" android:orientation="horizontal"> <TextView android:text="TextView A" android:id="@+id/textviewA" android:layout_width="200dp" android:layout_height="match_parent" android:background="@color/white"/> <View android:layout_height="match_parent" android:layout_width="5px" android:background="#190C29" /> <TextView android:text="TextView B" android:id="@+id/textviewB" android:layout_width="200dp" android:layout_height="match_parent" android:background="@color/white"/> </LinearLayout>
具体示例二:使用 LinearLayout的divider属性
-
使用描述:实质上就是将图片充当分割线
- 分割线图片:为了效果明显,将图片设置较大
-
重要属性:设置父容器属性
-
设置分割线图片路径
android:dividerPadding="10dp"
-
设置分割线在子View中的位置
android:showDividers="middle"
-
设置分割线的Padding
android:dividerPadding="10dp"
-
-
实现效果:
-
完整布局代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="ui.LineraLayoutSuper" android:orientation="vertical" android:divider="@drawable/divider_line" android:showDividers="middle" android:dividerPadding="10dp" > <TextView android:text="TextView A" android:textSize="35sp" android:id="@+id/textviewA" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white"/> <TextView android:text="TextView B" android:textSize="35sp" android:id="@+id/textviewB" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white"/> <TextView android:text="TextView C" android:textSize="35sp" android:id="@+id/textviewC" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white"/> </LinearLayout>
线性布局的局限:无法处理子View相对摆放
业务需求:
- 线性布局水平摆放两个元素(TextViewA 与 TextViewB),希望设置A在左边,B在右边
尝试操作一:设置子View的layout_gravity属性(子View相对于父容器的摆放位置)
-
布局代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="ui.LineraLayoutSuper" android:orientation="horizontal" > <TextView android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="left" android:background="#FF7878" android:gravity="center" android:text="左边" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="right" android:background="#FF7428" android:gravity="center" android:text="右边" /> </LinearLayout>
-
运行效果:
-
问题出现原因:
- 当 android:orientation="vertical" 时, 只有水平方向的设置才起作用,垂直方向的设置不起作用。 即:left,right,center_horizontal 是生效的。 当 android:orientation="horizontal" 时, 只有垂直方向的设置才起作用,水平方向的设置不起作用。 即:top,bottom,center_vertical 是生效的。
补充:将上例父容器orientation设为vertical,出现一个怪怪的效果
-
测试代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout …… android:orientation="vertical" > …… </LinearLayout>
-
运行效果:
-
出现原因:为什么会发生这种事情?
- 这个跟orientation优先级有关
-
解决手段:此时就应该引入相对布局处理问题