##### 相关知识

android度量计算公式

• px = density * dp
• density = dpi / 160
• px = dp * (dpi / 160)
• DisplayMetrics.density
• DisplayMetrics.densityDpi
• DisplayMetrics.scaledDensity

dp 和 px 的转换是通过 DisplayMetrics 中相关的值来计算的，view、bitmap 等元素在计算中的dp转换也是如此。

``````/**
* Converts an unpacked complex data value holding a dimension to its final floating
* point value. The two parameters <var>unit</var> and <var>value</var>
* are as in {@link #TYPE_DIMENSION}.
*
* @param unit The unit to convert from.
* @param value The value to apply the unit to.
* @param metrics Current display metrics to use in the conversion --
*                supplies display density and scaling information.
*
* @return The complex floating point value multiplied by the appropriate
* metrics depending on its unit.
*/
public static float applyDimension(int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/72);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/25.4f);
}
return 0;
}

##### 今日头条方案
###### 原理
• density = px / dp
###### 适配方案
• 给定一个宽高大小固定的标准设计图，支持以宽或高一个维度自适应适配，保持改维度和设计图一致；
• 支持dp和sp单位。
###### 实现

``````final float targetScaledDensity = targetDensity * (appDisplayMetrics.scaledDensity / appDisplayMetrics.density);

``````/**
* 头条处理多设备的方案  setCustomDensity(this, getApplication());
*
* @param activity
* @param application
*/
private void setCustomDensity(Activity activity, final Application application) {

//application
final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();

if (sRoncompatDennsity == 0) {
sRoncompatDennsity = appDisplayMetrics.density;
sRoncompatScaledDensity = appDisplayMetrics.scaledDensity;
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig != null && newConfig.fontScale > 0) {
sRoncompatScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
}
}

@Override
public void onLowMemory() {

}
});
}

//计算宽为360dp 同理可以设置高为640dp的根据实际情况
final float targetDensity = appDisplayMetrics.widthPixels / 360;
final float targetScaledDensity = targetDensity * (sRoncompatScaledDensity / sRoncompatDennsity);
final int targetDensityDpi = (int) (targetDensity * 160);

appDisplayMetrics.density = targetDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
appDisplayMetrics.scaledDensity = targetScaledDensity;

//activity
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();

activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
}

###### 原理
• 基于设计图的宽度值（或高度值）和对应的dpi适配，即根据设备的实际宽度（或高度）相对应的缩放view的尺寸。
• 缩放比率 = value * ((float) actualWidth / (float) designWidth)
###### 适配方案
• 给定一个宽高大小固定的标准设计图，支持以宽或高一个维度自适应适配，保持改维度和设计图一致；
• 支持dp和sp单位。
###### 实现

``````/**
* Created by zhangyuwan0 on 2018/3/21.
*/

public class SimpleConversion implements IConversion {

@Override
if (view.getLayoutParams() != null) {
}
}

}

``````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;
}

###### 项目实际反馈
``````/**
* Created by guokun on 2018/7/21.
* 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;
}

• 自定义 View 基本不支持。每个自定义 View 你需要查看源码调用 calcualteValue 重新计算参数。幸运的是项目自定义 View 不是很多和复杂。最致命的是：wrapcontent 不适配，所有的 View 必须给定尺寸；SeekBarProgress 不支持，手动反射调用方法处理适配问题。
``````@Override
/**Created by guokun on 2018/7/28.
* Description: MyLinearLayout_h381特殊处理
* 1. MyLinearLayout键盘的高度大于标准高度360;
* 2. 这里UI标准图设计bug，未加上20dp 键盘top;
* */
if (view.getTag() != null && (Integer)view.getTag() == MyLinearLayout_h381.getCustomHeight(view.getContext())) {
if (view.getLayoutParams() != null) {
}
}else {
if (view.getLayoutParams() != null) {