当前位置:   article > 正文

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

android 修改手机显示大小 布局问题

在开发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

  1. package jp.co.sharp.android.parents.kidsguard.utils;
  2. import android.annotation.SuppressLint;
  3. import android.content.Context;
  4. import android.os.IBinder;
  5. import android.os.RemoteException;
  6. import android.util.DisplayMetrics;
  7. import android.view.Display;
  8. import android.view.IWindowManager;
  9. import android.view.WindowManager;
  10. import java.lang.reflect.InvocationTargetException;
  11. import java.lang.reflect.Method;
  12. /**
  13. * Screen Helper Class to get default dpi .
  14. */
  15. public class ScreenHelper {
  16. /**
  17. * Dcm Log Tag .
  18. */
  19. private static final String TAG = "ScreenHelper";
  20. /**
  21. * The defalut LDPI value .
  22. */
  23. private static final int LDPI = DisplayMetrics.DENSITY_DEFAULT;
  24. /**
  25. * The defalut HDPI value .
  26. */
  27. private static final int HDPI = DisplayMetrics.DENSITY_HIGH;
  28. /**
  29. * The defalut XHDPI value .
  30. */
  31. private static final int XHDPI = DisplayMetrics.DENSITY_XHIGH;
  32. /**
  33. * The defalut XXHDPI value .
  34. */
  35. private static final int XXHDPI = DisplayMetrics.DENSITY_XXHIGH;
  36. /**
  37. * The 560 dpi value .
  38. */
  39. private static final int DPI_560 = DisplayMetrics.DENSITY_560;
  40. /**
  41. * The defalut XXXHDPI value .
  42. */
  43. private static final int XXXHDPI = DisplayMetrics.DENSITY_XXXHIGH;
  44. /**
  45. * The standard 720p device width.
  46. */
  47. private static final int DEFALUT_SCREEN_WIDTH = 720;
  48. /**
  49. * The standard 1080p device width.
  50. */
  51. private static final int MEDIUM_SCREEN_WIDTH = 1080;
  52. /**
  53. * The standard 4k device width.
  54. */
  55. private static final int HIGH_SCREEN_WIDTH = 1440;
  56. /**
  57. * The standard max device width.
  58. */
  59. private static final int MAX_SCREEN_WIDTH = 2160;
  60. /**
  61. * The Max Screen DP to distinguish phone and tablet.
  62. */
  63. private static final int MAX_SCREEN_DP = 600;
  64. /**
  65. * Default Mode Name .
  66. */
  67. private static final String DEFAULT_MODE = "defaultMode";
  68. /**
  69. * getDefaultDpi.<br>
  70. * Note : get default dpi when Display Size has changed
  71. *
  72. * @param context attach activity context
  73. * @return default dpi
  74. */
  75. public int getDefaultDpi(Context context) {
  76. WindowManager windowManager = (WindowManager) context
  77. .getSystemService(Context.WINDOW_SERVICE);
  78. Display display = windowManager.getDefaultDisplay();
  79. DisplayMetrics metrics = new DisplayMetrics();
  80. display.getRealMetrics(metrics);
  81. // boolean changed = isChangedResolution(display, metrics);
  82. boolean changed = false;
  83. // if it is tablet,no need to keep current dpi
  84. // if (isTablet(context)) {
  85. // return metrics.densityDpi;
  86. // } else if (changed) {
  87. // // get special device dpi
  88. // return getSpecialDeviceDpi(metrics);
  89. // } else {
  90. // return getInitialDisplayDensity(metrics);
  91. // }
  92. return getInitialDisplayDensity(metrics);
  93. }
  94. private int getInitialDisplayDensity(DisplayMetrics metrics) {
  95. int physicalDensity = metrics.densityDpi;
  96. boolean isError = false;
  97. try {
  98. @SuppressLint("PrivateApi") Class<?> clazz = Class.forName("android.os.ServiceManager");
  99. try {
  100. @SuppressLint("DiscouragedPrivateApi") Method method = clazz.getDeclaredMethod("checkService", String.class);
  101. try {
  102. IWindowManager mWindowManager = IWindowManager.Stub.asInterface((IBinder) method.invoke(null, Context.WINDOW_SERVICE));
  103. try {
  104. if (mWindowManager != null) {
  105. physicalDensity = mWindowManager.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
  106. }
  107. } catch (RemoteException e) {
  108. e.printStackTrace();
  109. isError = true;
  110. }
  111. } catch (IllegalAccessException | InvocationTargetException e) {
  112. e.printStackTrace();
  113. isError = true;
  114. }
  115. } catch (NoSuchMethodException e) {
  116. e.printStackTrace();
  117. isError = true;
  118. }
  119. } catch (ClassNotFoundException e) {
  120. e.printStackTrace();
  121. isError = true;
  122. }
  123. if (isError) {
  124. physicalDensity = getSpecialDeviceDpi(metrics);
  125. }
  126. return physicalDensity;
  127. }
  128. /**
  129. * isChangedResolution.<br>
  130. * Note : check if huawei device
  131. *
  132. * @param display view display instance
  133. * @param displayMetrics view displayMetrics to get some device screen attr
  134. * @return true is huawei device,false is not
  135. */
  136. // private boolean isChangedResolution(Display display, DisplayMetrics displayMetrics) {
  137. // Display.Mode[] modes = display.getSupportedModes();
  138. // if (modes.length > 1) {
  139. // String displayInfo = display.toString();
  140. //
  141. // int firstIndex = displayInfo.indexOf(DEFAULT_MODE);
  142. // int length = DEFAULT_MODE.length();
  143. // String defaultModeInfo = displayInfo.substring(firstIndex);
  144. // int firstSymbolIndex = defaultModeInfo.indexOf(",");
  145. // String defaultMode = defaultModeInfo.substring(length, firstSymbolIndex);
  146. // int defaultModeIndex = Integer.parseInt(defaultMode.replace(" ", ""));
  147. // for (Display.Mode mode : modes) {
  148. // if (mode.getModeId() == defaultModeIndex) {
  149. // int defaultWidth = mode.getPhysicalWidth();
  150. // return defaultWidth != displayMetrics.widthPixels;
  151. // }
  152. // }
  153. // }
  154. //
  155. // // get the init screen Pixels
  156. // int physicalWidth = display.getMode().getPhysicalWidth();
  157. // int physicalHeight = display.getMode().getPhysicalHeight();
  158. //
  159. // // get current screen Pixels
  160. // int currentWidth = displayMetrics.widthPixels;
  161. // int currentHeight = displayMetrics.heightPixels;
  162. //
  163. // //if init size is different with current size, this means resolution
  164. // //has changed
  165. // if (physicalWidth != currentWidth && physicalHeight != currentHeight) {
  166. // return true;
  167. // }
  168. //
  169. // return false;
  170. // }
  171. /**
  172. * getSpecialDeviceDpi.<br>
  173. * Note : when the Resolution could be changed, according to stardand Resolution to
  174. * get the dpi
  175. *
  176. * @param displayMetrics display metrics instances
  177. * @return standard dpi
  178. */
  179. private int getSpecialDeviceDpi(DisplayMetrics displayMetrics) {
  180. int widthPixels = displayMetrics.widthPixels;
  181. int heightPixels = displayMetrics.heightPixels;
  182. // get min Pixels.
  183. int minPixels = Math.min(widthPixels, heightPixels);
  184. // get the apposite dpi
  185. if (minPixels < DEFALUT_SCREEN_WIDTH) {
  186. return HDPI;
  187. } else if (minPixels >= DEFALUT_SCREEN_WIDTH && minPixels < MEDIUM_SCREEN_WIDTH) {
  188. return XHDPI;
  189. } else if (minPixels >= MEDIUM_SCREEN_WIDTH && minPixels < HIGH_SCREEN_WIDTH) {
  190. return XXHDPI;
  191. } else if (minPixels >= HIGH_SCREEN_WIDTH && minPixels < MAX_SCREEN_WIDTH) {
  192. return DPI_560;
  193. } else {
  194. return XXXHDPI;
  195. }
  196. }
  197. /**
  198. * isTablet.<br>
  199. * Note : check if current device is tablet.
  200. *
  201. * @param context attach activity context
  202. * @return true is tablet, false is not
  203. */
  204. // private boolean isTablet(Context context) {
  205. // return context.getResources().getBoolean(R.bool.isTablet);
  206. // }
  207. /**
  208. * get default resolution beacause HuaWei can setting multi resolution,
  209. * this method can get the default when you change to another resolution
  210. * @param context
  211. * @return
  212. */
  213. public int getDefaultResolutionWidth(Context context) {
  214. WindowManager windowManager = (WindowManager) context
  215. .getSystemService(Context.WINDOW_SERVICE);
  216. Display display = windowManager.getDefaultDisplay();
  217. Display.Mode[] modes = display.getSupportedModes();
  218. DisplayMetrics metrics = new DisplayMetrics();
  219. display.getRealMetrics(metrics);
  220. if (modes.length > 1) {
  221. String displayInfo = display.toString();
  222. int firstIndex = displayInfo.indexOf(DEFAULT_MODE);
  223. int length = DEFAULT_MODE.length();
  224. String defaultModeInfo = displayInfo.substring(firstIndex);
  225. int firstSymbolIndex = defaultModeInfo.indexOf(",");
  226. String defaultMode = defaultModeInfo.substring(length, firstSymbolIndex);
  227. int defaultModeIndex = Integer.parseInt(defaultMode.replace(" ", ""));
  228. for (Display.Mode mode : modes) {
  229. if (mode.getModeId() == defaultModeIndex) {
  230. int defaultWidth = mode.getPhysicalWidth();
  231. return defaultWidth;
  232. }
  233. }
  234. }
  235. // get the init screen Pixels
  236. int physicalWidth = display.getMode().getPhysicalWidth();
  237. int physicalHeight = display.getMode().getPhysicalHeight();
  238. return physicalWidth;
  239. }
  240. }

然后在BaseActivity这个基类中重写attachBaseContext方法(其他Activity要继承这个类昂),代码如下:

  1. @Override
  2. protected void attachBaseContext(Context newBase) {
  3. float scale = 0f;
  4. if (Build.VERSION.SDK_INT > 22) {
  5. Resources res = newBase.getResources();
  6. Configuration configuration = res.getConfiguration();
  7. ScreenHelper screenHelper = new ScreenHelper();
  8. int defaultDpi = screenHelper.getDefaultDpi(newBase);
  9. // configuration.fontScale = 0.2f;
  10. // configuration.densityDpi = defaultDpi;
  11. int defaultWidth = screenHelper.getDefaultResolutionWidth(newBase);
  12. DisplayMetrics metrics = newBase.getResources().getDisplayMetrics();
  13. int width = metrics.widthPixels;
  14. if(defaultWidth !=width){
  15. scale = new BigDecimal((float)width/defaultWidth).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
  16. configuration.densityDpi = (int)(defaultDpi * scale);
  17. }else {
  18. configuration.densityDpi = defaultDpi;
  19. }
  20. Context newContext = newBase.createConfigurationContext(configuration);
  21. super.attachBaseContext(newContext);
  22. } else {
  23. super.attachBaseContext(newBase);
  24. }
  25. }

到此就ok了,运行代码,然后修改【分辨率】和【显示大小】看看效果吧,是不是app中的字体都不会在变动了吧。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/836978
推荐阅读
相关标签
  

闽ICP备14008679号