如何让一套代码完美适配各种屏幕?

news/2024/5/3 3:55:45/文章来源:https://blog.csdn.net/dageliuqing/article/details/127093871

一、适配的目的

区别于iOS,android设备有不同的分辨率大小以及不同厂商的系统,目前市场的分辨率可以看下相关统计。

可以看到主流的分辨率有10多种,当不做适配时,一套代码在不同设备上的效果偏大、偏小、截断以及留白严重,那一套代码如何完美的展示在不同的设备上,可以看下面的一些适配方案。

二、UI适配

2.1、常见的适配方式

2.1.1、xml布局控件适配

  1. 避免写死View的宽高,尽量使用warp_content和match_parent;
  2. 父布局为LinearLayout,选择使用android:layout_weight属性,为布局中的每个子View设置权重;
  3. 父布局为RelativeLayout,可以选择使用layout_centerInParent等属性,设置子View的相对位置;
  4. 谷歌官方在之前版本中提供了一个百分比布局方式:support:percent,它支持RelativeLayout和FrameLayout的百分比布局,但是目前官方已经不再维护,而将他取而代之的是新晋布局:ConstraintLayout,ConstraintLayout强大之处不仅在于它能够进行百分比布局,还可以进行相对定位、角度定位、尺寸约束、宽高比、Chainl链布局、住院病历等,在不同设备间都能处理的游刃有余。

2.1.2、图片适配

  1. .9图
    .9.png图片本质上还是png图片,相对于普通png图来说,.9图可以让图片在指定的位置拉伸和在指定的位置显示内容且不会失真;
  2. 见2.1.4分辨率限定符;

2.1.3、依据产品设计适配

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

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

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

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

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),当当前设备屏幕宽度大于这个值就加载一个布局,

例如在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.displayMetricsval 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和scaledDensityappDensity = displayMetrics.densityappScaleDensity = displayMetrics.scaledDensity
}/*2.计算目标值density, scaleDensity, densityDpitargetDensity为当前设备的宽度/设计稿固定的宽度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
复制代码

三、刘海屏适配

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

针对刘海屏适配,在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 :在竖屏模式下,内容会呈现到刘海区域中;但在横屏模式下,内容会显示黑边。
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:在竖屏模式和横屏模式下,内容都会呈现到刘海区域中。
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:内容从不呈现到刘海区域中。
/*** @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 = modewindow.attributes = this}}
复制代码

判断是否当前设备是否有刘海:

/*** 判断当前设备是否有刘海*/
@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
}
复制代码

在activity的 setContentView(R.layout.activity_main)之前设置layoutInDisplayCutoutMode。

LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULTLAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVERLAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_15730.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

EntityFrameworkCore+CodeFirst:根据实体自动生成数据库(二) 程序包管理控制台迁移

二、程序包管理控制台迁移 需要在DAL层引用包:Microsoft.EntityFrameworkCore.Tools然后在控制台程序中也需要引用包:Microsoft.EntityFrameworkCore.Design这样,就完成了需要的组件的引用了。 接下来打开程序包管理控制台,在vs中的“工具”中 将默认项目切换为CodeFirst.D…

SpringBoot统一异常处理详解

文章目录一、概述1、统一异常处理介绍2、原理和目标二、Assert(断言)1、概述2、Assert自定义实战2.1 自定义接口Assert2.2 自定义异常2.3 Enum整合2.4 实战检测三、统一异常处理器1、异常处理器说明1.1 handleServletException1.2 handleBindException和handleValidException1.…

35分钟了解sql注入-盲注(三)

🏆今日学习目标: 🍀学习sql注入之盲注操作 ✅创作者:贤鱼 ⏰预计时间:25分钟 🎉个人主页:贤鱼的个人主页 🔥专栏系列:网络安全 盲注布尔盲注原理布尔盲注payload构造步骤…

切面的优先级、基于XML的AOP实现

切面的优先级 相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序 1.优先级高的切面:外面 2.优先级低的切面:里面 使用Order注解可以控制切面的优先级 1.Order(较小的数):优先级高 2.Order(较大的数)&#x…

MySQL(五)增删改查进阶

目录 一、数据排序 1、基础排序 2、过滤筛选 ​3、多字段排序 4、区间判断 5、嵌套/多条件 6、 查询不重复记录 二、分组 1、对结果进行分组 三、限制 1、限制输出的结果记录 四、设置别名 1、对于列的别名 2、对于表的别名 四、视图 1、视图和表区别 2、联系…

遇到一个bug,组件不更新内容

解决办法 当v-if的值发生变化时,组件都会被重新渲染一遍。因此,利用v-if指令的特性,可以达到强制刷新组件的目的。 <template><comp v-if="update"></comp><button @click="reload()">刷新test1组件</button> </templ…

Linux权限总结

放弃不难&#xff0c;但坚持很酷&#xff0c;加油&#xff01;希望此文对您有所帮助&#xff01; 目录 shell运行原理---外壳程序Linux权限的概念如何修改文件权限&#xff1f;常见权限问题&#xff08;面试题&#xff09;1.目录权限2.umask3.写权限的作用4.如何创建一个共享目…

机器学习在分子模拟中的应用

文章目录一、背景二、分子动力学模拟介绍简史基本步骤应用发展方向三、AlphaFold**前言****Alphafold2做了什么改进****Alphafold2会议具体细节&#xff1a;****Alphafold为什么强&#xff1f;****学术问题&#xff1a;**四、TorchMD五、Deep Potential**阶段一&#xff1a;对已…

TCP/IP网络编程:P3->地址族与数据序列

本系列文章为《TCP/IP网络编程----尹圣雨》学习笔记&#xff0c;前面的系列文章链接如下 TCP/IP网络编程&#xff1a;P1-&#xff1e;理解网络编程和套接字 TCP/IP网络编程&#xff1a;P2-&#xff1e;套接字类型与协议设置 文章目录前言一、分配给套接字的IP地址与端口号1.1 网…

CSP2022 J/S 游寄

9.18 A.m. 自己学校考,但只能睡到7点不到,就很无语。 来了好多同学,关系也不错,聊了一会天就去考试了。 至于考试没什么好说的,J也就那样。 P.m. 上午对了一下答案,貌似 \(92\) ? 中午机房太吵了,没怎么睡就去考试了。 考什么大家应该都有数,宇宙射线?秒表?做尼玛呢…

卷积神经网络的应用实例,卷积神经网络可解释性

神经网络激活函数与损失函数的作用 谷歌人工智能写作项目&#xff1a;神经网络伪原创 深度学习之损失函数与激活函数的选择 深度学习之损失函数与激活函数的选择在深度神经网络&#xff08;DNN&#xff09;反向传播算法(BP)中&#xff0c;我们对DNN的前向反向传播算法的使用做…

单片机原理与应用以及C51编程技术——硬件体系结构梳理

文章目录一、单片机的结构原理1.1 主要性能和特点1.2 内部框图1.3 CPU1.3.1 运算器1.3.2 控制器1.4 几个主要的特殊功能寄存器SFR说明1.4.1 程序指针PC1.4.2 累加器A1.4.3 寄存器B1.4.4 数据指针DPTR1.4.5 程序状态字PSW介绍例子1.4.6 堆栈指针SP堆栈的介绍堆栈的作用堆栈操作的…

Verilog学习笔记

sky视频笔记&#xff1a;数字逻辑回顾&Hello World_哔哩哔哩_bilibili 一、数电基础 1.组合逻辑 电路逻辑输出值只和当前的输入有关比如&#xff1a;AND/OR/XOR/NAND/NOR/MUX/Adder/Multiplier 2.时序逻辑 电路逻辑输出值跟当前的输入和电路的当前状态有关保存当前状态的…

用Python生成Hilbert矩阵

代码放在了最后&#xff0c;前面是解题思路 目录 1.什么是Hilbert矩阵矩阵&#xff1a; 2.找规律 1.第一种思路&#xff1a;先从值出发&#xff08;找规律&#xff09; 2.第二种思路&#xff1a;先从下标索引出发&#xff08;找规律&#xff09; 三、代码展示 四、输出展…

WPF 界面打不开提示 System.ArithmeticException Overflow or underflow in the arithmetic operation 异常

本文告诉大家如何解决界面打不开,抛出 System.ArithmeticException: Overflow or underflow in the arithmetic operation 异常的修复方法本文告诉大家如何解决界面打不开,抛出 System.ArithmeticException: Overflow or underflow in the arithmetic operation 异常的修复方…

某IOT设备漏洞分析

申明&#xff1a;本文章所分享内容仅用于网络安全技术讨论&#xff0c;切勿用于违法途径&#xff0c;所有渗透都需获取授权&#xff0c;违者后果自行承担&#xff0c;与本文及作者无关&#xff0c;请谨记守法. 设备名称: DLINK DIR-818l 固件包: d-link DIR818L_FW105b01 A1 环…

Rust学习笔记:简单练习

最近一个月比较闲&#xff0c;忙碌大半年终于有自己短暂的休息时间。如果不写大的程序&#xff0c;偶尔写写一些小东西&#xff0c;其实用起来也很自在&#xff0c;前提是如果没有太多生活压力。看电视变成奢侈的事情。有时候&#xff0c;我会羡慕老外享受福利待遇非常好&#…

javaweb|JSTL的下载、配置与原理,解决uri导入时报错的问题

今天在配置jstl时&#xff0c;发现在引入uri时出现了问题&#xff0c;地址直接报红。在尝试了几种方法后&#xff0c;最后成功解决了。 JSTL一、下载1、https://tomcat.apache.org/2、找到taglib标准库3、下载前2个jar包二、配置1、放入web-inf的lib文件夹里2、将Jar包放入tomc…

windows下 解决PHP-CGI 进程崩溃502

PHP是世界上最好的语言&#xff0c;但需要PHP解析器&#xff1b;Apachephp,需要通过mod_php.so和php相连&#xff1b;nginxphp 需要转发给 cgi程序 关于FastCGI&#xff1a; 全称 FastCGI Process Manager&#xff0c;是一种进程管理器&#xff0c;管理 cgi&#xff0c;市面上…

1474_AURIX TC275 WDT的运行模式

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 1. 前面的内容中其实已经看到了&#xff0c;这个看门狗的时钟其实是固定的&#xff0c;SPB的时钟。这样&#xff0c;后面了解时钟树的时候需要注意一下。其实&#xff0c;在功能安全的失效…