今天讲一些比较常用的布局控件,还有一些不常用的就不做介绍了。
- LinearLayout
- RelativeLayout
- FrameLayout
- ConstraintLayout
LinearLayout
线性布局,可以设置横向和纵向,效果就是挨着往下排。看看效果:
xml代码:
<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="10dp">
<TextView
android:text="线性布局,垂直方向"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:text="线性布局,垂直方向"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
水平方向:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:text="线性布局,垂直方向"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_marginStart="20dp"
android:text="线性布局,垂直方向"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
可以看到控制方向的属性为:android:orientation="horizontal"和android:orientation="vertical"
LinearLayout还是比较简单,另外需要介绍的一个属性是:权重 android:weightSum,意思是当前的布局分成多少份。
每个子控件通过使用android:layout_weight来设置自身占多少。
xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="5">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:background="@android:color/darker_gray"
android:text="权重:占2/5" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:background="@android:color/holo_red_light"
android:text="权重:占3/5" />
</LinearLayout>
上面的代码将布局分成了5份,第一个TextView占2份,第二个TextView占3份。
LinearLayout的weightSum其实也可以不设置,总份数就是每个子控件占比的总和。
RelativeLayout
相对布局,这种布局对比LinearLayout更加的灵活一点,LinearLayout只能按照一个方向顺序的排下去。介绍RelativeLayout就必须了解与它相关的属性:
下面再来看看例子:
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/acMainTvOne"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:text="相对父控件开始位置" />
<TextView
android:id="@+id/acMainTvTwo"
android:layout_marginTop="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/acMainTvOne"
android:text="在指定控件下方" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_toRightOf="@id/acMainTvOne"
android:text="在指定控件右边" />
<TextView
android:layout_centerInParent="true"
android:text="父控件水平和垂直都居中"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
FrameLayout
该布局的所有元素都默认在左上角,一层一层的网上叠加。
xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/acMainTvOne"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第一个TextView" />
<TextView
android:id="@+id/acMainTvTwo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FrameLayout 第二个TextView" />
</FrameLayout>
但是我们还是可以通过android:layout_gravity属性来指定位置
xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/acMainTvOne"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="第一个TextView" />
<TextView
android:id="@+id/acMainTvTwo"
android:layout_width="wrap_content"
android:layout_gravity="right"
android:layout_height="wrap_content"
android:text="FrameLayout 第二个TextView" />
</FrameLayout>
layout_gravity的相关取值可以查看这里:https://blog.csdn.net/zgy441008825/article/details/106558947有比较详细的描述。
ConstraintLayout
约束布局,更加高级和灵活的RelativeLayout。是后面才推出的一种新布局,所以在使用之前要先引入包:
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
大家可以查看一下官方的文档:https://developer.android.google.cn/reference/android/support/constraint/ConstraintLayout
其实现在网上有很多关于约束布局的优秀文章,我这里列一些大家可以参考一下:
https://blog.csdn.net/qq_33721320/article/details/93859756
https://www.jianshu.com/p/17ec9bd6ca8a
https://www.jianshu.com/p/fa2dc62a0f70
这里就讲一下我们为什么要使用约束布局,其实就是它能解决什么问题?
在实际项目中我们界面布局一般都是非常复杂的,单独使用之前的某一种布局很难实现效果,我们就需要通过各种不同的布局来组合、嵌套来实现,这就带来另外一个问题,我们的界面太复杂,启动activity的时候系统解析xml文件太耗时导致启动界面变慢。约束布局就是为此而生的。
下面我们来看看我们使用普通的布局文件来做一个界面和约束布局的界面效果。
我们简单做一个音乐播放界面
差不多这样的效果
xml:
<?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:background="#114B7D"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="4">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_baseline_favorite_24" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_download" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_msg" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_baseline_more_horiz_24" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<TextView
android:id="@+id/acMainTvTimeStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:text="00:00"
android:textColor="@android:color/white" />
<View
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="5dp"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_toStartOf="@id/acMainTvTimeEnd"
android:layout_toEndOf="@id/acMainTvTimeStart"
android:background="@drawable/drawable_progress" />
<TextView
android:id="@+id/acMainTvTimeEnd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="00:00"
android:textColor="@android:color/white" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<ImageView
android:id="@+id/acMainImgMode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_baseline_repeat_24" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@id/acMainImgStart"
android:src="@drawable/ic_last" />
<ImageView
android:id="@+id/acMainImgStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/ic_baseline_play_circle_filled_24" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/acMainImgStart"
android:src="@drawable/ic_next" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_baseline_playlist_play_24" />
</RelativeLayout>
</LinearLayout>
布局的内容比较多,最外层是一个LinearLayout,垂直方向的布局,里面主要嵌套了3个布局,第一个也是LinearLayout,水平方向,4个图标平均分配。第二个和第三个都是相对布局。
借助android studio的界面分析工具我们可以来看看界面的层次
可以看到整个层次的嵌套还是非常多的。如果我们使用约束布局,可以做到我们自己内部的布局只用一层来完成。
xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:background="#114B7D"
android:orientation="vertical"
android:padding="10dp"
tools:ignore="MissingConstraints">
<ImageView
android:id="@+id/acMainImgFavorite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_baseline_favorite_24"
app:layout_constraintEnd_toStartOf="@id/acMainImgDownload"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/acMainImgDownload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_download"
app:layout_constraintEnd_toStartOf="@+id/acMainImgMsg"
app:layout_constraintStart_toEndOf="@id/acMainImgFavorite" />
<ImageView
android:id="@+id/acMainImgMsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_msg"
app:layout_constraintEnd_toStartOf="@+id/acMainImgMore"
app:layout_constraintStart_toEndOf="@+id/acMainImgDownload" />
<ImageView
android:id="@+id/acMainImgMore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_baseline_more_horiz_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/acMainImgMsg" />
<TextView
android:id="@+id/acMainTvTimeStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginTop="10dp"
android:text="00:00"
android:textColor="@android:color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/acMainImgFavorite" />
<View
android:id="@+id/progressBar"
android:layout_width="0dp"
android:layout_height="5dp"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_toStartOf="@id/acMainTvTimeEnd"
android:layout_toEndOf="@id/acMainTvTimeStart"
android:background="@drawable/drawable_progress"
app:layout_constraintBottom_toBottomOf="@+id/acMainTvTimeStart"
app:layout_constraintEnd_toStartOf="@+id/acMainTvTimeEnd"
app:layout_constraintStart_toEndOf="@+id/acMainTvTimeStart"
app:layout_constraintTop_toTopOf="@+id/acMainTvTimeStart" />
<TextView
android:id="@+id/acMainTvTimeEnd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="00:00"
android:textColor="@android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/acMainTvTimeStart" />
<ImageView
android:id="@+id/acMainImgMode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_baseline_repeat_24"
app:layout_constraintBottom_toBottomOf="@+id/acMainImgStart"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/acMainImgStart" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@id/acMainImgStart"
android:src="@drawable/ic_last"
app:layout_constraintBottom_toBottomOf="@+id/acMainImgStart"
app:layout_constraintEnd_toStartOf="@+id/acMainImgStart"
app:layout_constraintTop_toTopOf="@+id/acMainImgStart" />
<ImageView
android:id="@+id/acMainImgStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="10dp"
android:src="@drawable/ic_baseline_play_circle_filled_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/acMainTvTimeStart" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/acMainImgStart"
android:src="@drawable/ic_next"
app:layout_constraintBottom_toBottomOf="@+id/acMainImgStart"
app:layout_constraintStart_toEndOf="@+id/acMainImgStart"
app:layout_constraintTop_toTopOf="@+id/acMainImgStart" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_baseline_playlist_play_24"
app:layout_constraintBottom_toBottomOf="@+id/acMainImgStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/acMainImgStart" />
</androidx.constraintlayout.widget.ConstraintLayout>
可以看到我们整个内容都只是在一个布局中完成,这样可以有效的减少嵌套从而提升界面加载速度。
另外再介绍2个我们在xml中会经常使用的关键字,他们并不是控件或者布局:
- include :在xml中使用,作用是直接将制定的布局原封不动的插入到指定位置。layout:<include layout="@layout/activity_main"/>
- merge:它是作为根节点出现,一般我们自定义ViewGroup加载的布局文件使用。比如我们刚刚的播放控件,我们创建一个类,继承自ConstraintLayout,我们再在代码里面使用layoutInflater加载当前的布局,如果不适用merge作为根节点,那么加载出来的界面会多一层布局。merge会消除一层嵌套,不过需要注意的是我们在做布局的时候还是应该使用我们需要的布局作为根节点,因为merge会消除所有的属性,指定的属性都不起作用,在做完布局之后再改成merge。另外一个需要注意的就是我们自定义的ViewGroup继承类要和xml里面的相匹配。什么意思呢?比如我们刚刚的xml根节点是约束布局,那么我们就必须继承ConstraintLayout,如果是之前的布局我们使用的是LinearLayout,并且是垂直方向的,那么我们就必须继承LinearLayout,并且在代码里面设置垂直方向。
好了,今天的布局就讲这些了。