一,前期基础知识储备
大屏,全面屏时代到来,屏幕适配从未显得如此重要。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSP9cXTzUEVNdXRU1EM4wmYwhGWhxGZzwEMW1mY1RzRapnTtxkb5ckYplTeMZTTINGMShUYfRHelRHLwEzX39GZhh2css2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3Pn5GcuEjNwUTNwgTM2EDMxkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
如图,一个简单的付费界面,如果完全按照上图的标注来写,那么在传统16:9的手机上表现良好,但在大屏手机上表现就差强人意,无论使用相对布局还是线性布局,都无法做到完整的适配。在正式项目中,就采用了一个笨办法:根据分辨率比例,使用不同的布局:
View view;
if (mScreenRatio > 1.8) {
view = View.inflate(this, R.layout.dialog_prime_permission_max, null);
} else {
view = View.inflate(this, R.layout.dialog_prime_permission, null);
}
UI做两套图,不同分辨率,做到适配。但是不香,而且不灵活,甚至会有其他问题。所以找寻最优解。
1)support包下百分比布局PercentRelativeLayout + PercentFrameLayout;
implementation 'com.android.support:percent:28.0.0'
用宽高百分比确认View的大小和边距Margin值。但是已被谷歌弃用,所以不建议使用。
<ImageView
android:id="@+id/img"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_heightPercent="57.19%"
app:layout_marginTopPercent="3.13%"
app:layout_widthPercent="57.22%" />
2)support包下约束布局ConstraintLayout,真香;
ConstraintLayout, 即约束布局, 在2016年由Google I/O推出. 从支持力度而言, 将成为主流布局样式, 完全代替其他布局, 减少布局的层级, 优化渲染性能.。在新版Android Studio中, ConstraintLayout已替代RelativeLayout, 成为HelloWorld项目的默认布局。ConstraintLayout作为非绑定(Unbundled)的支持库, 命名空间是app
:
, 即来源于本地的包命名空间. 最新版本是1.1.3(2019.10.16)。
“嘴上说不学,身体却很诚实”
ConstraintLayout具有以下优势:
-
较高的性能优势。
布局嵌套层次越高,性能开销越大。而使用ConstraintLayout,经常就一层嵌套就搞定了,所以其性能要好很多。
详细的性能分析可参见:解析ConstraintLayout的性能优势
-
完美的屏幕适配
ConstraintLayout的大小、距离都可以使用比例来设置,所以其适配性更好。
- 书写简单
-
可视化编辑。
ConstraintLayout也有十分方便完善的可视化编辑器,不用写xml也基本上能实现大部分功能。但个人还是比较喜欢写xml,所以本篇文章主要介绍如何使用代码控制。如果想看如何使用可视化编辑器,可以参考郭霖大神的这篇文章
二,上代码,具体实现
添加依赖:
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
定位一个View就是三大元素:位子,Margin,大小;下面就来使用清楚这三个方向。
1)掌握基础用法;
① 属性定位View位置
- 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_constraintBaseline_toBaselineOf
- layout_constraintStart_toEndOf
- layout_constraintStart_toStartOf
- layout_constraintEnd_toStartOf
- layout_constraintEnd_toEndOf
ConstraintLayout约束布局的含义: 根据布局中的其他元素或视图, 确定View在屏幕中的位置, 受到三类约束, 即其他视图, 父容器(parent), 基准线(Guideline)。
layout_constraint[本源位置]_[目标位置]="[目标ID]"
例如:
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
约束当前View的底部至目标View的底部, 目标View是constraintLayout,即,把当前View的底部对齐到constraintLayout的底部。
② 可视化编辑器定位
推荐大腿郭神文章《Android新特性介绍,ConstraintLayout完全解析》
大神出品,必属精品,足够清晰,包看包会。
2)掌握比例(偏移量 / 边距);
ConstraintLayout除了指定约束, 还支持设置比例. Center按钮的全部边界与ConstraintLayout
(父容器)
边界对齐, 则为居中. 同时还可以设置水平与竖直的比例, 如Bias按钮, 在对齐父容器后, 设置水平与竖直的比例均为0.25, 表示左侧与右侧比例是1:4, 上部与下部的比例是1:4。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Center"
app:layout_constraintEnd_toEndOf="@id/constraintLayout"
app:layout_constraintStart_toStartOf="@id/constraintLayout"
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bias"
app:layout_constraintEnd_toEndOf="@id/constraintLayout"
app:layout_constraintStart_toStartOf="@id/constraintLayout"
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
app:layout_constraintHorizontal_bias="0.25"
app:layout_constraintVertical_bias="0.25"/>
</android.support.constraint.ConstraintLayout>
注意,要同时为View加上同一方向上的两个约束,此时使用偏移才会生效。利用此特性,在做适配的时候可以将UI标注好的上下边距或者左右边距转换为百分比例的形式,然后设置为偏移量,实测非常好用。
3)掌握引导线(Guidline);
ConstraintLayout除了与视图约束以外, 还支持与
引导线(
Guidline
)
约束. 如, 设置
竖直引导线(
Guidline
)
距离左侧
72dp
. 两个按钮的左侧都与引导线约束, 上下使用比例方式排列, 一个是0.25, 一个是0.75。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.Guideline
android:id="@+id/guideLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="72dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Guide Up"
app:layout_constraintStart_toStartOf="@id/guideLine"
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
app:layout_constraintVertical_bias="0.25"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Guide Down"
app:layout_constraintStart_toStartOf="@id/guideLine"
app:layout_constraintTop_toTopOf="@+id/constraintLayout"
app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"
app:layout_constraintVertical_bias="0.75"/>
</android.support.constraint.ConstraintLayout>
引导线非常重要,它可以帮助确定View的位置,还可以据此帮助确定View的大小,用四根线划分View区域,View在此填充,用在布局中的关键元素,十分好用。
4)大小(视图尺寸);
ConstraintLayout也支持自动填充宽高, 把宽高设置为
0dp
会根据位置自动填充. 如, Large按钮, 左侧与Small按钮的左侧对齐, 右侧与ConstraintLayout
(父控件)
的右侧对齐, 宽度设置为
0dp
, 则会填充全部空位。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:id="@+id/constraintLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Small"
app:layout_constraintStart_toStartOf="@id/constraintLayout"
app:layout_constraintTop_toTopOf="@id/constraintLayout"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Large"
app:layout_constraintBottom_toBottomOf="@id/constraintLayout"
app:layout_constraintEnd_toEndOf="@id/constraintLayout"
app:layout_constraintStart_toEndOf="@id/small"
app:layout_constraintTop_toTopOf="@id/constraintLayout"/>
</android.support.constraint.ConstraintLayout>
View的大小是重中之重,注意约束布局不鼓励使用match_parent,相反使用wrap_content和0dp非常多,使用0dp和约束规则,很容易实现填充(match_parent)的效果。
三,上代码,具体实现付费界面
这是使用约束布局得到的结果,用不同分辨率的手机实测表现良好。
使用7根引导线,去布置View的位置和控制View的大小,然后根据上下Margin值,去计算竖直方向的偏移量,设置好比例,就可以定下竖直方向的显示问题。具体分析如下:
布局如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#175700">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Join Premium"
android:textSize="24sp"
app:layout_constraintBottom_toTopOf="@+id/viewPager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.56" />
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#A25151"
app:layout_constraintBottom_toTopOf="@+id/guideline9"
app:layout_constraintEnd_toStartOf="@+id/guideline7"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/guideline6"
app:layout_constraintTop_toTopOf="@+id/guideline8"
app:layout_constraintVertical_bias="0.0" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginEnd="2dp"
android:src="@drawable/ic_prime_point_select"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline9" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginEnd="4dp"
android:src="@drawable/ic_prime_point_unselect"
app:layout_constraintEnd_toStartOf="@+id/imageView2"
app:layout_constraintTop_toTopOf="@+id/imageView2" />
<ImageView
android:id="@+id/imageView4"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="2dp"
android:src="@drawable/ic_prime_point_unselect"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline9" />
<ImageView
android:id="@+id/imageView5"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="4dp"
android:src="@drawable/ic_prime_point_unselect"
app:layout_constraintStart_toEndOf="@+id/imageView4"
app:layout_constraintTop_toTopOf="@+id/imageView4" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@+id/guideline4"
app:layout_constraintEnd_toEndOf="@+id/guideline7"
app:layout_constraintStart_toStartOf="@+id/guideline6"
app:layout_constraintTop_toTopOf="@+id/guideline3">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true">
<ImageView
android:id="@+id/imageView6"
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_dot" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="23dp"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginStart="14dp"
android:layout_toRightOf="@id/imageView6"
android:text="60+ film filters"
android:textSize="15sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true">
<ImageView
android:id="@+id/imageView8"
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_dot" />
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="23dp"
android:layout_centerVertical="true"
android:layout_marginStart="14dp"
android:layout_toRightOf="@id/imageView8"
android:text="No Ads"
android:textSize="15sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="23dp"
android:layout_alignParentBottom="true">
<ImageView
android:id="@+id/imageView9"
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_dot" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="23dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="14dp"
android:layout_toEndOf="@id/imageView9"
android:text="Import Photos"
android:textSize="15sp" />
</RelativeLayout>
</RelativeLayout>
<TextView
android:id="@+id/button"
android:layout_width="140dp"
android:layout_height="44dp"
android:layout_marginEnd="5dp"
android:background="@drawable/dialog_buy"
android:gravity="center"
android:text="$2.9/ Year"
android:textSize="15sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline4"
app:layout_constraintVertical_bias="0.562" />
<TextView
android:id="@+id/button2"
android:layout_width="140dp"
android:layout_height="44dp"
android:layout_marginStart="5dp"
android:background="@drawable/dialog_buy"
android:gravity="center"
android:text="$6.9 One time pay"
android:textSize="15sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="@+id/guideline4"
app:layout_constraintVertical_bias="0.562" />
<android.support.constraint.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<android.support.constraint.Guideline
android:id="@+id/guideline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.213" />
<android.support.constraint.Guideline
android:id="@+id/guideline7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.787" />
<android.support.constraint.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.68105066" />
<android.support.constraint.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.82216144" />
<android.support.constraint.Guideline
android:id="@+id/guideline8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.111" />
<android.support.constraint.Guideline
android:id="@+id/guideline9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.62106705" />
</android.support.constraint.ConstraintLayout>
总结一下:
① 使用约束布局去做适配最关键的就是,对于边距值Margin的处理,Ui标注的都是dp,使用时,注意计算为比例,然后作为比例偏移量添加到约束布局中;
② 使用约束布局尽量将布局写活,不使用固定大小或者不鼓励使用match_parent属性,多使用0dp+约束规则实现;
③ 可在约束布局中插入其他ViewGroup,灵活为上。
参考文章
《Android ConstraintLayout百分比布局-适配终结者(基本适配所有机型)》
《约束布局ConstraintLayout看这一篇就够了》