Skip to content

Commit

Permalink
Create AutoSizeCompat
Browse files Browse the repository at this point in the history
  • Loading branch information
JessYanCoding committed Jan 8, 2019
1 parent de553cc commit 365ed05
Showing 1 changed file with 294 additions and 0 deletions.
294 changes: 294 additions & 0 deletions autosize/src/main/java/me/jessyan/autosize/AutoSizeCompat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
/*
* Copyright 2019 JessYan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.jessyan.autosize;

import android.app.Activity;
import android.content.res.Resources;
import android.util.DisplayMetrics;

import java.lang.reflect.Field;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import me.jessyan.autosize.external.ExternalAdaptInfo;
import me.jessyan.autosize.external.ExternalAdaptManager;
import me.jessyan.autosize.internal.CustomAdapt;
import me.jessyan.autosize.utils.LogUtils;
import me.jessyan.autosize.utils.Preconditions;

/**
* ================================================
* 当遇到本来适配正常的布局突然出现适配失效,适配异常等问题, 重写当前 {@link Activity} 的 {@link Activity#getResources()} 并调用
* {@link AutoSizeCompat} 的对应方法即可解决问题
* <p>
* Created by JessYan on 2018/8/8 19:20
* <a href="mailto:[email protected]">Contact me</a>
* <a href="https://github.com/JessYanCoding">Follow me</a>
* ================================================
*/
public final class AutoSizeCompat {
private static Map<String, DisplayMetricsInfo> mCache = new ConcurrentHashMap<>();

private AutoSizeCompat() {
throw new IllegalStateException("you can't instantiate me!");
}

/**
* 使用 AndroidAutoSize 初始化时设置的默认适配参数进行适配 (AndroidManifest 的 Meta 属性)
*
* @param resources {@link Resources}
*/
public static void autoConvertDensityOfGlobal(Resources resources) {
if (AutoSizeConfig.getInstance().isBaseOnWidth()) {
autoConvertDensityBaseOnWidth(resources, AutoSizeConfig.getInstance().getDesignWidthInDp());
} else {
autoConvertDensityBaseOnHeight(resources, AutoSizeConfig.getInstance().getDesignHeightInDp());
}
}

/**
* 使用 {@link Activity} 或 {@link android.support.v4.app.Fragment} 的自定义参数进行适配
*
* @param resources {@link Resources}
* @param customAdapt {@link Activity} 或 {@link android.support.v4.app.Fragment} 需实现 {@link CustomAdapt}
*/
public static void autoConvertDensityOfCustomAdapt(Resources resources, CustomAdapt customAdapt) {
Preconditions.checkNotNull(customAdapt, "customAdapt == null");
float sizeInDp = customAdapt.getSizeInDp();

//如果 CustomAdapt#getSizeInDp() 返回 0, 则使用在 AndroidManifest 上填写的设计图尺寸
if (sizeInDp <= 0) {
if (customAdapt.isBaseOnWidth()) {
sizeInDp = AutoSizeConfig.getInstance().getDesignWidthInDp();
} else {
sizeInDp = AutoSizeConfig.getInstance().getDesignHeightInDp();
}
}
autoConvertDensity(resources, sizeInDp, customAdapt.isBaseOnWidth());
}

/**
* 使用外部三方库的 {@link Activity} 或 {@link android.support.v4.app.Fragment} 的自定义适配参数进行适配
*
* @param resources {@link Resources}
* @param externalAdaptInfo 三方库的 {@link Activity} 或 {@link android.support.v4.app.Fragment} 提供的适配参数, 需要配合 {@link ExternalAdaptManager#addExternalAdaptInfoOfActivity(Class, ExternalAdaptInfo)}
*/
public static void autoConvertDensityOfExternalAdaptInfo(Resources resources, ExternalAdaptInfo externalAdaptInfo) {
Preconditions.checkNotNull(externalAdaptInfo, "externalAdaptInfo == null");
float sizeInDp = externalAdaptInfo.getSizeInDp();

//如果 ExternalAdaptInfo#getSizeInDp() 返回 0, 则使用在 AndroidManifest 上填写的设计图尺寸
if (sizeInDp <= 0) {
if (externalAdaptInfo.isBaseOnWidth()) {
sizeInDp = AutoSizeConfig.getInstance().getDesignWidthInDp();
} else {
sizeInDp = AutoSizeConfig.getInstance().getDesignHeightInDp();
}
}
autoConvertDensity(resources, sizeInDp, externalAdaptInfo.isBaseOnWidth());
}

/**
* 以宽度为基准进行适配
*
* @param resources {@link Resources}
* @param designWidthInDp 设计图的总宽度
*/
public static void autoConvertDensityBaseOnWidth(Resources resources, float designWidthInDp) {
autoConvertDensity(resources, designWidthInDp, true);
}

/**
* 以高度为基准进行适配
*
* @param resources {@link Resources}
* @param designHeightInDp 设计图的总高度
*/
public static void autoConvertDensityBaseOnHeight(Resources resources, float designHeightInDp) {
autoConvertDensity(resources, designHeightInDp, false);
}

/**
* 这里是今日头条适配方案的核心代码, 核心在于根据当前设备的实际情况做自动计算并转换 {@link DisplayMetrics#density}、
* {@link DisplayMetrics#scaledDensity}、{@link DisplayMetrics#densityDpi} 这三个值, 额外增加 {@link DisplayMetrics#xdpi}
* 以支持单位 {@code pt}、{@code in}、{@code mm}
*
* @param resources {@link Resources}
* @param sizeInDp 设计图上的设计尺寸, 单位 dp, 如果 {@param isBaseOnWidth} 设置为 {@code true},
* {@param sizeInDp} 则应该填写设计图的总宽度, 如果 {@param isBaseOnWidth} 设置为 {@code false},
* {@param sizeInDp} 则应该填写设计图的总高度
* @param isBaseOnWidth 是否按照宽度进行等比例适配, {@code true} 为以宽度进行等比例适配, {@code false} 为以高度进行等比例适配
* @see <a href="https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA">今日头条官方适配方案</a>
*/
public static void autoConvertDensity(Resources resources, float sizeInDp, boolean isBaseOnWidth) {
Preconditions.checkNotNull(resources, "resources == null");

float subunitsDesignSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getUnitsManager().getDesignWidth()
: AutoSizeConfig.getInstance().getUnitsManager().getDesignHeight();
subunitsDesignSize = subunitsDesignSize > 0 ? subunitsDesignSize : sizeInDp;

int screenSize = isBaseOnWidth ? AutoSizeConfig.getInstance().getScreenWidth()
: AutoSizeConfig.getInstance().getScreenHeight();
String key = sizeInDp + "|" + subunitsDesignSize + "|" + isBaseOnWidth + "|"
+ AutoSizeConfig.getInstance().isUseDeviceSize() + "|"
+ AutoSizeConfig.getInstance().getInitScaledDensity() + "|"
+ screenSize;

DisplayMetricsInfo displayMetricsInfo = mCache.get(key);

float targetDensity = 0;
int targetDensityDpi = 0;
float targetScaledDensity = 0;
float targetXdpi = 0;

if (displayMetricsInfo == null) {
if (isBaseOnWidth) {
targetDensity = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / sizeInDp;
} else {
targetDensity = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / sizeInDp;
}
float scale = AutoSizeConfig.getInstance().isExcludeFontScale() ? 1 : AutoSizeConfig.getInstance().
getInitScaledDensity() * 1.0f / AutoSizeConfig.getInstance().getInitDensity();
targetScaledDensity = targetDensity * scale;
targetDensityDpi = (int) (targetDensity * 160);

if (isBaseOnWidth) {
targetXdpi = AutoSizeConfig.getInstance().getScreenWidth() * 1.0f / subunitsDesignSize;
} else {
targetXdpi = AutoSizeConfig.getInstance().getScreenHeight() * 1.0f / subunitsDesignSize;
}

mCache.put(key, new DisplayMetricsInfo(targetDensity, targetDensityDpi, targetScaledDensity, targetXdpi));
} else {
targetDensity = displayMetricsInfo.getDensity();
targetDensityDpi = displayMetricsInfo.getDensityDpi();
targetScaledDensity = displayMetricsInfo.getScaledDensity();
targetXdpi = displayMetricsInfo.getXdpi();
}

setDensity(resources, targetDensity, targetDensityDpi, targetScaledDensity, targetXdpi);

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"
, isBaseOnWidth, isBaseOnWidth ? "designWidthInDp"
: "designHeightInDp", sizeInDp, isBaseOnWidth ? "designWidthInSubunits" : "designHeightInSubunits", subunitsDesignSize
, targetDensity, targetScaledDensity, targetDensityDpi, targetXdpi));
}

/**
* 取消适配
*
* @param resources {@link Resources}
*/
public static void cancelAdapt(Resources resources) {
float initXdpi = AutoSizeConfig.getInstance().getInitXdpi();
switch (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits()) {
case PT:
initXdpi = initXdpi / 72f;
break;
case MM:
initXdpi = initXdpi / 25.4f;
break;
default:
}
setDensity(resources, AutoSizeConfig.getInstance().getInitDensity()
, AutoSizeConfig.getInstance().getInitDensityDpi()
, AutoSizeConfig.getInstance().getInitScaledDensity()
, initXdpi);
}

/**
* 给几大 {@link DisplayMetrics} 赋值
*
* @param resources {@link Resources}
* @param density {@link DisplayMetrics#density}
* @param densityDpi {@link DisplayMetrics#densityDpi}
* @param scaledDensity {@link DisplayMetrics#scaledDensity}
* @param xdpi {@link DisplayMetrics#xdpi}
*/
private static void setDensity(Resources resources, float density, int densityDpi, float scaledDensity, float xdpi) {
//兼容 MIUI
DisplayMetrics activityDisplayMetricsOnMIUI = getMetricsOnMiui(resources);
DisplayMetrics appDisplayMetricsOnMIUI = getMetricsOnMiui(AutoSizeConfig.getInstance().getApplication().getResources());

if (activityDisplayMetricsOnMIUI != null) {
setDensity(activityDisplayMetricsOnMIUI, density, densityDpi, scaledDensity, xdpi);
} else {
DisplayMetrics activityDisplayMetrics = resources.getDisplayMetrics();
setDensity(activityDisplayMetrics, density, densityDpi, scaledDensity, xdpi);
}

if (appDisplayMetricsOnMIUI != null) {
setDensity(appDisplayMetricsOnMIUI, density, densityDpi, scaledDensity, xdpi);
} else {
DisplayMetrics appDisplayMetrics = AutoSizeConfig.getInstance().getApplication().getResources().getDisplayMetrics();
setDensity(appDisplayMetrics, density, densityDpi, scaledDensity, xdpi);
}
}

/**
* 赋值
*
* @param displayMetrics {@link DisplayMetrics}
* @param density {@link DisplayMetrics#density}
* @param densityDpi {@link DisplayMetrics#densityDpi}
* @param scaledDensity {@link DisplayMetrics#scaledDensity}
* @param xdpi {@link DisplayMetrics#xdpi}
*/
private static void setDensity(DisplayMetrics displayMetrics, float density, int densityDpi, float scaledDensity, float xdpi) {
if (AutoSizeConfig.getInstance().getUnitsManager().isSupportDP()) {
displayMetrics.density = density;
displayMetrics.densityDpi = densityDpi;
}
if (AutoSizeConfig.getInstance().getUnitsManager().isSupportSP()) {
displayMetrics.scaledDensity = scaledDensity;
}
switch (AutoSizeConfig.getInstance().getUnitsManager().getSupportSubunits()) {
case NONE:
break;
case PT:
displayMetrics.xdpi = xdpi * 72f;
break;
case IN:
displayMetrics.xdpi = xdpi;
break;
case MM:
displayMetrics.xdpi = xdpi * 25.4f;
break;
default:
}
}

/**
* 解决 MIUI 更改框架导致的 MIUI7 + Android5.1.1 上出现的失效问题 (以及极少数基于这部分 MIUI 去掉 ART 然后置入 XPosed 的手机)
* 来源于: https://github.com/Firedamp/Rudeness/blob/master/rudeness-sdk/src/main/java/com/bulong/rudeness/RudenessScreenHelper.java#L61:5
*
* @param resources {@link Resources}
* @return {@link DisplayMetrics}, 可能为 {@code null}
*/
private static DisplayMetrics getMetricsOnMiui(Resources resources) {
if ("MiuiResources".equals(resources.getClass().getSimpleName()) || "XResources".equals(resources.getClass().getSimpleName())) {
try {
Field field = Resources.class.getDeclaredField("mTmpMetrics");
field.setAccessible(true);
return (DisplayMetrics) field.get(resources);
} catch (Exception e) {
return null;
}
}
return null;
}
}

0 comments on commit 365ed05

Please sign in to comment.