64、屏幕适配:dp/sp概念、限定符(layout-large、sw600dp)、百分比布局

屏幕适配,说白了就是让App在不同尺寸、不同密度的手机上,都能“长”得差不多。我刚开始做Android开发那会儿,还真没把这当回事。直到有一次,测试拿了一台老款的三星平板过来,我的界面直接“飞”了——按钮跑到了屏幕外面,文字叠在了一起。嗯,从那以后,我就老老实实把适配这块啃透了。

今天咱们就聊聊适配的三个核心武器:dp/sp资源限定符、还有百分比布局。你想想看,搞懂了这些,基本上90%的适配问题都能搞定。

1. dp与sp:别再写死px了

很多新手刚入行时,喜欢直接在布局里写 android:layout_width="100px"。我个人习惯是,看到这种代码就头疼。为什么?因为px是像素点,不同手机像素密度不一样。同样100px,在320dpi的屏幕上可能只有1厘米,在640dpi的屏幕上可能只有0.5厘米。界面自然就“缩水”了。

Android引入了dp(密度无关像素)来解决这个问题。1dp在不同密度的屏幕上,实际占用的物理尺寸是基本一致的。比如你写 layout_width="160dp",在mdpi(160dpi)屏幕上就是160px,在xhdpi(320dpi)屏幕上就是320px。系统会自动帮你换算。

至于sp,它和dp类似,但多了一个“用户字体缩放”的因素。我建议:文字用sp,其他尺寸用dp。为什么?因为有些用户喜欢把系统字体调大,如果你用dp写文字大小,那字体就不会跟着系统缩放,用户体验会很差。

核心原则:

  • 宽高、间距、边距 → 用dp
  • 字体大小 → 用sp
  • 永远不要用px(除非你在写自定义View的Canvas绘制)

💡 我个人习惯在项目的 dimens.xml 里统一管理所有dp/sp值。比如 @dimen/margin_normal 定义成16dp,这样后期改起来特别方便,不用满世界找硬编码。

2. 资源限定符:让系统自动帮你选布局

光靠dp还不够。比如平板和手机,同样是160dp的按钮,在手机上占一半屏幕,在平板上可能只占一小块。这时候就需要资源限定符了。

资源限定符,说白了就是给不同屏幕尺寸或方向,准备不同的布局文件。系统会根据设备属性,自动加载对应的资源。你只需要在 res 目录下建对应的文件夹就行。

2.1 尺寸限定符(layout-large / layout-xlarge)

这是最早期的方式。比如你有一个 res/layout/activity_main.xml,再建一个 res/layout-large/activity_main.xml。当设备屏幕物理尺寸大于5英寸左右(系统判定为large),就会自动加载 layout-large 里的布局。

我记得在Android 3.2之前,这个方式很流行。但它有个问题:它只看物理尺寸,不看像素密度。比如一台5.5英寸的720p手机和一台5.5英寸的1080p手机,都被归为“large”,但实际显示效果差很多。

⚠️ 我曾经在项目里用过 layout-large,结果在三星Note系列上表现很好,但在华为Mate系列上却出现了布局错乱。后来发现是因为Note的dpi较低,而Mate的dpi较高,同样的dp值在Mate上实际像素更多,导致布局“撑开”了。所以现在更推荐用 sw<N>dp 限定符。

2.2 最小宽度限定符(sw<N>dp)

sw600dp 的意思是:当设备屏幕的最小宽度(即屏幕宽高中较小的那个值,单位dp)大于等于600dp时,加载该资源。这个方式更精确,因为它基于dp值,而不是物理尺寸。

举个例子:一台7英寸平板,分辨率是1024×600,dpi是160。那么它的宽度是600dp(因为600px / 1.0 = 600dp),高度是1024dp。最小宽度就是600dp。所以 res/layout-sw600dp/ 里的布局就会被加载。

我建议你记住几个常用断点:

限定符 典型设备 说明
sw320dp 普通手机(4.7-5.5英寸) 大多数手机的默认值
sw480dp 大屏手机(5.5-6.5英寸) 比如一些“巨屏”手机
sw600dp 7英寸平板 比如Nexus 7
sw720dp 10英寸平板 比如Nexus 10

💡 实际项目中,我一般只准备 layout(默认手机)、layout-sw600dp(平板)、layout-sw720dp(大平板)三套布局。太多反而不好维护。你想想看,每多一套布局,测试成本就翻一倍。

3. 百分比布局:让控件按比例分配

有时候,我们希望两个按钮各占屏幕宽度的一半。用dp写死肯定不行,因为不同屏幕宽度不一样。这时候就需要百分比布局了。

Android官方提供了 PercentRelativeLayoutPercentFrameLayout,但它们在AndroidX中已经被标记为废弃。我个人更推荐使用 ConstraintLayout 配合 app:layout_constraintWidth_percent 来实现百分比效果。

举个例子,让两个按钮各占一半宽度:

<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">

    <Button
        android:id="@+id/btn_left"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:text="左侧按钮"
        app:layout_constraintWidth_percent="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btn_right"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:text="右侧按钮"
        app:layout_constraintWidth_percent="0.5"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/btn_left"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

你看,app:layout_constraintWidth_percent="0.5" 就表示宽度占父容器的50%。而且 layout_width 必须设为 0dp,这样ConstraintLayout才能根据百分比计算实际宽度。

百分比布局的适用场景:

  • 导航栏按钮平分宽度
  • 卡片列表的列数自适应(比如平板显示3列,手机显示2列)
  • 弹窗宽度按屏幕比例缩放

⚠️ 注意:百分比布局不要滥用。如果一个布局里超过5个控件都用了百分比,那维护起来会非常痛苦。我建议只对“需要按屏幕比例动态变化”的控件使用百分比,其他固定尺寸的控件还是用dp。

4. 知识体系总览

下面这张图,是我自己总结的屏幕适配知识体系。你可以把它当作一个“检查清单”,每次做适配时对照一下,基本不会漏掉什么。

Android屏幕适配 dp/sp 单位体系 dp:密度无关像素(宽高/间距) sp:缩放无关像素(字体) 资源限定符(Resource Qualifiers) layout-large / layout-xlarge sw<N>dp(如sw600dp) 方向限定符(land/port) 百分比布局(ConstraintLayout)

从图上你可以看到,适配不是单一技术,而是一个组合拳。dp/sp是基础,限定符解决“不同尺寸加载不同布局”的问题,百分比布局解决“同一布局内按比例分配”的问题。三者结合,基本能覆盖所有主流设备。

5. 避坑指南

最后,分享几个我踩过的坑:

  • 不要依赖单一适配方式。我曾经只用了dp,结果在平板上界面被拉得很宽,文字间距变得很奇怪。后来加了 sw600dp 布局才解决。
  • 注意系统字体缩放。有些用户会把系统字体调到最大,这时候用sp的文字可能会换行。我建议在布局里给TextView设置 android:maxLinesandroid:ellipsize,防止布局被撑爆。
  • 测试要覆盖真机。模拟器只能模拟分辨率,但实际设备的dpi、圆角、刘海屏等差异,只有真机才能暴露问题。我一般会准备3-4台不同尺寸的真机做回归测试。

💡 如果你用的是ConstraintLayout,可以多利用 app:layout_constraintWidth_percentapp:layout_constraintHeight_percent。这两个属性比写死dp灵活得多,而且不需要额外引入库。我个人现在写新项目,基本只用ConstraintLayout,配合百分比属性,适配工作量能减少一半。

好了,关于屏幕适配的核心内容就这些。你想想看,其实适配没那么神秘,无非就是“单位选对、布局分屏、比例灵活”。下次遇到界面变形的问题,不妨从这三个角度去排查,大概率能找到原因。

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