jessyancoding / androidautosize Goto Github PK
View Code? Open in Web Editor NEW🔥 A low-cost Android screen adaptation solution (今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案).
License: Apache License 2.0
🔥 A low-cost Android screen adaptation solution (今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案).
License: Apache License 2.0
坚果Pro2手机的toast弹出,我原本是只显示一句:模块不存在,请联系管理员,但是弹出Toast的时候有时候toast的弹出框就会全屏占满,然后也不显示文字,实际的截图已发送到邮箱,希望能看一下
app进程杀死后,再进入app,页面效果不生效,只有热启动,页面效果生效
现在这样适配都对到activity,对多fragment不怎么友好..比如cancelAdapter
可能有人觉得 AndroidAutoSize 对横屏、平板电脑、TV 的适配效果不够好,这里统一解答下
AndroidAutoSize 对于横屏的适配:假如你某个页面的的设计图尺寸是 1920 * 1080,设计图是为竖屏设计的,运行设备的屏幕分辨率也是 1920 * 1080,当在竖屏的时候,一切显示完好,当设备切换为横屏时,屏幕的高或宽就已经互换,这时高为 1080 px,宽为 1920 px,横屏时如果
AndroidAutoSize 是以宽度为基准进行适配,AndroidAutoSize 就会将原本在竖屏时宽为 1080 px 的布局横向拉伸为 1920 px,布局宽度刚好平铺满屏幕,高度也会等比例放大,但布局高度却不会平铺满屏幕,所以会造成竖屏时高为 1920 px 的布局在横屏时纵向的内容显示不全,感觉整个布局被放大很多的视觉效果,如果想让布局在横屏时纵向内容显示完全,高度从 1920 px 缩小至 1080 px,就要让 AndroidAutoSize 以高度为基准进行适配,但是这样又会造成布局的宽度不能平铺满屏幕,导致布局的横向空间上有间隙,那为什么 AndroidAutoSize 只能让高度或宽度中,其中的一个平铺满屏幕,不能让高度和宽度同时平铺满屏幕呢,请看这个 回答,这就是 AndroidAutoSize 在横屏时的适配情况,如果你想布局在横屏时获得更好的适配效果,就只有让设计师针对横屏再出一套设计图 (针对横屏做单独适配这是基础吧,毕竟高和宽都完全不一样)
AndroidAutoSize 对平板电脑、TV 等大屏幕设备的适配:你根据设计师为手机出的设计图,实现为布局后,在手机上的显示是完好的,但显示在平板电脑、TV 等大屏幕设备上感觉变得很大 (如果是横屏,也会出现上面的效果),但我想告诉你这是正常的, AndroidAutoSize 能保证的只是小屏幕显示的内容和大屏幕显示的内容相对于整个屏幕的比例是相等的,因为平板电脑或 TV 的屏幕空间比手机大很多,所以将手机的设计图等比例放大到平板电脑或 TV 上,布局中的控件变大几倍甚至十几倍都是正常的,这就是百分比库的特性,如果你将为平板设计的设计图等比例缩小到手机上,你也会感觉布局在手机上的显示效果偏小,再一次强调这就是百分比库的特性,AndroidAutoSize 能做的只是在不同尺寸的屏幕上以设计图上的比例拉伸或缩放布局,至于这个布局在拉伸或缩放后的效果在这个屏幕上是否美观,不在 AndroidAutoSize 所考虑的范围内
其实不使用任何的百分比屏幕适配库(AutoSizeConfig.getInstance().stop(activity) 停止框架就可以了),直接使用原生的 dp 进行适配,就可以达到在越大的屏幕上显示更多的内容,百分比库是实现不了在越大的屏幕上显示更多的内容的,不管是小到手机还是大到TV,他只会让布局在所有屏幕上都显示相同大小的内容,因为这就是它的特性。
用户都希望在大屏幕设备上让 APP 能显示更多的内容,以更好的利用大屏幕的优势,这都是人之常情,但你想让一套手机设计图就能完美适配平板和TV,就是非常不现实的行为了。
如果你想让平板电脑、TV 等大屏幕设备能比手机屏幕显示更多的内容,并且达到更完美的适配效果,就只有让设计师针对平板电脑、TV 等大屏幕设备再出一套设计图 (这也是基础,毕竟尺寸差别这么大,如果出一套手机的设计图就能让平板电脑、TV 等大屏幕设备达到最好的适配效果,那那些音视频的 App 都不用花费这么多精力专门适配平板了),如果你觉得现在的适配效果还能接受或者想偷懒,那也可以继续使用手机的设计图来适配平板电脑、TV 等大屏幕设备。
09-04 15:04:36.299 7450-7450/xx.xx D/AndroidAutoSize: designWidthInDp = 0, designHeightInDp = 0, screenWidth = 1440, screenHeight = 2960
09-04 15:04:36.300 7450-7450/xx.xx D/AndroidAutoSize: initDensity = 3.5, initScaledDensity = 2.8
这是全部日志,可以看到没有执行DefaultAutoAdaptStrategy中的方法打印出相应的log
这是AutoSizeConfig中获取到的appcation的相关信息
整合了tinker(DefaultLifeCycle注解的方式)之后就不起作用了,DefaultAutoAdaptStrategy中的applyAdapt都不会执行。适配失败
public class CustomAdaptActivity extends AppCompatActivity implements CustomAdapt {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_adapt);
}
/**
* 是否按照宽度进行等比例适配 (为了保证在高宽比不同的屏幕上也能正常适配, 所以只能在宽度和高度之中选一个作为基准进行适配)
*
* @return {@code true} 为按照宽度适配, {@code false} 为按照高度适配
*/
@Override
public boolean isBaseOnWidth() {
return true;
}
/**
* 这里使用 IPhone 的设计图, IPhone 的设计图尺寸为 750px * 1334px, 高换算成 dp 为 667 (1334px / 2 = 667dp)
* <p>
* 返回设计图上的设计尺寸, 单位 dp
* {@link #getSizeInDp} 须配合 {@link #isBaseOnWidth()} 使用, 规则如下:
* 如果 {@link #isBaseOnWidth()} 返回 {@code true}, {@link #getSizeInDp} 则应该返回设计图的总宽度
* 如果 {@link #isBaseOnWidth()} 返回 {@code false}, {@link #getSizeInDp} 则应该返回设计图的总高度
* 如果您不需要自定义设计图上的设计尺寸, 想继续使用在 AndroidManifest 中填写的设计图尺寸, {@link #getSizeInDp} 则返回 {@code 0}
*
* @return 设计图上的设计尺寸, 单位 dp
*/
@Override
public float getSizeInDp() {
return 375;
}
}
为什么这种写法按照高度适配的时候是正常的,按照宽度适配的时候就出现问题了呢
我最近遇到的问题,
以下的布局,没有webview时候,适配是ok的,但是如果有webview的化,这种是适配就失效了
不知楼主有没有好的方案
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<WebView
android:id="@+id/webView"
android:layout_width="180dp"
android:layout_height="180dp"
android:background="#FF0000" />
<View
android:layout_width="180dp"
android:layout_height="180dp"
android:background="#00FF00" />
</LinearLayout>
java.lang.NoClassDefFoundError: me.jessyan.autosize.FragmentLifecycleCallbacksImpl
at me.jessyan.autosize.ActivityLifecycleCallbacksImpl.<init>(ActivityLifecycleCallbacksImpl.java:45)
at me.jessyan.autosize.AutoSizeConfig.init(AutoSizeConfig.java:206)
at me.jessyan.autosize.AutoSizeConfig.init(AutoSizeConfig.java:146)
at me.jessyan.autosize.InitProvider.onCreate(InitProvider.java:37)
at android.content.ContentProvider.attachInfo(ContentProvider.java:1801)
at android.content.ContentProvider.attachInfo(ContentProvider.java:1776)
at android.app.ActivityThread.installProvider(ActivityThread.java:5427)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:5000)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4940)
at android.app.ActivityThread.-wrap1(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1549)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:5728)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
您好,我自己使用头条的适配方案的时候发现系统的弹窗适配不对,都超出屏幕了。碰巧看到您的实现,发现也有这问题。请问什么原因?怎么解决?
项目默认是横屏显示的,本来适配是正常的,但是在锁屏解锁后或者切到桌面再回到app会出现popupwindow和dialog适配失效。
D/AndroidAutoSize: designWidthInDp = 584, designHeightInDp = 830, screenWidth = 1920, screenHeight = 1080
D/AndroidAutoSize: initDensity = 3.0, initScaledDensity = 3.0
D/AndroidAutoSize: initScaledDensity = 3.0 on ConfigurationChanged
D/AndroidAutoSize: cn.pluss.yunlan.paddiancan.activity.LoginActivity implemented by me.jessyan.autosize.internal.CustomAdapt!
D/AndroidAutoSize: The cn.pluss.yunlan.paddiancan.activity.LoginActivity has been adapted!
LoginActivity Info: isBaseOnWidth = false, designHeightInDp = 830.000000, targetDensity = 1.214458, targetScaledDensity = 1.214458, targetDensityDpi = 194, targetXdpi = 1.214458
D/AndroidAutoSize: cn.pluss.yunlan.paddiancan.activity.MainActivity implemented by me.jessyan.autosize.internal.CustomAdapt!
D/AndroidAutoSize: The cn.pluss.yunlan.paddiancan.activity.MainActivity has been adapted!
MainActivity Info: isBaseOnWidth = false, designHeightInDp = 830.000000, targetDensity = 1.214458, targetScaledDensity = 1.214458, targetDensityDpi = 194, targetXdpi = 1.214458
D/AndroidAutoSize: initScaledDensity = 3.0 on ConfigurationChanged
D/AndroidAutoSize: initScaledDensity = 3.0 on ConfigurationChanged
DialogFragment也会出现这个问题,图就没截了。
Bug Description:
当高度使用?attr/actionBarSize时,会导致这个尺寸也改变。
平板适配起来很糟糕
切换多语言,会导致适配失效,字体变小了,布局也变了
isBaseOnWidth = true, designWidthInDp = 360.000000, targetDensity = 3.000000, targetScaledDensity = 3.000000, targetDensityDpi = 480
public static void changeLanguageSetting(Locale locale, Activity activity) {
changeAppLanguage(activity, locale, true);
//开始新的activity同时移除之前所有的activity
PreferenceStore.ofBoolean(CHANGE_LANGUAGE, false).set(true);
Intent intent = new Intent(activity, MainPageActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
activity.startActivity(intent);
}
/**
* 更改应用语言
*
* @param context
* @param locale 语言地区
* @param persistence 是否持久化
*/
public static Context changeAppLanguage(Context context, Locale locale,
boolean persistence) {
if (locale == null) {
return context;
}
curLocale = locale;
Log.i(TAG, "changeAppLanguage: local : " + locale.getLanguage());
if (persistence) {
saveLanguageSetting(context, locale);
}
CommonCountryManager.ins().changeCommonCountryLanguage(context, locale);
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
Configuration configuration = resources.getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(locale);
} else {
configuration.locale = locale;
}
context = context.createConfigurationContext(configuration);
resources.updateConfiguration(configuration, metrics);
return context;
}
WindowManager windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
这样获取到的metrics中的density,densityDpi,scaledDensity,是否适配后的?
如果不是,是否可以考虑保存了原始density,densityDpi,scaledDensity后,hook直接修改
小弟入行时间不长,勿喷!只是提出自己的一些想法!
HuaWei
08-22 16:31:45.598 D/AndroidAutoSize: designWidthInDp = 375, designHeightInDp = 667, screenWidth = 720, screenHeight = 1440
08-22 16:31:45.599 D/AndroidAutoSize: initDensity = 2.0, initScaledDensity = 2.0
Samsung
08-22 16:30:50.367 D/AndroidAutoSize: designWidthInDp = 375, designHeightInDp = 667, screenWidth = 1440, screenHeight = 2560
08-22 16:30:50.368 D/AndroidAutoSize: initDensity = 4.0, initScaledDensity = 4.0
<meta-data
android:name="design_width_in_dp"
android:value="375" />
<meta-data
android:name="design_height_in_dp"
android:value="667" />
只填写了清单文件的meta value ,最终出来的效果好像还是有点差别, 截图右边是华为分辨率小的,布局底部空出了一大块
在模拟器上运行,有底部导航栏的话,适配的时候会有一条白底,用命令隐藏底部导航栏 adb shell settings put global policy_control immersive.navigation=* 就没有白底了。有人在真机测试中遇到类似的情况吗?
SettingActivity Info: isBaseOnWidth = true, designWidthInDp = 360.000000, targetDensity = 4.000000, targetScaledDensity = 4.000000, targetDensityDpi = 640
我自定义了一个titlebar,并且设置为全屏模式,正常情况下我会获取状态栏高度(除刘海屏外一般为25dp左右),然后设置titlebar的topPadding为状态栏高度以实现透明状态栏.
问题出在使用该框架后25dp会被放大,但是手机的状态栏还是25dp,导致titlebar上面padding的空间会变大,该如何解决?
看到了只需这一步
下面的CustomAdapt CancelAdapt 这个实现是什么意思
建议多添加点文字注释
还有用法,博客里面原理看的差不多,最后还是不知道怎么用
如题。
适配失败,看日志像是1比1输出,但这个跑出来的效果差很远
08-27 08:10:28.119 4224-4224/com.zhixiang.balancewallet D/AndroidAutoSize: designWidthInDp = 750, designHeightInDp = 1334, screenWidth = 750, screenHeight = 1334
08-27 08:10:28.120 4224-4224/com.zhixiang.balancewallet D/AndroidAutoSize: initDensity = 2.0, initScaledDensity = 2.0
08-27 08:10:28.214 4224-4224/com.zhixiang.balancewallet D/AndroidAutoSize: com.zhixiang.balancewallet.ui.MainActivity used the global configuration.
08-27 08:10:28.216 4224-4224/com.zhixiang.balancewallet D/AndroidAutoSize: The com.zhixiang.balancewallet.ui.MainActivity has been adapted!
MainActivity Info: isBaseOnWidth = true, designWidthInDp = 750.000000, targetDensity = 1.000000, targetScaledDensity = 1.000000, targetDensityDpi = 160
配置只是配置了AndroidManifest文件而已,View写的是DP单位
android 9.0限制私有api的访问,这个库里面有反射,会不会有影响?
设计给的图片是750px 1334px 然后我在AndroidManifest设置的是375667
发现状态栏和标题栏之间有点空隙。暂时只测了红米note5 和 金立S9L 没问题
获取状态栏高度的代码
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
还有个问题就是,字体也可以用px吗 ?
java.lang.NoClassDefFoundError: me.jessyan.autosize.FragmentLifecycleCallbacksImpl
at me.jessyan.autosize.ActivityLifecycleCallbacksImpl.(ActivityLifecycleCallbacksImpl.java:45)
日志中提示FragmentLifecycleCallbacksImpl这个类找不到,报错,但是我去External Libraries里面能找到引入的Autosize包里面有该类。所以这个问题显得很诡异。
不知道问题出在哪里,只能回到原点,自己新建工程,重新引入,运行,没问题!这就奇怪了。于是我对比gradle里面的依赖及配置信息,由于使用的是Android studio 3.1.4,compileSdkVersion,targetSdkVersion,com.android.support:appcompat-v7:26.1.0等属性版本不一致,版本23可能太低了, 改成使用新建工程后的默认版本26,具体版本可参照新建工程后的版本,问题解决了。
我也遇到过类似的问题,报错日志:Please call the AutoSizeConfig#init() first
崩溃场景:华为,小米等多部手机没问题,后来公司同事用乐视手机就出现这个崩溃
解决办法:注释掉setAutoAdaptStrategy(new AutoAdaptStrategy())方法就不会崩溃了
查看代码:
//第一步:查看源码有两个地方有这个文字提示
Preconditions.checkNotNull(mApplication, "Please call the AutoSizeConfig#init() first");
Preconditions.checkNotNull(mActivityLifecycleCallbacks, "Please call the AutoSizeConfig#init() first");
//第二步:再进去看代码如下所示
//可知传进来的reference为null空值导致出现问题
public static <T> T checkNotNull(T reference, Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
} else {
return reference;
}
}
//第三步:查看什么时候创建mApplication和mActivityLifecycleCallbacks对象
//mActivityLifecycleCallbacks对象是在AutoSizeConfig类中的init方法中创建的,第206行
//mApplication对象实在AutoSizeConfig类中init方法中赋值的,第171行
//而init初始化的操作又是在InitProvider类中的onCreate方法中初始化的,(Application) getContext().getApplicationContext()获取全局application会不会出现问题呢?而这个需要的application上下文能否让开发者传递进来呢?
@Override
public boolean onCreate() {
AutoSizeConfig.getInstance()
.setLog(true)
.init((Application) getContext().getApplicationContext())
.setUseDeviceSize(false);
return true;
}
出现bug原因分析
还不清楚,很奇怪,希望得到大神解答
崩溃日志信息
java.lang.RuntimeException: Unable to create application com.paidian.hwmc.base.SophixStubApplication: java.lang.NullPointerException: Please call the AutoSizeConfig#init() first
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5210)
at android.app.ActivityThread.-wrap3(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1659)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:5981)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:749)
Caused by: java.lang.NullPointerException: Please call the AutoSizeConfig#init() first
at me.jessyan.autosize.utils.Preconditions.checkNotNull(Preconditions.java:77)
at me.jessyan.autosize.AutoSizeConfig.setAutoAdaptStrategy(AutoSizeConfig.java:247)
at com.paidian.hwmc.base.BaseApplication.initAutoSizeConfig(BaseApplication.java:385)
at com.paidian.hwmc.base.BaseApplication.onCreate(BaseApplication.java:133)
at com.taobao.sophix.a.c.b(Sophix:722)
at com.taobao.sophix.a.e.a(Sophix:177)
at com.taobao.sophix.SophixApplication.onCreate(Sophix:17)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1018)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5207)
at android.app.ActivityThread.-wrap3(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1659)
at android.os.Handler.dispatchMessage(Handler.java:111)
at android.os.Looper.loop(Looper.java:207)
at android.app.ActivityThread.main(ActivityThread.java:5981)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:749)
型号:nexus 6P
log:
designWidthInDp = 0, designHeightInDp = 768, screenWidth = 2560, screenHeight = 1440
initDensity = 3.5, initScaledDensity = 3.5
HomeActivity Info: isBaseOnWidth = false, designHeightInDp = 768.000000, targetDensity = 1.765625, targetScaledDensity = 1.765625, targetDensityDpi = 282
解锁-屏幕旋转后
initScaledDensity = 3.5 on ConfigurationChanged
initScaledDensity = 3.5 on ConfigurationChanged
fg中recycleview的item就有异常了 和刚进来的不一致。
后来 我在锁屏的时候调用了 stop 在开屏的时候加上 restart发现的log是这样的
HomeActivity Info: isBaseOnWidth = false, designHeightInDp = 768.000000, targetDensity = 3.333333, targetScaledDensity = 3.333333, targetDensityDpi = 533
屏幕旋转后 尺寸不同了 怎么修复呢
java.lang.RuntimeException: Unable to create application com.crest.crest_parent.application.MyApplication: java.lang.NullPointerException: Please call the AutoSizeConfig#init() first
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4710)
at android.app.ActivityThread.-wrap1(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.NullPointerException: Please call the AutoSizeConfig#init() first
at me.jessyan.autosize.utils.Preconditions.checkNotNull(Preconditions.java:77)
at me.jessyan.autosize.AutoSizeConfig.setAutoAdaptStrategy(AutoSizeConfig.java:247)
at com.crest.crest_parent.application.MyApplication.initAutoSizeConfig(MyApplication.java:179)
at com.crest.crest_parent.application.MyApplication.onCreate(MyApplication.java:70)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1013)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4707)
at android.app.ActivityThread.-wrap1(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
在模拟器上运行,有底部导航栏的话,适配的时候会有一条白底,用命令隐藏底部导航栏 adb shell settings put global policy_control immersive.navigation=* 就没有白底了。有人在真机测试中遇到类似的情况吗?
SettingActivity Info: isBaseOnWidth = true, designWidthInDp = 360.000000, targetDensity = 4.000000, targetScaledDensity = 4.000000, targetDensityDpi = 640
如题,问题不大~~
.9图片拉伸变形,没有按照制定好的拉伸范围进行拉伸。当去掉AndroidAutoSize后,.9图片就可以正常按照制定的拉伸范围拉伸。
+ 如果你想用好 AndroidAutoSize,少出问题,请认真看完下面的所有文字,不要嫌字数长,拒绝做伸手党!!!
Tips: 今日头条适配方案的原理来源于修改 DisplayMetrics#density,因为 DisplayMetrics#density 是全局的,所以只要 DisplayMetrics#density 一经修改,项目中的所有页面、所有控件都可以奏效,包括三方库控件和系统控件,这就是今日头条屏幕适配方案天然拥有优于其他屏幕适配方案的低成本和低侵入性
但 DisplayMetrics#density 是公有的,谁都有权限修改,AndroidAutoSize 可以把 DisplayMetrics#density 修改成一个可以完成屏幕适配的值,其他三方库、Android 系统、以及项目成员就可以把 DisplayMetrics#density 修改或恢复成另一个值,这都将导致屏幕适配的失效,特别是在某些定制系统上,因为这个定制系统做的某些特殊操作都是未知的 (但 AndroidAutoSize 开源这么久,大多数系统都是很稳定的)
但这个问题 AndroidAutoSize 却无能为力,因为 DisplayMetrics#density 是公有的,AndroidAutoSize 只不过是修改 DisplayMetrics#density 中的一员,系统并没有赋予 AndroidAutoSize 可以锁死 DisplayMetrics#density 为某一特定值的权限,而只要其他代码拥有修改 DisplayMetrics#density 的权限,就有不稳定性的产生
所以该问题目前唯一的解决方案就是确保在页面显示到屏幕上的 0.0000000000000000000000000001 秒之前,将 DisplayMetrics#density 修改为期望的值,哪怕页面显示完的后一秒,DisplayMetrics#density 又被修改为其他值,只要这个页面不重建,就不会影响已经显示了的页面,也就是下面的第一个解决方案
+ 以下内容是大部分 issues 出现的核心原因,本框架上手容易,但是要想真正的驾驭,需要熟悉其中的原理!
由于 DisplayMetrics#density 在整个 Application 中是公有并且唯一的,所以并不存在在每一个 Activity 或者 Fragment 中都拥有一个独立的 DisplayMetrics#density,框架之所以能实现每个 Activity 或 Fragment 都可以拥有不同的设计图尺寸,是因为框架自动在每个 Activity 或 Fragment 显示 UI 之前将 DisplayMetrics#density 更改为了它期望的正确值,但是框架并不知道项目中的每个 UI 需要显示或更新到屏幕上的正确时机,只是默认在 setContentView() 之前修改了 DisplayMetrics#density,但在实际开发中,业务何其复杂,有些时候 UI 的显示或更新并不只局限于 setContentView(),比如说在某些业务中会出现一些自定义事件触发自定义 View 进行 requestLayout() 、invalidate() ,或者 RecyclerView 在某些时机需要再次更新 Item 中的内容,但要注意的是,在所有会影响 UI 显示的方法之前都应该保证 DisplayMetrics#density 切换到正确的值,如果框架在你的自定义事件中并未在 UI 显示或更新前将 DisplayMetrics#density 设置为期望的值,就会出现屏幕适配异常的问题,请你辅助框架在 UI 显示或更新之前调用 AutoSize 中的方法,将 DisplayMetrics#density 更改为期望的值。
但如果你对框架原理不是非常熟悉,请不要频繁的更改 DisplayMetrics#density(更改设计图尺寸,以及横竖屏切换都会导致 DisplayMetrics#density 的更改),如果又不懂原理,又喜欢更改 DisplayMetrics#density,那就有一定概率出现你们经常在 issues 中提到的一些屏幕适配失效或者异常的问题,这有很大概率是因为你们自己不懂原理又瞎鸡巴乱搞导致的!出现这个问题的原因就是因为更改 DisplayMetrics#density 后导致期望的 DisplayMetrics#density 与实际的 DisplayMetrics#density 不一致,什么叫懂原理?请把 *年你的屏幕适配方式该升级了! 系列文章都看一遍,再认真看下 AndroidAutoSize 中核心类的源码,别问我核心类是哪些,问就是全部!
一般情况下,下面的 万能解决方案 就能解决大部分的问题,因为在每个与 UI 相关的方法执行时,都会调用 getResources() 方法, 但这个解决方案只能保证一个 Activity 只存在一个设计图尺寸,如果你的一个 Activity 出现了多个设计图尺寸,比如说嵌入了系统 View、第三方 View (系统 View 或第三方 View 的设计图尺寸有很大概率和你项目的设计图尺寸不一致)或者不同设计图尺寸的 Fragment,那这个 万能解决方案 也不能解决所有问题。
@Override
public Resources getResources() {
//需要升级到 v1.1.2 及以上版本才能使用 AutoSizeCompat
AutoSizeCompat.autoConvertDensityOfGlobal(super.getResources());//如果没有自定义需求用这个方法
AutoSizeCompat.autoConvertDensity(super.getResources(), 667, false);//如果有自定义需求就用这个方法
return super.getResources();
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
AutoSizeCompat.autoConvertDensityOfGlobal(getResources());//如果没有自定义需求用这个方法
AutoSizeCompat.autoConvertDensity(getResources(), 667, false);//如果有自定义需求就用这个方法
return super.generateLayoutParams(attrs);
}
Bitmap bitmap = Bitmap.createBitmap(activity.getResources().getDisplayMetrics(),width,height,config);
我们公司车载项目我采用的是适配:
https://github.com/yatoooon/AndroidScreenAdaptation
private float calculateValue(float value) {
if ("px".equals(unit)) {
return value * ((float) actualWidth / (float) designWidth);
} else if ("dp".equals(unit)) {
int dip = dp2pxUtils.px2dip(actualDensity, value);
value = ((float) designDpi / 160) * dip;
return value * ((float) actualWidth / (float) designWidth);
}
return 0;
}
原本设计以宽度作为标准进行适配,后面发现一个问题:byd车载的屏幕是:1000x1920 ; 按照标准图(宽高640x360(16:9) ),1920/640 = 3; 结果实际高度1000 < 1080 ;导致底部view被遮挡。
后面,我修改了该方法:
/**
* Description: 标准宽高640x360(16:9) density = 1.0 dpi = 160
* 1. 高度低于设计高度,以高度作标准缩放;
* 2. 高度高于设计高度,但是高度:宽度 < 9:16,以高度作标准缩放;
* 3. 其余以宽度作标准缩放;
* @param
* @return
*/
public float calculateValue(float value) {
if ("px".equals(unit)) {
return value * ((float) actualWidth / (float) designWidth);
} else if ("dp".equals(unit)) {
int dip = dp2pxUtils.px2dip(actualDensity, value);
value = ((float) designDpi / 160) * dip;
if (actualHeight < designHeight || actualWidth * designHeight / designWidth > actualHeight) {
return value * ((float) actualHeight / (float) designHeight);
}
return value * ((float) actualWidth / (float) designWidth);
}
return 0;
}
我提的问题是:
头条的方案,本质也是缩放,也会存在这个问题?不知你同意否?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.