Android实际开发中会遇到各种各样的机型,所以就需要对多种机型进行适配。常见的适配在这类就不详细介绍了,大家可以参考这里的系列文章Android通用屏幕适配讲解的已经很详细了。下面说的是:当修改系统的字体大小、显示大小、默认分辨率的情况下,如何明智的进行适配。
设置--->显示--->字体与显示大小:
会看到有如下两项:
1、字体大小:
我们实际开发中习惯将字体的单位(sp)作为默认大小单位。当使用sp作为字体单位是,具体的字体大小是跟随系统字体大小的设置而变化的。所以当希望布局的字体跟随系统设置字体的大小改变的情况下就可以使用sp作为字体大小单位。
当你已经使用的字体单位是sp,你又不想跟随系统设置字体的大小改变而改变的情况下,可以在Activity中使用如下方式:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.fontScale != 1) {
//非默认值
getResources();
}
super.onConfigurationChanged(newConfig);
}
@Override
public Resources getResources() {
Resources res = super.getResources();
if (res.getConfiguration().fontScale != 1) {//非默认值
Configuration newConfig = new Configuration();
newConfig.setToDefaults();//设置默认
res.updateConfiguration(newConfig, res.getDisplayMetrics());
}
return res;
}
而当如果不希望该页面布局的字体大小随系统设置字体的大小改变的情况下就可以直接使用dp作为字体大小单位。此时修改系统的字体大小,布局中的字体是不会变化的。【其实现在很多开发中的字体大小都使用了dp作为字体单位,原因是可以有效避免改变字体大小引起的UI布局错乱】。
2、显示大小:
显示大小是7.0及以上系统才支持的功能【如上图】(当然有些手机商家系统自动屏蔽了该功能,所以你在设置中找不到,一般华为手机是具有这个功能的)。
当然很多人要问,当我改变了显示大小的时候,具体改变的是什么。其实最终改变的就是dpi (dots per inch 即每英寸像素数),而dp与px之间的一个很重要的公式:1dp = 1px * (dpi / 160) ,就是根据dpi算出来的。所以当显示大小调大的时候dpi也会变大,所以最终每1dp占有的像素数就会变大,而当系统分辨率不变的其情况下,对应的dp值就会占用比较大的UI空间(宽度/高度),所以相应的UI显示会变大。
如果希望修改系统”显示大小“的时候也不改版原有的UI布局大小,那么可以使用如下方式:
package com.hongri.layout.util;
import java.lang.reflect.Method;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import android.util.Log;
import android.view.Display;
/**
* Created by zhongyao on 2019-09-03.
*/
public class DisplayUtil {
private static final String TAG = DisplayUtil.class.getSimpleName();
/**
* 系统设置"显示大小"时原有UI样式保持不变:
*
* 1、当调节手机系统"显示大小"【调大】的时候,相应的dpi会变大【dp = (dpi/160) * px】,此时dp就会变大,所以相应的UI布局就会变大。
* 2、当调节手机系统"分辨率"【调小】的时候,相应的dpi会变小【比如由480-->320】。如果此时使用技术手段使dpi保持不变,那么相同的dp就会占用更多的px,所以UI布局就会变大。
*
* @param context
*/
public static void setDefaultDisplay(Context context) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
Configuration origConfig = context.getResources().getConfiguration();
//获取手机出厂时默认的densityDpi【注释1】
origConfig.densityDpi = getDefaultDisplayDensity();
Log.d(TAG, "densityDpi: " + origConfig.densityDpi);
context.getResources().updateConfiguration(origConfig, context.getResources().getDisplayMetrics());
}
}
public static int getDefaultDisplayDensity() {
try {
Class clazz = Class.forName("android.view.WindowManagerGlobal");
Method method = clazz.getMethod("getWindowManagerService");
method.setAccessible(true);
Object iwm = method.invoke(clazz);
Method getInitialDisplayDensity = iwm.getClass().getMethod("getInitialDisplayDensity", int.class);
getInitialDisplayDensity.setAccessible(true);
Object densityDpi = getInitialDisplayDensity.invoke(iwm, Display.DEFAULT_DISPLAY);
return (int)densityDpi;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
}
除了上面一种方式之外,还有一种方式,可以达到同样的效果,
/**
* 获取手机出厂时默认的densityDpi并设置给手机
* 在需要用到的activity中的onCreate中调用该方法
* @param context
*/
public static void setDefaultDisplay(Context context) {
if(Build.VERSION.SDK_INT > 23) {
Configuration origConfig = context.getResources().getConfiguration();
origConfig.densityDpi = getDefaultDisplayDensity(Display.DEFAULT_DISPLAY);//获取手机出厂时默认的densityDpi
context.getResources().updateConfiguration(origConfig, context.getResources().getDisplayMetrics());
}
}
/**
* 获取手机出厂时默认的densityDpi
* @param displayId
* @return
*/
public static int getDefaultDisplayDensity(int displayId) {
try {
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
return wm.getInitialDisplayDensity(displayId);
} catch (RemoteException exc) {
return -1;
}
}
不过远端打包的时候jar包有时会找不到WindowManagerGlobal相关类,所以并没有使用该方法。
然后在Activity设置布局前调用即可:
你会发现,即使调整”显示大小“的时候,也会保持默认原有的UI展示大小。但是,使用这种方式会引入如下问题:
1、这种方式会改变整体的dpi(不仅仅是该Activity)。所以其他页面也会受到影响。
2、另一方,当修改手机系统的分辨率【某些部分手机支持修改手机分辨率】的时候,比如调小了分辨率。那么【注释1】你使用的还是默认分辨率的dpi,所以根据计算公式:1dp = 1px * (dpi / 160),由于dpi还是比较大,所以现在每1dp所占有的像素数就会变多。所以,当前的1dp包含的像素数是大于小分辨率下1dp的像素数。所以UI布局会相应的变大,甚至变大的情况有可能导致布局错乱或展示不全。
综上所述不建议使用技术手段控制系统的显示大小【经验证,市场上绝大多数的APP都不会控制显示大小,也不符合用户习惯】。
设置--->显示--->屏幕分辨率:
一般情况下修改分辨率的情况下,不需要做适配,因为修改分辨率后,相应的布局大小是不会变化的。特殊情况是如上面第二部分谈到的,如果你使用技术手段控制了系统的显示大小,此时在修改分辨率的情况下,就会有问题了。原因就是【比如你调大显示大小,调小分辨率】那么此时屏幕是小分辨率,但还得使用默认的原有(大分辨率)的dpi,所以会导致布局变大。
附:dpi、分辨率、density关系表
文件夹名称 | 分辨率 | dpi | density |
ldpi | 120dpi | 0.75 | |
mdpi | 160dpi | 1 | |
hdpi | 480*800 | 240dpi | 1.5 |
xhdpi | 720*1280 | 320dpi | 2 |
xxhdpi | 1080*1920 | 480dpi | 3 |
xxxhdpi | 1440*2560 | 640dpi | 4 |
布局适配是一门学问。不同的系统及厂商也会有新的变化。需要不断的关注学习才能运用自如!!!
参考:
Android屏幕适配 px,dp,dpi及density的关系与深入理解
Support different pixel densities【官网】