当前位置: 首页 > news >正文

【Android】屏幕适配小合集

【Android】屏幕适配一点都不全面的合集

本文参考:

一文读懂 Android 主流屏幕适配方案_字节 屏幕适配 远离-CSDN博客

Android 屏幕适配全攻略 - 简书

什么是dp?sp?px?

为啥要文字要用sp单位?

由于Android设备的多样性——从低分辨率的老旧机型到4K屏幕的高端设备,从传统直板手机到可折叠的动态屏幕——开发者必须确保应用在不同尺寸、密度和形态的屏幕上都能呈现一致的视觉效果和交互体验。

dp?sp?px?

理解屏幕适配的基础,需从物理单位与Android的抽象单位体系入手。

物理像素(px) 是屏幕上最小的物理显示单元,每一个像素对应硬件上的一个发光点。

直接使用像素作为单位看似直观,却存在致命缺陷:假设一个按钮宽度设为100px,在低密度屏幕(如480x800像素的5英寸设备)上可能占据屏幕宽度的1/5,而在高密度屏幕(如1440x3120像素的6.5英寸设备)上可能仅占1/10,导致界面比例严重失调。

这种绝对单位的特性决定了它只适合处理与屏幕物理特性强相关的需求(例如相机预览画面布局),而在常规UI开发中应尽量避免使用。

为此,Android引入了密度无关像素(dp)和缩放独立像素(sp)。dp基于屏幕密度动态转换为实际像素,1dp在160dpi(基准密度)的设备上等于1px,而在更高密度的设备中会自动放大;sp则专用于文字,可跟随系统字体大小设置调整。

密度无关像素(dp) 是Android为适配不同密度屏幕设计的抽象单位。其核心逻辑是:1dp在160dpi(基准密度)的设备上等于1px,在其他密度设备上会根据比例自动缩放。例如,在320dpi(xhdpi)设备中,1dp=2px。这种机制使得使用dp定义尺寸的元素,能在不同设备上保持相近的物理尺寸(例如10dp的线条在多数手机上都接近1.5毫米宽)。

缩放独立像素(sp) 专为字体设计,继承dp的密度适配特性,同时叠加用户字体偏好设置的影响。当用户在系统设置中将字体大小调整为“大”或“超大”时,所有使用sp单位的文字会按比例缩放。这种设计平衡了可读性与用户自主权,但也带来潜在风险:若过度依赖sp,可能导致极端设置下文本溢出布局。因此,对于必须严格控制的文本(如导航栏标题),可采用dp单位或动态计算缩放比例,而对正文等需适配用户偏好的场景则坚持使用sp。

ppi?dpi?

img

像素密度(ppi与dpi) 是理解屏幕物理特性的关键指标。

**ppi(Pixels Per Inch)**是物理密度单位,通过屏幕分辨率和物理尺寸计算得出。例如,6英寸1080x1920像素的手机,其ppi为√(1080² + 1920²)/6 ≈ 440ppi,数值越高代表像素点越密集,显示越细腻。

**dpi(Dots Per Inch)**在Android中被重新定义为一种逻辑密度等级,用于将屏幕归类到预设的密度分组(如mdpi、xhdpi)。开发者通过DisplayMetrics.densityDpi获取的dpi值可能与实际ppi不同——系统允许厂商在一定范围内自定义dpi值以优化显示效果。

代码示例:

val displayMetrics = applicationContext.resources.displayMetrics
Log.e("TAG", "densityDpi: " + displayMetrics.densityDpi)
Log.e("TAG", "density: " + displayMetrics.density)
Log.e("TAG", "widthPixels: " + displayMetrics.widthPixels)
Log.e("TAG", "heightPixels: " + displayMetrics.heightPixels)

屏幕适配方案

本部分照搬一种极低成本的Android屏幕适配方式

该文档为2018年5月25日发布,时至今日可能已经不具备时效性,在此处作为屏幕适配方案的参考。

dp适配的缺陷

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

  • px = density * dp;
  • density = dpi / 160;
  • px = dp * (dpi / 160);

dp到px的转换逻辑看似完美,理论上可以让10dp的按钮在不同设备上保持相近的物理尺寸。但现实远比公式复杂,这种理想化的适配模型在实际开发中遭遇多重挑战,其根源在于硬件参数的非标准化与厂商定制化带来的混乱。

以一台6英寸1080×1920像素的手机为例,按物理公式计算其dpi应为√(1080² + 1920²)/6 ≈ 440。

若严格遵循系统定义,此时1dp=440/160=2.75px,屏幕宽度dp值为1080/2.75≈392.7dp。然而,多数设备的DisplayMetrics.densityDpi值并不精确等于物理计算的ppi,而是被归入预设的密度等级(如xhdpi=320dpi)。

更棘手的是,厂商可能故意修改dpi值:例如将实际440ppi的屏幕报告为480dpi(xxhdpi),导致1dp=3px,此时屏幕宽度dp值变为1080/3=360dp。这种人为干预虽能让屏幕宽度“恰好”匹配设计稿的360dp,却破坏了dp单位与物理尺寸的对应关系。

兼容适配(今日头条方案)

通过动态重置density值,强行统一所有设备的逻辑dp尺寸。

今日头条提出的方案:在应用启动时,基于设备的真实物理宽度(像素值)和设计基准宽度(如360dp)重新计算density值。

例如1080px宽的设备,若设计基准为360dp,则设置density=1080/360=3,此后所有dp单位都基于此新基准转换。这种“视口缩放”模式类似Web开发中的viewport meta标签,虽能实现像素级精确适配,但可能引发系统控件(如Dialog)尺寸异常,需额外处理第三方库的兼容性。

通过修改系统DisplayMetrics的密度参数,强制统一所有设备的逻辑像素基准。假设设计稿宽度为360dp,则动态计算:

val targetDensity = screenWidthPx / 360f

此时任意设备的屏幕宽度在代码中都会被识别为360dp,实现设计图与代码的1:1映射。此方案本质是将物理像素宽度与设计稿dp宽度进行动态绑定,类似于Web开发中的视口(viewport)缩放机制。

代码示例:

object DensityAdaptEngine {// 设计基准尺寸(单位:dp)private const val DESIGN_WIDTH_DP = 360fprivate const val DESIGN_HEIGHT_DP = 640f// 原始密度值缓存private var originalDensity: Float = 0fprivate var originalScaledDensity: Float = 0ffun adapt(activity: Activity, isWidthBased: Boolean = true) {val appMetrics = activity.application.resources.displayMetricsval activityMetrics = activity.resources.displayMetrics// 计算目标密度val screenWidthPx = appMetrics.widthPixelsval screenHeightPx = appMetrics.heightPixelsval targetDensity = if (isWidthBased) {screenWidthPx / DESIGN_WIDTH_DP} else {screenHeightPx / DESIGN_HEIGHT_DP}// 保留原始比例关系val originalScaleRatio = appMetrics.scaledDensity / appMetrics.densityoriginalDensity = appMetrics.densityoriginalScaledDensity = appMetrics.scaledDensity// 更新全局密度appMetrics.density = targetDensityappMetrics.densityDpi = (targetDensity * 160).toInt()appMetrics.scaledDensity = targetDensity * originalScaleRatio// 更新Activity级密度activityMetrics.density = targetDensityactivityMetrics.densityDpi = (targetDensity * 160).toInt()activityMetrics.scaledDensity = targetDensity * originalScaleRatio// 注册字体变化监听activity.application.registerComponentCallbacks(object : ComponentCallbacks {override fun onConfigurationChanged(config: Configuration) {if (config.fontScale != activity.resources.configuration.fontScale) {appMetrics.scaledDensity = targetDensity * (config.fontScale * originalScaleRatio)activityMetrics.scaledDensity = targetDensity * (config.fontScale * originalScaleRatio)}}override fun onLowMemory() {}})}fun reset(activity: Activity) {val metrics = activity.resources.displayMetricsmetrics.density = originalDensitymetrics.densityDpi = (originalDensity * 160).toInt()metrics.scaledDensity = originalScaledDensity}
}

双维度适配支持

  • 宽度基准模式 (isWidthBased=true):保证所有设备的宽度逻辑值等于设计稿宽度(360dp)
  • 高度基准模式 (isWidthBased=false):保证所有设备的高度逻辑值等于设计稿高度(640dp)

字体缩放补偿

  • 通过originalScaleRatio保留原始scaledDensity/density比例
  • 动态监听系统字体缩放系数(fontScale),实时更新scaledDensity

作用域控制

  • 同时修改Application级Activity级的DisplayMetrics
  • 避免部分第三方库通过不同Context获取Metrics导致的适配失效

宽高限定符

宽高限定符的核心思路是为特定分辨率设备生成专属尺寸资源

假设设计稿基于1920×1080像素,开发者需创建默认的values/dimens.xml,其中包含从x1x1080的宽度像素值和y1y1920的高度像素值。例如,针对1440×720像素的设备,需生成values-1440x720/dimens.xml,将宽度均分为1080份(每份720/1080=0.666px),高度均分为1920份(每份1440/1920=0.75px)。布

局中直接引用设计稿的像素值(如@dimen/x100),系统根据设备分辨率自动匹配资源。

SmallestWidth

SmallestWidth(最小宽度)方案通过设备逻辑宽度(单位:dp)的渐进适配,显著提升了容错率。其核心在于以设计稿宽度为基准(假设为1080px),转换为逻辑宽度(如360dp),随后为不同逻辑宽度的设备生成阶梯化的尺寸资源。

实现关键步骤

基准转换:若设计稿为1080px宽,在xhdpi设备(320dpi)中,逻辑宽度为1080px / (320dpi/160) = 540dp

资源生成:以10dp为步长创建资源文件(如values-sw360dpvalues-sw540dp),每个文件包含按比例缩放的dp值。例如,在values-sw540dp中,x100对应的值为(100 × 540dp) / 1080 = 50dp

http://www.xdnf.cn/news/588547.html

相关文章:

  • 基于python,html,echart,php,mysql,在线实时监控入侵检测系统
  • 六一儿童节礼物清单|雷克赛恩 CyberPro1 打造亲子光影盛宴
  • OptiStruct结构分析与工程应用:结构激励
  • 线程安全问题
  • 100个Linux运维知识
  • Cursor远程连接+工具使用
  • VSCode查询
  • 人工智能如何改变医疗健康行业?
  • 门店自检数字化解决方案:智能任务派发+AI复核实践
  • Strace:深入系统调用的强大调试工具
  • 手机打电话时由对方DTMF响应切换多级IVR语音菜单(完结)
  • PyTorch中diag_embed和transpose函数使用详解
  • 小白的进阶之路系列之三----人工智能从初步到精通pytorch计算机视觉详解上
  • vue2使用pdfmake
  • Qt无边框界面添加鼠标事件
  • 吃透 Golang 基础:数据结构之切片
  • 实现了TCP的单向通信
  • 【数据库】-2 mysql基础语句(上)
  • 旋转编码器计次 红外对射传感器计次小实验及其相关库函数详解 (江协科技)
  • 第四章:YOLOv11 实战应用与开发指南
  • LeetCode 404.左叶子之和的迭代求解:栈结构与父节点定位的深度解析
  • 力扣.H指数力扣.字母异位词力扣.289生命游戏力扣452.用最小数量的箭引爆气球力扣.86分隔链表力扣.轮转数组
  • 高等数学-常微分方程
  • 国产三维CAD皇冠CAD(CrownCAD)建模教程:交流发电机
  • 推荐一个Excel与实体映射导入导出的C#开源库
  • 手写简单的tomcat
  • (泛函分析)线性算子连续必有界的证明
  • GraphRAG使用
  • 动态规划(七)——子数组系列(求和问题)
  • labview实现将百分制分数转换为等级制分数