在Android开发中,屏幕适配一直是个让人头疼的问题。不同厂商的设备屏幕尺寸千差万别,从4寸的小屏到10寸以上的平板,分辨率更是五花八门。记得我刚入行时,为了适配各种屏幕,不得不在res目录下创建几十个dimens文件,每次修改都要同步更新所有文件,工作量巨大。
Autosize的出现彻底改变了这种局面。它基于今日头条的屏幕适配方案,通过动态修改density值来实现完美适配。简单来说,就是让不同尺寸的屏幕都能按照设计稿的比例显示UI元素。比如设计稿是750x1334的尺寸,在1080x1920的屏幕上和720x1280的屏幕上,都能保持相同的视觉效果。
我最近在一个电商项目中使用Autosize,效果非常惊艳。项目需要适配从5寸到10寸的各种平板设备,传统方案至少要写5套布局,而使用Autosize后,一套布局就能完美适配所有设备,开发效率提升了至少3倍。
目前Autosize的最新版本是1.2.1,可以通过JitPack引入。首先在项目根目录的build.gradle中添加JitPack仓库:
groovy复制allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
然后在app模块的build.gradle中添加依赖:
groovy复制dependencies {
implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
}
在AndroidManifest.xml的application标签内添加meta-data配置:
xml复制<meta-data
android:name="design_width_in_dp"
android:value="360"/>
<meta-data
android:name="design_height_in_dp"
android:value="640"/>
这里的360和640对应设计稿的宽高(以dp为单位)。比如设计稿是720x1280的尺寸,那么360x640就是对应的dp值(720/2=360,1280/2=640)。
我在实际项目中发现,很多设计师提供的标注图尺寸不统一。建议在项目初期就和设计师约定好设计稿尺寸,避免后期频繁调整。
横竖屏切换是屏幕适配中最复杂的场景之一。Autosize提供了onAdaptListener来监听适配过程:
java复制AutoSizeConfig.getInstance().setOnAdaptListener(new onAdaptListener() {
@Override
public void onAdaptBefore(Object target, Activity activity) {
// 更新屏幕尺寸
int[] screenSize = ScreenUtils.getScreenSize(activity);
AutoSizeConfig.getInstance()
.setScreenWidth(screenSize[0])
.setScreenHeight(screenSize[1]);
// 根据方向设置设计尺寸
if (activity.getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
// 横屏设计尺寸
AutoSizeConfig.getInstance()
.setDesignWidthInDp(1280)
.setDesignHeightInDp(800);
} else {
// 竖屏设计尺寸
AutoSizeConfig.getInstance()
.setDesignWidthInDp(800)
.setDesignHeightInDp(1280);
}
}
@Override
public void onAdaptAfter(Object target, Activity activity) {
// 适配完成后回调
}
});
在一个视频播放器项目中,横屏时需要全屏显示视频,竖屏时显示视频列表。我遇到了一个坑:横竖屏切换时部分TextView的文字大小会跳动。经过排查发现是因为横竖屏使用了不同的设计尺寸,但某些控件在切换时没有及时重绘。
解决方法是在onAdaptAfter回调中手动调用View的requestLayout():
java复制@Override
public void onAdaptAfter(Object target, Activity activity) {
if(target instanceof View){
((View) target).requestLayout();
}
}
在迁移到AndroidX后,可能会遇到如下错误:
code复制Rejecting re-init on previously-failed class java.lang.Class<me.jessyan.autosize.FragmentLifecycleCallbacksImpl>:
java.lang.NoClassDefFoundError: Failed resolution of: Landroid/support/v4/app/FragmentManager$FragmentLifecycleCallbacks;
解决方法是在gradle.properties中添加:
code复制android.useAndroidX=true
android.enableJetifier=true
有些开发者反馈sp单位的字体在不同设备上显示不一致。这是因为Autosize默认也会适配sp单位。如果希望系统字体大小设置能生效,可以关闭sp适配:
java复制AutoSizeConfig.getInstance().setExcludeFontScale(true);
某些页面可能需要禁用Autosize,比如视频播放页需要固定尺寸。可以通过以下方式实现:
java复制AutoSize.cancelAdapt(activity);
记得在页面恢复时重新启用适配:
java复制@Override
protected void onResume() {
super.onResume();
AutoSize.autoConvertDensity(activity, 360, true);
}
大型项目可能会有多个设计师参与,设计稿尺寸不统一。我建议在BaseActivity中动态设置设计尺寸:
java复制public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// 根据业务模块设置不同设计尺寸
if(this instanceof ProductDetailActivity) {
AutoSizeConfig.getInstance()
.setDesignWidthInDp(375)
.setDesignHeightInDp(667);
} else {
AutoSizeConfig.getInstance()
.setDesignWidthInDp(360)
.setDesignHeightInDp(640);
}
super.onCreate(savedInstanceState);
}
}
Autosize和ConstraintLayout是绝配。ConstraintLayout的比例特性可以很好地配合Autosize的适配策略:
xml复制<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_cover"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Autosize虽然方便,但在低端设备上频繁计算可能会影响性能。我总结了几个优化点:
为了确保适配效果,我建议建立设备矩阵进行测试:
| 设备类型 | 分辨率 | 屏幕尺寸 | 测试重点 |
|---|---|---|---|
| 小屏手机 | 720x1280 | 5寸 | 文字可读性 |
| 主流手机 | 1080x1920 | 6寸 | 整体布局 |
| 大屏手机 | 1440x2560 | 6.5寸 | 图片清晰度 |
| 平板 | 1600x2560 | 10寸 | 横竖屏切换 |
可以编写简单的UI测试脚本验证适配效果:
java复制@Test
public void testScreenAdapt() {
onView(withId(R.id.main_container)).check(matches(
allOf(
isDisplayed(),
hasWidth(equalTo(DeviceDisplayHelper.getScreenWidth())),
hasHeight(equalTo(DeviceDisplayHelper.getScreenHeight()))
)
));
}
Autosize的核心原理是通过修改Application的displayMetrics:
java复制public static void autoConvertDensity(Activity activity, float sizeInDp, boolean isBaseOnWidth) {
DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
float targetDensity = isBaseOnWidth
? displayMetrics.widthPixels / sizeInDp
: displayMetrics.heightPixels / sizeInDp;
displayMetrics.density = targetDensity;
displayMetrics.scaledDensity = targetDensity * (displayMetrics.scaledDensity / displayMetrics.density);
displayMetrics.densityDpi = (int)(targetDensity * 160);
}
基于这个原理,我们可以扩展出自定义策略。比如针对平板设备,可以保持density不变,只调整特定View的尺寸:
java复制public class TabletAdaptStrategy implements CustomAdapt {
@Override
public boolean isBaseOnWidth() {
return false;
}
@Override
public float getSizeInDp() {
return 667;
}
@Override
public void adaptBeforeCreate(View view, Activity activity) {
// 平板特有适配逻辑
}
}
在实际项目中,我基于这个思路开发了针对折叠屏的特殊适配方案,可以根据折叠状态动态调整布局。