赞
踩
在开发app时,自适应问题是都需要解决的问题,一般大家的实现方式是
1. 给字体不同分辨率设置对应的sp,然后根据不同的分辨率显示对应的大小。
2.设置布局中的单位为dp,这样不同的手机,字体会自动变大变小。
笔者在开发中使用了第二种方法,使用了dp作为单位。这也导致在后续的测试中出现了下面的问题:
1. 当手动修改手机的【设置】中的【显示大小】后,app字体会自动变大导致布局错乱,超出范围
2. 现在很多手机,比如华为,可以在手机的【设置】中手动修改【分辨率】,一旦修改分辨率后,同样也会出现布局字体变大导致布局错乱。
那么怎么能够解决在不进行大的改动的情况下,来解决上面的2个问题,从而使修改【显示大小】后app中的字体保持不变。在修改【分辨率】后,app的字体会自动进行相应的缩放(类似于字体不变,其实是变了),从而保证字体看上去跟原先的没什么两样,保证字体不会因为分辨率修改而变大,导致app布局错乱。
解决方式:
思路:
写一个BaseActivity,所有的Activity都继承它。然后在它的attachBaseContext(Context newBase)声明周期中编写代码,代码通过设置不同的dpi,来保证了上面说的2个问题的解决。
举例说明:
关于问题一:比如,你手机的dpi是440,那么当你把手机的【设置】的【显示大小】的值变大时,手机会自动修改设置dpi为一个值,这也就出现了app字体变大或者变小的现象了。当我们重写attachBaseContext这个生命周期方发后,一旦修改【显示大小】后,便会进出方法,在方法中,我们只需固定dpi为默认的dpi值440就行,这样不管你怎么改,相同分辨率下,dpi不变,你的app中的字体就会不变了。
关于问题二:当手动修改分辨率时,跟修改【显示大小】的机制是不同的,修改分辨率后,因为dpi不会变,这就导致了字体会变大变小。那么怎么实现让字体不变呢(有可能会多少变一点点,因为计算时精确率问题,但总体不影响)。我们通过先获取到手机自带的那个默认分辨率,然后在获取修改后的当前分辨率,算出其缩放率(例如缩放率 = 新的宽度/默认的款的),然后用这个缩放率乘以默认dpi,然后把算出的dpi设置给app,这样就解决问题了。
代码实现:
首先需要一个工具了,这里叫做ScreenHelp.java
- package jp.co.sharp.android.parents.kidsguard.utils;
-
- import android.annotation.SuppressLint;
- import android.content.Context;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.util.DisplayMetrics;
- import android.view.Display;
- import android.view.IWindowManager;
- import android.view.WindowManager;
-
-
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
-
-
- /**
- * Screen Helper Class to get default dpi .
- */
- public class ScreenHelper {
-
- /**
- * Dcm Log Tag .
- */
- private static final String TAG = "ScreenHelper";
-
- /**
- * The defalut LDPI value .
- */
- private static final int LDPI = DisplayMetrics.DENSITY_DEFAULT;
- /**
- * The defalut HDPI value .
- */
- private static final int HDPI = DisplayMetrics.DENSITY_HIGH;
- /**
- * The defalut XHDPI value .
- */
- private static final int XHDPI = DisplayMetrics.DENSITY_XHIGH;
- /**
- * The defalut XXHDPI value .
- */
- private static final int XXHDPI = DisplayMetrics.DENSITY_XXHIGH;
- /**
- * The 560 dpi value .
- */
- private static final int DPI_560 = DisplayMetrics.DENSITY_560;
-
- /**
- * The defalut XXXHDPI value .
- */
- private static final int XXXHDPI = DisplayMetrics.DENSITY_XXXHIGH;
-
- /**
- * The standard 720p device width.
- */
- private static final int DEFALUT_SCREEN_WIDTH = 720;
- /**
- * The standard 1080p device width.
- */
- private static final int MEDIUM_SCREEN_WIDTH = 1080;
- /**
- * The standard 4k device width.
- */
- private static final int HIGH_SCREEN_WIDTH = 1440;
- /**
- * The standard max device width.
- */
- private static final int MAX_SCREEN_WIDTH = 2160;
-
- /**
- * The Max Screen DP to distinguish phone and tablet.
- */
- private static final int MAX_SCREEN_DP = 600;
- /**
- * Default Mode Name .
- */
- private static final String DEFAULT_MODE = "defaultMode";
-
- /**
- * getDefaultDpi.<br>
- * Note : get default dpi when Display Size has changed
- *
- * @param context attach activity context
- * @return default dpi
- */
- public int getDefaultDpi(Context context) {
- WindowManager windowManager = (WindowManager) context
- .getSystemService(Context.WINDOW_SERVICE);
- Display display = windowManager.getDefaultDisplay();
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
-
- // boolean changed = isChangedResolution(display, metrics);
- boolean changed = false;
- // if it is tablet,no need to keep current dpi
- // if (isTablet(context)) {
- // return metrics.densityDpi;
- // } else if (changed) {
- // // get special device dpi
- // return getSpecialDeviceDpi(metrics);
- // } else {
- // return getInitialDisplayDensity(metrics);
- // }
- return getInitialDisplayDensity(metrics);
- }
-
- private int getInitialDisplayDensity(DisplayMetrics metrics) {
- int physicalDensity = metrics.densityDpi;
- boolean isError = false;
- try {
- @SuppressLint("PrivateApi") Class<?> clazz = Class.forName("android.os.ServiceManager");
- try {
- @SuppressLint("DiscouragedPrivateApi") Method method = clazz.getDeclaredMethod("checkService", String.class);
- try {
- IWindowManager mWindowManager = IWindowManager.Stub.asInterface((IBinder) method.invoke(null, Context.WINDOW_SERVICE));
- try {
- if (mWindowManager != null) {
- physicalDensity = mWindowManager.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- isError = true;
- }
- } catch (IllegalAccessException | InvocationTargetException e) {
- e.printStackTrace();
- isError = true;
- }
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- isError = true;
- }
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- isError = true;
- }
- if (isError) {
- physicalDensity = getSpecialDeviceDpi(metrics);
- }
- return physicalDensity;
- }
-
- /**
- * isChangedResolution.<br>
- * Note : check if huawei device
- *
- * @param display view display instance
- * @param displayMetrics view displayMetrics to get some device screen attr
- * @return true is huawei device,false is not
- */
- // private boolean isChangedResolution(Display display, DisplayMetrics displayMetrics) {
- // Display.Mode[] modes = display.getSupportedModes();
- // if (modes.length > 1) {
- // String displayInfo = display.toString();
- //
- // int firstIndex = displayInfo.indexOf(DEFAULT_MODE);
- // int length = DEFAULT_MODE.length();
- // String defaultModeInfo = displayInfo.substring(firstIndex);
- // int firstSymbolIndex = defaultModeInfo.indexOf(",");
- // String defaultMode = defaultModeInfo.substring(length, firstSymbolIndex);
- // int defaultModeIndex = Integer.parseInt(defaultMode.replace(" ", ""));
- // for (Display.Mode mode : modes) {
- // if (mode.getModeId() == defaultModeIndex) {
- // int defaultWidth = mode.getPhysicalWidth();
- // return defaultWidth != displayMetrics.widthPixels;
- // }
- // }
- // }
- //
- // // get the init screen Pixels
- // int physicalWidth = display.getMode().getPhysicalWidth();
- // int physicalHeight = display.getMode().getPhysicalHeight();
- //
- // // get current screen Pixels
- // int currentWidth = displayMetrics.widthPixels;
- // int currentHeight = displayMetrics.heightPixels;
- //
- // //if init size is different with current size, this means resolution
- // //has changed
- // if (physicalWidth != currentWidth && physicalHeight != currentHeight) {
- // return true;
- // }
- //
- // return false;
- // }
-
- /**
- * getSpecialDeviceDpi.<br>
- * Note : when the Resolution could be changed, according to stardand Resolution to
- * get the dpi
- *
- * @param displayMetrics display metrics instances
- * @return standard dpi
- */
- private int getSpecialDeviceDpi(DisplayMetrics displayMetrics) {
- int widthPixels = displayMetrics.widthPixels;
- int heightPixels = displayMetrics.heightPixels;
-
- // get min Pixels.
- int minPixels = Math.min(widthPixels, heightPixels);
-
-
- // get the apposite dpi
- if (minPixels < DEFALUT_SCREEN_WIDTH) {
- return HDPI;
- } else if (minPixels >= DEFALUT_SCREEN_WIDTH && minPixels < MEDIUM_SCREEN_WIDTH) {
- return XHDPI;
- } else if (minPixels >= MEDIUM_SCREEN_WIDTH && minPixels < HIGH_SCREEN_WIDTH) {
- return XXHDPI;
- } else if (minPixels >= HIGH_SCREEN_WIDTH && minPixels < MAX_SCREEN_WIDTH) {
- return DPI_560;
- } else {
- return XXXHDPI;
- }
- }
-
- /**
- * isTablet.<br>
- * Note : check if current device is tablet.
- *
- * @param context attach activity context
- * @return true is tablet, false is not
- */
- // private boolean isTablet(Context context) {
- // return context.getResources().getBoolean(R.bool.isTablet);
- // }
-
-
- /**
- * get default resolution beacause HuaWei can setting multi resolution,
- * this method can get the default when you change to another resolution
- * @param context
- * @return
- */
- public int getDefaultResolutionWidth(Context context) {
- WindowManager windowManager = (WindowManager) context
- .getSystemService(Context.WINDOW_SERVICE);
- Display display = windowManager.getDefaultDisplay();
-
- Display.Mode[] modes = display.getSupportedModes();
-
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
-
- if (modes.length > 1) {
- String displayInfo = display.toString();
-
- int firstIndex = displayInfo.indexOf(DEFAULT_MODE);
- int length = DEFAULT_MODE.length();
- String defaultModeInfo = displayInfo.substring(firstIndex);
- int firstSymbolIndex = defaultModeInfo.indexOf(",");
- String defaultMode = defaultModeInfo.substring(length, firstSymbolIndex);
- int defaultModeIndex = Integer.parseInt(defaultMode.replace(" ", ""));
- for (Display.Mode mode : modes) {
- if (mode.getModeId() == defaultModeIndex) {
- int defaultWidth = mode.getPhysicalWidth();
- return defaultWidth;
- }
- }
- }
-
- // get the init screen Pixels
- int physicalWidth = display.getMode().getPhysicalWidth();
- int physicalHeight = display.getMode().getPhysicalHeight();
-
-
-
- return physicalWidth;
- }
- }
然后在BaseActivity这个基类中重写attachBaseContext方法(其他Activity要继承这个类昂),代码如下:
- @Override
- protected void attachBaseContext(Context newBase) {
- float scale = 0f;
- if (Build.VERSION.SDK_INT > 22) {
-
- Resources res = newBase.getResources();
- Configuration configuration = res.getConfiguration();
-
- ScreenHelper screenHelper = new ScreenHelper();
- int defaultDpi = screenHelper.getDefaultDpi(newBase);
- // configuration.fontScale = 0.2f;
- // configuration.densityDpi = defaultDpi;
-
- int defaultWidth = screenHelper.getDefaultResolutionWidth(newBase);
-
- DisplayMetrics metrics = newBase.getResources().getDisplayMetrics();
- int width = metrics.widthPixels;
- if(defaultWidth !=width){
- scale = new BigDecimal((float)width/defaultWidth).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
- configuration.densityDpi = (int)(defaultDpi * scale);
- }else {
- configuration.densityDpi = defaultDpi;
- }
-
- Context newContext = newBase.createConfigurationContext(configuration);
- super.attachBaseContext(newContext);
- } else {
- super.attachBaseContext(newBase);
- }
-
- }
到此就ok了,运行代码,然后修改【分辨率】和【显示大小】看看效果吧,是不是app中的字体都不会在变动了吧。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。