14、屏幕适配基础:dp、sp与px的换算、不同分辨率下的布局策略

做Android TV开发,屏幕适配是个绕不开的坎。

我记得刚接手第一个TV项目时,拿着设计师给的1920x1080的标注图,直接写死了像素值。结果换到3840x2160的4K电视上,界面缩成了邮票大小。嗯,那次教训挺深刻的。

说白了,屏幕适配就是要让你的UI在不同尺寸、不同分辨率的屏幕上,看起来都差不多。今天我们就来聊聊最基础的东西——dp、sp、px的换算,以及不同分辨率下的布局策略。

核心观点:TV端屏幕适配的核心,是理解密度无关像素(dp)和缩放无关像素(sp),并合理运用布局策略。不要直接使用px。

14.1 三个关键单位:dp、sp、px

先搞清楚这三个东西是什么。

  • px(像素):屏幕上的物理点。一个1920x1080的屏幕,就是有1920列、1080行像素点。
  • dp(密度无关像素):Android推荐的尺寸单位。1dp在不同密度的屏幕上,对应的物理尺寸大致相同。
  • sp(缩放无关像素):专门用于字体大小。它和dp类似,但还会跟随用户的系统字体缩放设置。

为什么不用px?因为不同屏幕的像素密度不一样。同样是100px,在320dpi的屏幕上看起来比160dpi的屏幕上小一半。你想想看,这还怎么适配?

14.2 dp与px的换算公式

换算公式其实很简单:

px = dp × (dpi / 160)

其中dpi是屏幕的像素密度。Android把屏幕密度分成了几个档次:

密度等级 dpi值 比例因子
mdpi 160 1x
hdpi 240 1.5x
xhdpi 320 2x
xxhdpi 480 3x
xxxhdpi 640 4x

举个例子:如果你在布局里写了一个48dp的按钮,在mdpi屏幕上就是48px,在xhdpi屏幕上就是96px。这样按钮的物理尺寸就差不多。

我的习惯:设计稿如果是1920x1080,我一般按xhdpi(2x)来处理。也就是设计稿上的1px,对应0.5dp。这样换算起来比较方便。

14.3 sp与px的换算

sp和dp的换算方式一样,只是多了一个字体缩放因子:

px = sp × (dpi / 160) × fontScale

fontScale是用户设置的字体缩放比例,默认是1.0。在TV上,我建议不要依赖用户的字体设置,因为电视的字体缩放通常和手机不同。我个人习惯在TV应用里统一使用dp来设置字体大小,避免sp带来的不确定性。

注意:在TV开发中,如果用户调整了系统字体大小,使用sp可能会导致文字显示不全或布局错乱。建议在TV应用中固定使用dp作为字体单位。

14.4 不同分辨率下的布局策略

搞懂了单位换算,接下来就是布局策略了。TV端常见的分辨率有:

  • 1920x1080(全高清)
  • 3840x2160(4K超高清)
  • 1280x720(高清)—— 较少见,但部分老电视还在用

针对不同分辨率,我总结了三种策略:

策略一:使用wrap_content和match_parent

这是最基础的做法。让视图根据内容自适应,或者填满父容器。比如:

<Button
    android:layout_width="wrap_content"
    android:layout_height="48dp"
    android:text="确认" />

这样按钮的宽度由文字决定,高度固定为48dp。在不同分辨率下,按钮的物理高度基本一致。

策略二:使用权重(weight)分配空间

LinearLayout的权重属性,可以按比例分配剩余空间。这在TV上特别有用,因为不同屏幕的宽高比可能不同。

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    
    <Button
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_weight="1"
        android:text="取消" />
    
    <Button
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_weight="2"
        android:text="确认" />
</LinearLayout>

这样两个按钮的宽度比例始终是1:2,不管屏幕多宽。

策略三:使用限定符(Qualifiers)

Android支持根据屏幕尺寸、密度等条件加载不同的资源。比如:

  • layout-sw600dp:屏幕宽度大于等于600dp时加载
  • layout-sw720dp:屏幕宽度大于等于720dp时加载
  • values-hdpi:hdpi密度下加载

我在项目中遇到过这样的情况:同一个界面在1080p和4K电视上,需要显示不同数量的卡片。用限定符就很好解决——在layout-sw600dp里放3列布局,在layout-sw720dp里放4列布局。

避坑指南:我曾经在4K电视上测试时,发现所有图片都模糊了。后来发现是图片资源只放在了drawable-xhdpi里,4K屏幕是xxhdpi甚至xxxhdpi,系统找不到对应资源就自动缩放了。解决办法是把图片放到drawable-nodpi目录下,或者提供多套资源。

14.5 知识体系结构图

下面这张图总结了本章的核心知识点:

屏幕适配基础知识体系 三个核心单位 换算公式 布局策略 px - 物理像素点 dp - 密度无关像素 sp - 缩放无关像素(字体) TV建议:字体用dp代替sp px = dp × (dpi / 160) 密度等级:mdpi/hdpi/xhdpi... 比例因子:1x / 1.5x / 2x ... 设计稿1920x1080 → 按2x处理 三种布局策略 ① wrap/match_parent ② 权重(weight)分配 ③ 限定符(Qualifiers) ⚠️ 常见陷阱 • 直接使用px → 不同密度下尺寸不一致 • 图片资源只放一套 → 高密度屏幕模糊

14.6 实战建议

最后,分享几个我在TV项目中的实战经验:

  1. 统一使用dp:所有尺寸、间距、字体大小都用dp,不要混用px和sp。
  2. 设计稿按1920x1080出:这是TV端最通用的分辨率,按2x密度处理即可。
  3. 测试要覆盖4K:现在4K电视越来越普及,一定要在4K屏幕上跑一遍。
  4. 图片资源放nodpi目录:避免系统自动缩放导致模糊或内存浪费。
  5. 多用ConstraintLayout:它的相对定位和比例约束,在TV适配中非常好用。

一个小技巧:在开发阶段,可以在Activity的onCreate里打印出屏幕的density和widthPixels/heightPixels,方便调试时确认当前设备的参数。

屏幕适配说难不难,说简单也不简单。核心就是理解dp/sp的本质,然后选择合适的布局策略。多测试、多踩坑,慢慢就有感觉了。


公众号:蓝海资料掘金营,微信deep3321