【Android】通过控制dpi来实现修改分辨率和显示大小app布局错乱问题 您所在的位置:网站首页 lv麻将包图片内标是什么样的 【Android】通过控制dpi来实现修改分辨率和显示大小app布局错乱问题

【Android】通过控制dpi来实现修改分辨率和显示大小app布局错乱问题

2023-06-13 16:11| 来源: 网络整理| 查看: 265

在开发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. * 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. * 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. * 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. * 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 2018-2019 实验室设备网 版权所有