概述
屏幕适配是安卓开发绕不开的问题,因为安卓的机型太多,厂商之间的屏幕密度不同,宽高不同,给 安卓开发者带来了不小的麻烦。
好在开源的安卓环境沉淀了不少 针对屏幕适配的方案,本文总结其中一些运用比较广泛的技巧。
约束布局ConstraintLayout
约束布局,是继 LineaLayout,RelativeLayout,之后的又一个主流布局方式。
实际上,它是PercentLayout的优化版。只不过 PercentLayout出现之后很快就被 约束布局替代,所以我们对PercentLayout没什么印象。
很多人不喜欢用,是因为,即使一个很简单的布局要求,用了 约束布局之后,都要设置上下左右的约束条件。
但是相信我,前期在代码中付出得越多,后面操心的也就越少。
约束条件基本使用
顾名思义: app:layout_constraintStart_toStart
表示的就是 当前view的开始坐标与 目标view的开始坐标一致。
其余几个都类似,我们根据布局的需要,灵活设置这几个属性值,就能确定一个View的最终位置。
比如说如下代码:
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorRedDot">
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BBBBBB"
android:background="@color/color_aqua_400"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AAAAAAA"
android:background="@color/color_grey_1000"
android:textColor="@color/white"
android:textSize="30sp"
android:layout_marginTop="60dp"
android:layout_marginLeft="60dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
效果如下:
实现了两个view的覆盖效果,我们将btn_1
和 btn_2
两个view交换位置,发现 两者的覆盖关系也变了。但是始终是写在后面的布局覆盖写在前面的布局。
骚操作1:bias
bias属性,继承了 原来PercentLayout
的特性,支持设置当前view
在水平或者垂直方向的位置百分比。取值范围是0~1
.
而且它贴心地考虑了触边的场景。比如下面的例子:
我们设置了垂直和水平方向的位置百分比。 而且,无论我们的View尺寸有多大,设置约束条件和位置百分比之后,都能保证View始终是在屏幕内显示的。
骚操作2:利用依赖链 平分布局空间
类似这种效果,我们在 LinearLayout 中,可以用 weight权重来解决。
在约束布局中,我们可以这样:
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorRedDot">
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/color_aqua_400"
android:text="TEXT1"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" // 看这里
app:layout_constraintRight_toLeftOf="@id/btn_2" // 看这里
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/color_aqua_400"
android:text="TEXT2"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/btn_1" // 看这里
app:layout_constraintRight_toLeftOf="@id/btn_3" // 看这里
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/color_aqua_400"
android:text="TEXT34222"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/btn_2" // 看这里
app:layout_constraintRight_toRightOf="parent" // 看这里
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
效果如下:
骚操作3:chainStyle
在上面的基础上,如果我们再利用一个属性,就能实现3个按钮排布的不同风格。
看下面的布局:
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorRedDot">
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/color_aqua_400"
android:text="TEXT1"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/btn_2"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/color_aqua_400"
android:text="TEXT2"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/btn_1"
app:layout_constraintRight_toLeftOf="@id/btn_3"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/color_aqua_400"
android:text="TEXT34222"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/btn_2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
约束链中默认 app:layout_constraintHorizontal_chainStyle="spread"
,呈现的就是上图的效果,可是如果我们将它改为: spread_inside
,效果就变成了 分散贴边:
如果我们将它改为: packed
,效果就变成了 集中分布:
骚操作4:ChainStyle 结合bias
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorRedDot">
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/color_aqua_400"
android:text="A"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintHorizontal_bias=".9"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/btn_2"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/holo_green_light"
android:text="B"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/btn_1"
app:layout_constraintRight_toLeftOf="@id/btn_3"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/darker_gray"
android:text="C"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/btn_2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
上面,在app:layout_constraintHorizontal_chainStyle="packed"
基础上,增加了 app:layout_constraintHorizontal_bias=".9"
:
于是将上面ABC3个按钮作为整体进行百分比位置平移。这种简洁优势是 其他布局不具备的。
注意可见属性 visibility
当 为 invisiable
时,仍然会占据空间,但是布局不可见。 当 为gone
时,占据空间为0,但是chain约束链上的其他view仍然能够遵循约束规则。
多dimens基于dp的适配方案
约束布局可以很灵活地决定view的相对位置,除此之外,如果是一个国际化的大项目,可能还会引入 多套dimen来尽可能适配不同的屏幕分辨率和密度。
它利用的其实是,android系统在运行时会自动识别 当前屏幕的最小宽度 ,也就是上图中的 sw
(smallest width
),然后根据识别的结果去对应的资源目录中去查找对应的属性值。
比如,有一个屏幕密度(dpi)为 360的手机运行app时,会自动到上图中的 values-sw360dp
中去找属性值。
这种多dimens的方案有一个自动生成多dimen的工具。
这种方式有很好的容错机制,假设有一个 屏幕密度为 350的手机,可是我们上面并没有350的dimen目录,此时,安卓系统会自动最接近的目录。
UI控件适配
Android app中文本和图片占据了大部分内容。虽然会有 RecyclerView,ScrollView,ViewPager ,但是最终渲染出来的,还是文本和图片居多。
对于文本的宽高,尽量使用 wrap_content
, 并且写布局的时候,尝试使用tools:text
属性,来测试超长文本在布局上的表现。它不会影响到正式的控件,只做调试使用。
图片的适配,不要使用 wrap_content
,而是要使用固定dp,因为ImageView接收的图片往往是网络图片,而网络图片可大可小,如果设置为 wrap_content
,我们就不能控制实际的图片控件大小了。
另一种常见的做法就是,在java代码中动态设置ImageView
的尺寸。 比如,我们要让3个图平分RecyclerView
的横向空间,
总结
本文总结了三种屏幕适配的常见方式,
- 利用约束布局可以预防UI问题
- 利用多dimen的方式可以查漏补缺
- 合理定义文本和图片控件的宽高,保证在任何情况下都能正常显示