一、屏幕适配核心概念
1. 关键术语解释
术语 | 定义 | 影响 |
---|---|---|
屏幕尺寸 | 屏幕对角线的物理长度(英寸) | 影响DPI计算 |
分辨率 | 屏幕像素数量(如1920x1080) | 影响像素布局 |
像素密度(DPI) | 每英寸像素数 | 影响资源选择 |
密度无关像素(dp) | 抽象像素单位(1dp≈1/160英寸) | 布局尺寸基准 |
缩放无关像素(sp) | 基于用户字体偏好的dp | 文字大小专用 |
2. Android屏幕分类
密度类型 | DPI范围 | 比例因子 | 代表设备 |
---|---|---|---|
ldpi | ~120dpi | 0.75x | 已淘汰 |
mdpi | ~160dpi | 1x | 参考基准 |
hdpi | ~240dpi | 1.5x | 早期设备 |
xhdpi | ~320dpi | 2x | 主流设备 |
xxhdpi | ~480dpi | 3x | 主流高清 |
xxxhdpi | ~640dpi | 4x | 高端设备 |
二、核心适配方案
1. 布局适配策略
(1) 约束布局(ConstraintLayout)
<androidx.constraintlayout.widget.ConstraintLayout>
<Button
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintBottom_toBottomOf="parent"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5"/>
</androidx.constraintlayout.widget.ConstraintLayout>
优势:减少嵌套,百分比定位,自适应各种屏幕
(2) 百分比尺寸
<androidx.percentlayout.widget.PercentFrameLayout>
<Button
app:layout_widthPercent="50%"
app:layout_heightPercent="30%"/>
</androidx.percentlayout.widget.PercentFrameLayout>
2. 资源限定符适配
(1) 尺寸资源
res/
├── values/
│ └── dimens.xml # 基准尺寸
├── values-sw600dp/
│ └── dimens.xml # 7寸平板
└── values-sw720dp/
└── dimens.xml # 10寸平板
(2) 布局资源
res/
├── layout/
│ └── main.xml # 手机布局
└── layout-sw600dp/
└── main.xml # 平板布局
(3) 最小宽度限定符(sw[N]dp)
-
sw600dp:7寸平板
-
sw720dp:10寸平板
3. 图片适配方案
(1) 矢量图(VectorDrawable)
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#FF0000"
android:pathData="M12,2L4,5v6c0,5.5 3.8,10.7 9,12c5.2-1.3 9-6.5 9-12V5L12,2z"/>
</vector>
优势:无限缩放不失真,单一资源适配所有密度
(2) 多密度位图
res/
├── drawable-mdpi/
│ └── icon.png # 48x48px (1x)
├── drawable-hdpi/
│ └── icon.png # 72x72px (1.5x)
└── drawable-xxhdpi/
└── icon.png # 144x144px (3x)
4. 代码适配方案
(1) 屏幕信息获取
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int widthPixels = metrics.widthPixels; // 屏幕宽度(px)
int heightPixels = metrics.heightPixels; // 屏幕高度(px)
float density = metrics.density; // 密度因子
int densityDpi = metrics.densityDpi; // 屏幕DPI
(2) 动态调整布局
// 根据屏幕宽度调整列数
int screenWidth = getResources().getConfiguration().screenWidthDp;
int columnCount = screenWidth / 300; // 每300dp显示一列
recyclerView.setLayoutManager(new GridLayoutManager(this, columnCount));
三、高级适配技术
1. 今日头条适配方案
原理:修改系统density值实现等比缩放
实现步骤:
public class ScreenAdapter {
public static void adaptScreen(Activity activity, int designWidth) {
DisplayMetrics appMetrics = activity.getApplication().getResources().getDisplayMetrics();
DisplayMetrics activityMetrics = activity.getResources().getDisplayMetrics();
float targetDensity = activityMetrics.widthPixels * 1f / designWidth;
int targetDensityDpi = (int) (160 * targetDensity);
appMetrics.density = appMetrics.scaledDensity = targetDensity;
appMetrics.densityDpi = targetDensityDpi;
activityMetrics.density = activityMetrics.scaledDensity = targetDensity;
activityMetrics.densityDpi = targetDensityDpi;
}
}
2. 刘海屏适配
(1) 全屏模式适配
// 在onCreate中调用
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowInsets windowInsets = getWindow().getDecorView().getRootWindowInsets();
DisplayCutout cutout = windowInsets.getDisplayCutout();
if (cutout != null) {
List<Rect> safeAreas = cutout.getBoundingRects();
// 处理安全区域
}
}
(2) 布局避开刘海
<androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/header"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="@dimen/status_bar_height"/>
</androidx.constraintlayout.widget.ConstraintLayout>
3. 折叠屏适配
(1) 屏幕变化监听
// 在Activity中注册
WindowManager windowManager = getWindowManager();
windowManager.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
// 处理折叠状态变化
}
});
(2) 分屏布局优化
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.constraintlayout.widget.ConstraintLayout>
<fragment
android:id="@+id/list_fragment"
android:name="com.example.ListFragment"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="@{screenWidth > 600 ? 0.3 : 1.0}"/>
<fragment
android:id="@+id/detail_fragment"
android:name="com.example.DetailFragment"
app:layout_constraintStart_toEndOf="@id/list_fragment"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="@{screenWidth > 600 ? 0.7 : 1.0}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
四、测试与验证
1. 多屏幕测试工具
# 创建不同尺寸模拟器
emulator -avd Pixel_XL -skin 1440x2880
emulator -avd Nexus_7 -skin 1200x1920
2. 自动化测试脚本
# 使用uiautomator测试不同分辨率
def test_ui_on_different_resolutions():
for resolution in [(1080, 1920), (1440, 2560)]:
device.change_resolution(*resolution)
assert device(text='Login').exists
3. 云测试平台
-
Firebase Test Lab
-
AWS Device Farm
-
腾讯WeTest
五、最佳实践总结
-
布局原则:
-
优先使用ConstraintLayout
-
避免绝对定位(px)
-
使用match_parent和wrap_content
-
-
尺寸单位:
-
使用dp作为基本单位
-
文字使用sp
-
需要精确控制时可用px(极少数情况)
-
-
资源管理:
-
提供多套drawable资源
-
使用矢量图替代位图
-
为不同屏幕提供备用布局
-
-
代码适配:
-
动态计算关键尺寸
-
监听屏幕配置变化
-
处理极端长宽比
-
-
测试覆盖:
-
覆盖主流屏幕尺寸
-
测试横竖屏切换
-
验证折叠屏行为
-
通过系统性地应用这些适配方案,可以确保应用在各种Android设备上都能提供优秀的用户体验。