Como fazer com que um conjunto de códigos se adapte perfeitamente a várias telas?

O objetivo da adaptação

Diferente do iOS, os dispositivos Android têm diferentes tamanhos de resolução e sistemas de diferentes fabricantes. Para a resolução atual do mercado, você pode ver as estatísticas relevantes de Umeng.

imagem.png

Pode-se ver que existem mais de 10 resoluções mainstream. Quando nenhuma adaptação é feita, o efeito de um conjunto de códigos em diferentes dispositivos é muito grande, muito pequeno, truncado e deixado em branco. Como esse conjunto de códigos pode ser perfeitamente exibido em dispositivos diferentes? Você pode ver alguns esquemas de adaptação abaixo.

2. Adaptação da interface do usuário

2.1. Métodos comuns de adaptação

2.1.1, adaptação de controle de layout xml

  1. Evite escrever a largura e a altura da View, tente usar warp_content e match_parent;
  2. O layout pai é LinearLayout , escolha usar o atributo android:layout_weight para definir o peso de cada View filho no layout;
  3. O layout pai é RelativeLayout, você pode optar por usar atributos como layout_centerInParent para definir a posição relativa do View filho;
  4. O Google forneceu oficialmente um método de layout de porcentagem nas versões anteriores:suporte: por cento, ele suporta o layout de porcentagem de RelativeLayout e FrameLayout, mas não é mais mantido oficialmente e é substituído por um novo layout: ConstraintLayout , ConstraintLayout é poderoso não apenas porque pode executar layout de porcentagem, mas também posicionamento relativo e posicionamento de ângulo , restrições de tamanho, proporção, layout de cadeia, etc., podem ser manipulados facilmente em diferentes dispositivos.

2.1.2, adaptação de imagem

  1. A imagem .9
    image.9.png é essencialmente uma imagem png. Comparada com a imagem png comum, a imagem .9 pode fazer a imagem esticar na posição especificada e exibir o conteúdo na posição especificada sem distorção;
  2. Consulte 2.1.4 Qualificadores de Resolução;

2.1.3, de acordo com a adaptação do projeto do produto

所谓产品设计适配,指的是产品流程在不同设备上有不同的展示方式,例如手机与Pad的区别,在手机设备上,一般来说具体Item列表是一个页面,点击每个Item会跳转至新的详情页;而在宽度>高度的Pad上,为了防止页面空白浪费,一般会要求屏幕左侧为Item列表,右侧即详情页,item与详情页会同时出现在用户的视觉内,如下图

Pad.png

关于这种类型的设计,其实郭霖《第一行代码》给出了一个方案,我在这里抛砖引玉一下,给出基本思路。

这种情况下,适配的核心在于利用android动态加载布局的机制,使得程序能够根据分辨率或者屏幕大小在运行时动态加载不同的布局,而动态加载就需要使用到限定符

  • 限定符 所谓限定符,指的是给res目录中的子目录加上“-限定符”,可以给不同设备提供不同的资源以及布局,如下图,layout添加-large,-small。

imagem.png

layout-small:指的是提供给小屏幕设备的资源;
layout-large:指的是提供给大屏幕设备的资源;
layout/layout-normal:指的是提供给中等屏幕设备的资源,也就是默认状态;
layout-xlarge:值得是提供给超大屏幕设备的资源;

在上面所提出的情景下,Pad即指的大屏幕,手机一般可看作为中等屏幕设备,为了在大屏幕下显示双页模式,我们可以在layout-large和layout目录下新建同一个name的布局xml,在layout-large下的xml针对Pad做双页处理,即左半边View+右半边View样式,layout目录下xml还是做普通处理。

在最后项目运行时,会根据不同设备来加载不同目录下的xml资源,即Pad会加载layout-large目录下的xml,普通手机设备会加载layout目录下的xml资源。

从而实现一套代码在不同设备上产品逻辑。

限定符可以大范围的区分设备,但是你还是不知道-large代表是多大的设备,-small代表的是多小的设备,如果需要清楚的区分各个屏幕的大小,那就需要用到最小宽度限定符。

  • 最小宽度限定符(Smallest-width Qualifier),简称SW 最小宽度限定符指的是,对屏幕的宽度设立一个最小的值(dp),当当前设备屏幕宽度大于这个值就加载一个布局,

imagem.png

例如在res下新建一个layout-sw720dp的文件夹,当屏幕宽度大于720dp时,项目就会加载layout-sw720dp/***.xml 资源文件。

2.1.4、限定符适配

在2.1.3中提到了限定符的概念,也解决了一部分的设计适配问题,但是还有一些限定符的概念没有涉及到,该目录下将会提到不同的限定符的概念,可以结合2.1.3一起食用。

  • 分辨率限定符 在Android项目中,会把放置图片资源的文件夹分为drawable-hdpi、xhdpi xxhdpi xxxhdpi等,这些指的就是分辨率限定符。

Andriod系统会根据手机屏幕的大小及屏幕密度去选择不同文件夹下的图片资源,以此来实现在不同大小不同屏幕分辨率下适配的问题。

这里提一点AS对图片资源的匹配规则:

举个例子,当当前的设备密度为xhdpi,此时代码中ImageView需要去引用drawable中的图片,那么根据匹配规则,系统首先会在drawable-xhdpi文件夹中去搜索,如果需要的图片存在,那么直接显示;如果不存在,那么系统将会开始从更高dpi中搜索,例如drawable-xxhdpi,drawable-xxxhdpi,如果在高dpi中搜索不到需要的图片,那么就会去drawable-nodpi中搜索,有则显示,无则继续向低dpi,如drawable-hdpi,drawable-mdpi,drawable-ldpi等文件夹一级一级搜索.

当在比当前设备密度低的文件夹中搜到图片,那么在ImageView(宽高在wrap_content状态下)中显示的图片将会被放大.图片放大也就意味着所占内存也开始增多.这也就是为什么分辨率不高的图片随意放置在drawable中也会出现OOM,而在高密度文件夹中搜到图片,图片在该设备上将会被缩小,内存也就相应减少。

在理想的状态下,不同dpi的文件下应该放置相应dpi的图片资源,以对不同的设备进行适配。

  • 尺寸限定符和最小宽度限定符 见2.1.3

  • 屏幕方向限定符 屏幕方向限定符即“-land”、“-port”,分别代表横排和竖屏。

手机会存在横竖屏切换的场景,当设备横屏时,会主动加载layout-land/目录下的资源文件,当设备为竖屏时,则加载layout-port目录下的资源文件。

2.2、今日头条适配方式

在开始今日头条的适配方案之前,需要提及px、dpi、density的概念。

px:即像素,我们常看到的480 * 800 、720 * 1280、1080 * 1920指的就是像素值宽高的意思;

dpi:即densityDpi,每英寸中的像素数;

density:屏幕密度,density = dpi / 160;

scaledDensity:字体的缩放因子,正常情况下和density相等,但是调节系统字体大小后会改变这个值

android中的dp在渲染前会将dp转为px,计算公式:

  • px = density * dp

从dp和px的转换公式 :px = dp * density 可以看出,如果设计图宽为360dp,想要保证在所有设备计算得出的px值都正好是屏幕宽度的话,我们只能修改 density 的值。这就是该方案的核心。

那如何修改系统的density?

可以通过DisplayMetrics获取系统density和scaledDensity值,

val displayMetrics = application.resources.displayMetrics

val density = displayMetrics.density
val scaledDensity = displayMetrics.scaledDensity

设配的目的在于使用一套设计稿,能完好的展示在不同设备上,所以UI需要确定一个固定的尺寸,依据density=px / dp的公式,确定density的值,其中px指的是真实设备的值, 这里我们以设计稿的宽度作为一个纬度进行测算。

举个例子,如设计稿中固定宽度为360dp,当前设备的屏幕宽度为720,那么density = 720 / 360 = 2,其中当前设备的屏幕宽度也可以用DisplayMetrics来获取:

val targetDensity = displayMetrics.widthPixels / 360

整体思路

//0.获取当前app的屏幕显示信息
val displayMetrics = application.resources.displayMetrics
if (appDensity == 0f) {
    //1.初始化赋值操作 获取app初始density和scaledDensity
    appDensity = displayMetrics.density
    appScaleDensity = displayMetrics.scaledDensity
}

/*
 2.计算目标值density, scaleDensity, densityDpi
 targetDensity为当前设备的宽度/设计稿固定的宽度
 targetScaleDensity:目标字体缩放Density,等比例测算
 targetDensityDpi:density = dpi / 160 即dpi = density * 160
 */
val targetDensity = displayMetrics.widthPixels / WIDTH
val targetScaleDensity = targetDensity * (appScaleDensity / appDensity)
val targetDensityDpi = (targetDensity * 160).toInt()

//3.替换Activity的density, scaleDensity, densityDpi
val dm = activity.resources.displayMetrics
dm.density = targetDensity
dm.scaledDensity = targetScaleDensity
dm.densityDpi = targetDensityDpi

三、刘海屏适配

imagem.png imagem.png
  • 有状态栏的界面:刘海区域会显示状态栏,无需适配;
  • 全屏界面:刘海区域可能遮挡内容,需要适配;

针对刘海屏适配,在Android P以上,谷歌官方给出了适配方案,可参考developer.android.google.cn/guide/topic… ,所以在 targetApi >= 28 上可以使用谷歌官方推荐的适配方案进行刘海屏适配。 而在Android O的设备上,如华为、小米、oppo等厂商给出了适配方案。

3.1、Android9.0官方适配

将内容呈现到刘海区域中,则可以使用 WindowInsets.getDisplayCutout() 来检索 DisplayCutout 对象,同时可以使用窗口布局属性 layoutInDisplayCutoutMode 控制内容如何呈现在刘海区域中。

layoutInDisplayCutoutMode

  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT : No modo retrato, o conteúdo será renderizado na área do entalhe; mas no modo paisagem, o conteúdo exibirá bordas pretas.
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES: Tanto no modo retrato quanto no modo paisagem, o conteúdo será renderizado na área do entalhe.
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER: O conteúdo nunca é renderizado na área de franja.
/**
 * @param mode 刘海屏下内容显示模式,针对Android9.0
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0; //在竖屏模式下,内容会呈现到刘海区域中;但在横屏模式下,内容会显示黑边
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;//不允许内容延伸进刘海区
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 1;//在竖屏模式和横屏模式下,内容都会呈现到刘海区域中
 */
@RequiresApi(Build.VERSION_CODES.P)
private fun setDisplayCutoutMode(mode: Int) {
    window.attributes.apply {
        this.layoutInDisplayCutoutMode = mode
        window.attributes = this
    }

}

Determine se o dispositivo atual tem franja:

/**
 * 判断当前设备是否有刘海
 */
@RequiresApi(Build.VERSION_CODES.P)
private fun hasCutout(): Boolean {
    window.decorView.rootWindowInsets?.let {
        it.displayCutout?.let {
            if (it.boundingRects.size > 0 && it.safeInsetTop > 0) {
                return true
            }
        }
    }
    return false
}

Defina layoutInDisplayCutoutMode antes de setContentView(R.layout.activity_main) da atividade.

LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
image.png image.png image.png

3.2. Soluções de adaptação dos principais fabricantes (Huawei, Xiaomi, oppo, etc.)

Além do esquema de adaptação oficial no sistema AndroidP, os principais fabricantes também fornecem esquemas de adaptação correspondentes para seus próprios sistemas, consulte:


opo vivoXiaomi Huawei
_

Documentos de referência Toutiao
Adaptation Solution
Android9.0 Official Adaptation Solution

Estou participando do recrutamento do programa de assinatura de criadores da Comunidade de Tecnologia Nuggets, clique no link para se cadastrar e enviar .

Acho que você gosta

Origin juejin.im/post/7117630529595244558
Recomendado
Clasificación