Skip to content

Commit 365ed05

Browse files
committed
Create AutoSizeCompat
1 parent de553cc commit 365ed05

File tree

1 file changed

+294
-0
lines changed

1 file changed

+294
-0
lines changed
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
/*
2+
* Copyright 2019 JessYan
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package me.jessyan.autosize;
17+
18+
import android.app.Activity;
19+
import android.content.res.Resources;
20+
import android.util.DisplayMetrics;
21+
22+
import java.lang.reflect.Field;
23+
import java.util.Locale;
24+
import java.util.Map;
25+
import java.util.concurrent.ConcurrentHashMap;
26+
27+
import me.jessyan.autosize.external.ExternalAdaptInfo;
28+
import me.jessyan.autosize.external.ExternalAdaptManager;
29+
import me.jessyan.autosize.internal.CustomAdapt;
30+
import me.jessyan.autosize.utils.LogUtils;
31+
import me.jessyan.autosize.utils.Preconditions;
32+
33+
/**
34+
* ================================================
35+
* 当遇到本来适配正常的布局突然出现适配失效,适配异常等问题, 重写当前 {@link Activity} 的 {@link Activity#getResources()} 并调用
36+
* {@link AutoSizeCompat} 的对应方法即可解决问题
37+
* <p>
38+
* Created by JessYan on 2018/8/8 19:20
39+
* <a href="mailto:[email protected]">Contact me</a>
40+
* <a href="https://github.com/JessYanCoding">Follow me</a>
41+
* ================================================
42+
*/
43+
public final class AutoSizeCompat {
44+
private static Map<String, DisplayMetricsInfo> mCache = new ConcurrentHashMap<>();
45+
46+
private AutoSizeCompat() {
47+
throw new IllegalStateException("you can't instantiate me!");
48+
}
49+
50+
/**
51+
* 使用 AndroidAutoSize 初始化时设置的默认适配参数进行适配 (AndroidManifest 的 Meta 属性)
52+
*
53+
* @param resources {@link Resources}
54+
*/
55+
public static void autoConvertDensityOfGlobal(Resources resources) {
56+
if (AutoSizeConfig.getInstance().isBaseOnWidth()) {
57+
autoConvertDensityBaseOnWidth(resources, AutoSizeConfig.getInstance().getDesignWidthInDp());
58+
} else {
59+
autoConvertDensityBaseOnHeight(resources, AutoSizeConfig.getInstance().getDesignHeightInDp());
60+
}
61+
}
62+
63+
/**
64+
* 使用 {@link Activity} 或 {@link android.support.v4.app.Fragment} 的自定义参数进行适配
65+
*
66+
* @param resources {@link Resources}
67+
* @param customAdapt {@link Activity} 或 {@link android.support.v4.app.Fragment} 需实现 {@link CustomAdapt}
68+
*/
69+
public static void autoConvertDensityOfCustomAdapt(Resources resources, CustomAdapt customAdapt) {
70+
Preconditions.checkNotNull(customAdapt, "customAdapt == null");
71+
float sizeInDp = customAdapt.getSizeInDp();
72+
73+
//如果 CustomAdapt#getSizeInDp() 返回 0, 则使用在 AndroidManifest 上填写的设计图尺寸
74+
if (sizeInDp <= 0) {
75+
if (customAdapt.isBaseOnWidth()) {
76+
sizeInDp = AutoSizeConfig.getInstance().getDesignWidthInDp();
77+
} else {
78+
sizeInDp = AutoSizeConfig.getInstance().getDesignHeightInDp();
79+
}
80+
}
81+
autoConvertDensity(resources, sizeInDp, customAdapt.isBaseOnWidth());
82+
}
83+
84+
/**
85+
* 使用外部三方库的 {@link Activity} 或 {@link android.support.v4.app.Fragment} 的自定义适配参数进行适配
86+
*
87+
* @param resources {@link Resources}
88+
* @param externalAdaptInfo 三方库的 {@link Activity} 或 {@link android.support.v4.app.Fragment} 提供的适配参数, 需要配合 {@link ExternalAdaptManager#addExternalAdaptInfoOfActivity(Class, ExternalAdaptInfo)}
89+
*/
90+
public static void autoConvertDensityOfExternalAdaptInfo(Resources resources, ExternalAdaptInfo externalAdaptInfo) {
91+
Preconditions.checkNotNull(externalAdaptInfo, "externalAdaptInfo == null");
92+
float sizeInDp = externalAdaptInfo.getSizeInDp();
93+
94+
//如果 ExternalAdaptInfo#getSizeInDp() 返回 0, 则使用在 AndroidManifest 上填写的设计图尺寸
95+
if (sizeInDp <= 0) {
96+
if (externalAdaptInfo.isBaseOnWidth()) {
97+
sizeInDp = AutoSizeConfig.getInstance().getDesignWidthInDp();
98+
} else {
99+
sizeInDp = AutoSizeConfig.getInstance().getDesignHeightInDp();
100+
}
101+
}
102+
autoConvertDensity(resources, sizeInDp, externalAdaptInfo.isBaseOnWidth());
103+
}
104+
105+
/**
106+
* 以宽度为基准进行适配
107+
*
108+
* @param resources {@link Resources}
109+
* @param designWidthInDp 设计图的总宽度
110+
*/
111+
public static void autoConvertDensityBaseOnWidth(Resources resources, float designWidthInDp) {
112+
autoConvertDensity(resources, designWidthInDp, true);
113+
}
114+
115+
/**
116+
* 以高度为基准进行适配
117+
*
118+
* @param resources {@link Resources}
119+
* @param designHeightInDp 设计图的总高度
120+
*/
121+
public static void autoConvertDensityBaseOnHeight(Resources resources, float designHeightInDp) {
122+
autoConvertDensity(resources, designHeightInDp, false);
123+
}
124+
125+
/**
126+
* 这里是今日头条适配方案的核心代码, 核心在于根据当前设备的实际情况做自动计算并转换 {@link DisplayMetrics#density}、
127+
* {@link DisplayMetrics#scaledDensity}、{@link DisplayMetrics#densityDpi} 这三个值, 额外增加 {@link DisplayMetrics#xdpi}
128+
* 以支持单位 {@code pt}、{@code in}、{@code mm}
129+
*
130+
* @param resources {@link Resources}
131+
* @param sizeInDp 设计图上的设计尺寸, 单位 dp, 如果 {@param isBaseOnWidth} 设置为 {@code true},
132+
* {@param sizeInDp} 则应该填写设计图的总宽度, 如果 {@param isBaseOnWidth} 设置为 {@code false},
133+
* {@param sizeInDp} 则应该填写设计图的总高度
134+
* @param isBaseOnWidth 是否按照宽度进行等比例适配, {@code true} 为以宽度进行等比例适配, {@code false} 为以高度进行等比例适配
135+
* @see <a href="https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA">今日头条官方适配方案</a>
136+
*/
137+
public static void autoConvertDensity(Resources resources, float sizeInDp, boolean isBaseOnWidth) {
138+
Preconditions.checkNotNull(resources, "resources == null");
139+
140+
float subunitsDesignSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getUnitsManager().getDesignWidth()
141+
: AutoSizeConfig.getInstance().getUnitsManager().getDesignHeight();
142+
subunitsDesignSize = subunitsDesignSize > 0 ? subunitsDesignSize : sizeInDp;
143+
144+
int screenSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getScreenWidth()
145+
: AutoSizeConfig.getInstance().getScreenHeight();
146+
String key = sizeInDp + "|" + subunitsDesignSize + "|" + isBaseOnWidth + "|"
147+
+ AutoSizeConfig.getInstance().isUseDeviceSize() + "|"
148+
+ AutoSizeConfig.getInstance().getInitScaledDensity() + "|"
149+
+ screenSize;
150+
151+
DisplayMetricsInfo displayMetricsInfo = mCache.get(key);
152+
153+
float targetDensity = 0;
154+
int targetDensityDpi = 0;
155+
float targetScaledDensity = 0;
156+
float targetXdpi = 0;
157+
158+
if (displayMetricsInfo == null) {
159+
if (isBaseOnWidth) {
160+
targetDensity = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / sizeInDp;
161+
} else {
162+
targetDensity = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / sizeInDp;
163+
}
164+
float scale = AutoSizeConfig.getInstance().isExcludeFontScale() ? 1 : AutoSizeConfig.getInstance().
165+
getInitScaledDensity() * 1.0f / AutoSizeConfig.getInstance().getInitDensity();
166+
targetScaledDensity = targetDensity * scale;
167+
targetDensityDpi = (int) (targetDensity * 160);
168+
169+
if (isBaseOnWidth) {
170+
targetXdpi = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / subunitsDesignSize;
171+
} else {
172+
targetXdpi = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / subunitsDesignSize;
173+
}
174+
175+
mCache.put(key, new DisplayMetricsInfo(targetDensity, targetDensityDpi, targetScaledDensity, targetXdpi));
176+
} else {
177+
targetDensity = displayMetricsInfo.getDensity();
178+
targetDensityDpi = displayMetricsInfo.getDensityDpi();
179+
targetScaledDensity = displayMetricsInfo.getScaledDensity();
180+
targetXdpi = displayMetricsInfo.getXdpi();
181+
}
182+
183+
setDensity(resources, targetDensity, targetDensityDpi, targetScaledDensity, targetXdpi);
184+
185+
LogUtils.d(String.format(Locale.ENGLISH, "AutoSizeCompat is running! \nAdapt Info: isBaseOnWidth = %s, %s = %f, %s = %f, targetDensity = %f, targetScaledDensity = %f, targetDensityDpi = %d, targetXdpi = %f"
186+
, isBaseOnWidth, isBaseOnWidth ? "designWidthInDp"
187+
: "designHeightInDp", sizeInDp, isBaseOnWidth ? "designWidthInSubunits" : "designHeightInSubunits", subunitsDesignSize
188+
, targetDensity, targetScaledDensity, targetDensityDpi, targetXdpi));
189+
}
190+
191+
/**
192+
* 取消适配
193+
*
194+
* @param resources {@link Resources}
195+
*/
196+
public static void cancelAdapt(Resources resources) {
197+
float initXdpi = AutoSizeConfig.getInstance().getInitXdpi();
198+
switch (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits()) {
199+
case PT:
200+
initXdpi = initXdpi / 72f;
201+
break;
202+
case MM:
203+
initXdpi = initXdpi / 25.4f;
204+
break;
205+
default:
206+
}
207+
setDensity(resources, AutoSizeConfig.getInstance().getInitDensity()
208+
, AutoSizeConfig.getInstance().getInitDensityDpi()
209+
, AutoSizeConfig.getInstance().getInitScaledDensity()
210+
, initXdpi);
211+
}
212+
213+
/**
214+
* 给几大 {@link DisplayMetrics} 赋值
215+
*
216+
* @param resources {@link Resources}
217+
* @param density {@link DisplayMetrics#density}
218+
* @param densityDpi {@link DisplayMetrics#densityDpi}
219+
* @param scaledDensity {@link DisplayMetrics#scaledDensity}
220+
* @param xdpi {@link DisplayMetrics#xdpi}
221+
*/
222+
private static void setDensity(Resources resources, float density, int densityDpi, float scaledDensity, float xdpi) {
223+
//兼容 MIUI
224+
DisplayMetrics activityDisplayMetricsOnMIUI = getMetricsOnMiui(resources);
225+
DisplayMetrics appDisplayMetricsOnMIUI = getMetricsOnMiui(AutoSizeConfig.getInstance().getApplication().getResources());
226+
227+
if (activityDisplayMetricsOnMIUI != null) {
228+
setDensity(activityDisplayMetricsOnMIUI, density, densityDpi, scaledDensity, xdpi);
229+
} else {
230+
DisplayMetrics activityDisplayMetrics = resources.getDisplayMetrics();
231+
setDensity(activityDisplayMetrics, density, densityDpi, scaledDensity, xdpi);
232+
}
233+
234+
if (appDisplayMetricsOnMIUI != null) {
235+
setDensity(appDisplayMetricsOnMIUI, density, densityDpi, scaledDensity, xdpi);
236+
} else {
237+
DisplayMetrics appDisplayMetrics = AutoSizeConfig.getInstance().getApplication().getResources().getDisplayMetrics();
238+
setDensity(appDisplayMetrics, density, densityDpi, scaledDensity, xdpi);
239+
}
240+
}
241+
242+
/**
243+
* 赋值
244+
*
245+
* @param displayMetrics {@link DisplayMetrics}
246+
* @param density {@link DisplayMetrics#density}
247+
* @param densityDpi {@link DisplayMetrics#densityDpi}
248+
* @param scaledDensity {@link DisplayMetrics#scaledDensity}
249+
* @param xdpi {@link DisplayMetrics#xdpi}
250+
*/
251+
private static void setDensity(DisplayMetrics displayMetrics, float density, int densityDpi, float scaledDensity, float xdpi) {
252+
if (AutoSizeConfig.getInstance().getUnitsManager().isSupportDP()) {
253+
displayMetrics.density = density;
254+
displayMetrics.densityDpi = densityDpi;
255+
}
256+
if (AutoSizeConfig.getInstance().getUnitsManager().isSupportSP()) {
257+
displayMetrics.scaledDensity = scaledDensity;
258+
}
259+
switch (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits()) {
260+
case NONE:
261+
break;
262+
case PT:
263+
displayMetrics.xdpi = xdpi * 72f;
264+
break;
265+
case IN:
266+
displayMetrics.xdpi = xdpi;
267+
break;
268+
case MM:
269+
displayMetrics.xdpi = xdpi * 25.4f;
270+
break;
271+
default:
272+
}
273+
}
274+
275+
/**
276+
* 解决 MIUI 更改框架导致的 MIUI7 + Android5.1.1 上出现的失效问题 (以及极少数基于这部分 MIUI 去掉 ART 然后置入 XPosed 的手机)
277+
* 来源于: https://github.com/Firedamp/Rudeness/blob/master/rudeness-sdk/src/main/java/com/bulong/rudeness/RudenessScreenHelper.java#L61:5
278+
*
279+
* @param resources {@link Resources}
280+
* @return {@link DisplayMetrics}, 可能为 {@code null}
281+
*/
282+
private static DisplayMetrics getMetricsOnMiui(Resources resources) {
283+
if ("MiuiResources".equals(resources.getClass().getSimpleName()) || "XResources".equals(resources.getClass().getSimpleName())) {
284+
try {
285+
Field field = Resources.class.getDeclaredField("mTmpMetrics");
286+
field.setAccessible(true);
287+
return (DisplayMetrics) field.get(resources);
288+
} catch (Exception e) {
289+
return null;
290+
}
291+
}
292+
return null;
293+
}
294+
}

0 commit comments

Comments
 (0)