本文:https://blog.csdn.net/CrazyApes/article/details/117136453
原文:一种极低成本的Android屏幕适配方式 - 字节跳动技术团队
前言
屏幕适配啊,老生长谈的问题,因为Android碎片化的原因,屏幕的分辨率简直是五花八门啊,不说别的,打开studio模拟器里就可以搞一大堆不同分辨率的模拟器。
适配是个头疼的事啊,虽然Android官方提供了dp单位来适配,但是在某些时候特殊的屏幕分辨率下,UI还是变形的难看的不行。
下面说一种字节跳动技术团队提供的适配方式,据说是今日头条中使用的方式。
详细的信息大家直接去看原文吧,链接如下:
原文:一种极低成本的Android屏幕适配方式 - 字节跳动技术团队
原文最终方案
我这里呢,就把原文里面实现的代码最终方案敲了一下,方便大家参阅。
同时在 Activity#onCreate 方法中调用下就行了。
// 适配
private static float sNonCompatDensity;
private static float sNonCompatScaledDensity;
// 默认设计图为360dp
private final static float TARGET_SCREEN_WIDTH_DP = 360;
private static void setCustomDensity(@NonNull Activity activity, @NonNull final Application application) {
final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
if (sNonCompatDensity == 0) {
sNonCompatDensity = appDisplayMetrics.density;
sNonCompatScaledDensity = appDisplayMetrics.scaledDensity;
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig != null && newConfig.fontScale > 0) {
sNonCompatScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
final float targetDensity = appDisplayMetrics.widthPixels / TARGET_SCREEN_WIDTH_DP;
final float targetScaledDensity = targetDensity * (sNonCompatScaledDensity / sNonCompatDensity);
final int targetDensityDpi = (int) (160 * targetDensity);
appDisplayMetrics.density = targetDensity;
appDisplayMetrics.scaledDensity = targetScaledDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}
}
如何确保每个Activity的应用
在使用过程中,因为发现项目中存在不少未继承Base类的Activity,这就尴尬了。
觉得去找每一个Activity的onCreate又不太现实,这里想到了一个其它方式。
ActivityLifecycleCallbacks
在 Application 中通过 registerActivityLifecycleCallbacks 监听Activity的生命周期,并在onCreate中设置适配。
public class DemoApplication extends Application {
private static DemoApplication mApplication = null;
@Override
public void onCreate() {
super.onCreate();
mApplication = this;
...
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
// 修改适配信息
setCustomDensity(activity,mApplication);
}
@Override
public void onActivityStarted(Activity activity) { }
@Override
public void onActivityResumed(Activity activity) { }
@Override
public void onActivityPaused(Activity activity) { }
@Override
public void onActivityStopped(Activity activity) { }
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) { }
@Override
public void onActivityDestroyed(Activity activity) { }
});
}
private static void setCustomDensity( ... ) { ... }
}
为什么要每个Activity都进行重设
主要和 Resource 的创建有关。
我这里先简单说一下,详细可以翻阅资料,或者以后有时间在做个 Resource 相关的文章。
每个Activity的Resource资源是独立的,并且有缓存。
在Activity启动的时候,根据activityToken,overrideConfig等关键信息去更新或者重新生成Resource放入缓存种以供使用。
而我们目前适配的方式,更改的只是某个Resource对象,不是全部,即使是全部的,也存在一个Resource可能被更新的问题。所以才需要每个Activity中都进行设置。
结语
有什么不对的地方,欢迎大家指正。
参考文献
本文:https://blog.csdn.net/CrazyApes/article/details/117136453
原文:一种极低成本的Android屏幕适配方式 - 字节跳动技术团队
参考:https://developer.android.google.cn/guide/practices/screens_support
网友评论