三十一、Android屏幕适配的技巧

概述

屏幕适配是安卓开发绕不开的问题,因为安卓的机型太多,厂商之间的屏幕密度不同,宽高不同,给 安卓开发者带来了不小的麻烦。

好在开源的安卓环境沉淀了不少 针对屏幕适配的方案,本文总结其中一些运用比较广泛的技巧。

约束布局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_1btn_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的方式可以查漏补缺
  • 合理定义文本和图片控件的宽高,保证在任何情况下都能正常显示
相关推荐
雪落满地香9 分钟前
前端:改变鼠标点击物体的颜色
前端
余生H42 分钟前
前端Python应用指南(二)深入Flask:理解Flask的应用结构与模块化设计
前端·后端·python·flask·全栈
outstanding木槿1 小时前
JS中for循环里的ajax请求不数据
前端·javascript·react.js·ajax
酥饼~1 小时前
html固定头和第一列简单例子
前端·javascript·html
一只不会编程的猫1 小时前
高德地图自定义折线矢量图形
前端·vue.js·vue
m0_748250931 小时前
html 通用错误页面
前端·html
来吧~1 小时前
vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)
前端·vue.js·音视频
han_1 小时前
不是哥们,我的console.log突然打印不出东西了!
前端·javascript·chrome
魔术师卡颂1 小时前
最近看到太多 cursor 带来的焦虑,有些话想说
前端·aigc·openai
鎈卟誃筅甡1 小时前
Vuex 的使用和原理详解
前端·javascript