一.Android多机型适配:
多机型适配在Android开发中经常会碰到,那么如果我们只有一部测试机,如何能做到用这一部手机来看各种不同分辨率手机的UI显示呢!不需要桌前摆一排手机来一个一个的是看。
二.WM命令:
1.什么是WM命令:
说了这么多前提,实际上需要使用的就是一行 wm 命令,可以通过 adb shell 来操作它。
wm 命令主要用来帮助我们修改一些屏幕的参数,先来看看它的文档。使用 adb shell wm 命令,可以方便的查看 wm 的文档描述
可以看到,wm 命令可以帮助我们修改当前设备的 size 、 density 等参数,而这两个参数就是决定了当前设备的显示效果。
2.2 WM命令
上面的文档中有很多命令,其实我们平时用到的一般只有size,density这两个,以下例子为一部1080x1920的设备:
1. wm size命令
wm size 命令,可以用来查询和修改当前设备的尺寸,如果想要修改尺寸,可以使用 px 或者 dp 为单位(默认为 px),使用 x 进行连接,这个其实看看 wm size 的输出,你就清楚了。
上面的例子中,调用了不同的命令:
1.使用 wm size 命令查看当前设备的尺寸。
2.使用 wm size 命令将当前尺寸修改成了 1000x2000。
3.再次使用 wm size 命令查看当前设备的尺寸。
4.最后使用 wm size reset 命令,将屏幕尺寸还原。
5.再用 wm size 命令,查看还原后的尺寸。
这几个步骤就已经涵盖了 wm size 命令的所有使用步骤,可以使用它查看当前屏幕尺寸,并且修改屏幕尺寸和还原它。
2.wm density命令
wm density 命令,可以用来查看和修改当前设备的 density 参数。
使用起来跟wm size相同,我们直接上命令吧。
在这个例子中,我做了几件事情:
1.使用 wm density 查看当前设备的 density。
2.在使用 wm density 命令,修改当前设备的 density 为 560,580。
3.然后再使用 wm density 查看修改后的参数。
4.最后使用 wm density reset 将设备的 density 还原。
5.最后再查看一下还原后的值。
3.wm overscan命令
wm overscan 命令,可以修改当前设备的屏幕内边距。这个命令一般用不到。
在这个例子中,做了两件事:
1.使用 wm overscan 修改边距为 30 。
2.然后使用 wm overscan reset 将其还原。
WM命令源码:
package com.android.commands.wm;
20
21import android.content.Context;
22import android.graphics.Point;
23import android.graphics.Rect;
24import android.os.RemoteException;
25import android.os.ServiceManager;
26import android.os.UserHandle;
27import android.util.AndroidException;
28import android.util.DisplayMetrics;
29import android.view.Display;
30import android.view.IWindowManager;
31import com.android.internal.os.BaseCommand;
32
33import java.io.PrintStream;
34import java.util.regex.Matcher;
35import java.util.regex.Pattern;
36
37public class Wm extends BaseCommand {
38
39 private IWindowManager mWm;
40
41 /**
42 * Command-line entry point.
43 *
44 * @param args The command-line arguments
45 */
46 public static void main(String[] args) {
47 (new Wm()).run(args);
48 }
49
50 @Override
51 public void onShowUsage(PrintStream out) {
52 out.println(
53 "usage: wm [subcommand] [options]\n" +
54 " wm size [reset|WxH|WdpxHdp]\n" +
55 " wm density [reset|DENSITY]\n" +
56 " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
57 " wm scaling [off|auto]\n" +
58 " wm screen-capture [userId] [true|false]\n" +
59 "\n" +
60 "wm size: return or override display size.\n" +
61 " width and height in pixels unless suffixed with 'dp'.\n" +
62 "\n" +
63 "wm density: override display density.\n" +
64 "\n" +
65 "wm overscan: set overscan area for display.\n" +
66 "\n" +
67 "wm scaling: set display scaling mode.\n" +
68 "\n" +
69 "wm screen-capture: enable/disable screen capture.\n" +
70 "\n" +
71 "wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " +
72 "necessary.\n"
73 );
74 }
75
76 @Override
77 public void onRun() throws Exception {
78 mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
79 Context.WINDOW_SERVICE));
80 if (mWm == null) {
81 System.err.println(NO_SYSTEM_ERROR_CODE);
82 throw new AndroidException("Can't connect to window manager; is the system running?");
83 }
84
85 String op = nextArgRequired();
86
87 if (op.equals("size")) {
88 runDisplaySize();
89 } else if (op.equals("density")) {
90 runDisplayDensity();
91 } else if (op.equals("overscan")) {
92 runDisplayOverscan();
93 } else if (op.equals("scaling")) {
94 runDisplayScaling();
95 } else if (op.equals("screen-capture")) {
96 runSetScreenCapture();
97 } else if (op.equals("dismiss-keyguard")) {
98 runDismissKeyguard();
99 } else {
100 showError("Error: unknown command '" + op + "'");
101 return;
102 }
103 }
104
105 private void runSetScreenCapture() throws Exception {
106 String userIdStr = nextArg();
107 String enableStr = nextArg();
108 int userId;
109 boolean disable;
110
111 try {
112 userId = Integer.parseInt(userIdStr);
113 } catch (NumberFormatException e) {
114 System.err.println("Error: bad number " + e);
115 return;
116 }
117
118 disable = !Boolean.parseBoolean(enableStr);
119
120 try {
121 mWm.setScreenCaptureDisabled(userId, disable);
122 } catch (RemoteException e) {
123 System.err.println("Error: Can't set screen capture " + e);
124 }
125 }
126
127 private void runDisplaySize() throws Exception {
128 String size = nextArg();
129 int w, h;
130 if (size == null) {
131 Point initialSize = new Point();
132 Point baseSize = new Point();
133 try {
134 mWm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
135 mWm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
136 System.out.println("Physical size: " + initialSize.x + "x" + initialSize.y);
137 if (!initialSize.equals(baseSize)) {
138 System.out.println("Override size: " + baseSize.x + "x" + baseSize.y);
139 }
140 } catch (RemoteException e) {
141 }
142 return;
143 } else if ("reset".equals(size)) {
144 w = h = -1;
145 } else {
146 int div = size.indexOf('x');
147 if (div <= 0 || div >= (size.length()-1)) {
148 System.err.println("Error: bad size " + size);
149 return;
150 }
151 String wstr = size.substring(0, div);
152 String hstr = size.substring(div+1);
153 try {
154 w = parseDimension(wstr);
155 h = parseDimension(hstr);
156 } catch (NumberFormatException e) {
157 System.err.println("Error: bad number " + e);
158 return;
159 }
160 }
161
162 try {
163 if (w >= 0 && h >= 0) {
164 // TODO(multidisplay): For now Configuration only applies to main screen.
165 mWm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
166 } else {
167 mWm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
168 }
169 } catch (RemoteException e) {
170 }
171 }
172
173 private void runDisplayDensity() throws Exception {
174 String densityStr = nextArg();
175 int density;
176 if (densityStr == null) {
177 try {
178 int initialDensity = mWm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
179 int baseDensity = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
180 System.out.println("Physical density: " + initialDensity);
181 if (initialDensity != baseDensity) {
182 System.out.println("Override density: " + baseDensity);
183 }
184 } catch (RemoteException e) {
185 }
186 return;
187 } else if ("reset".equals(densityStr)) {
188 density = -1;
189 } else {
190 try {
191 density = Integer.parseInt(densityStr);
192 } catch (NumberFormatException e) {
193 System.err.println("Error: bad number " + e);
194 return;
195 }
196 if (density < 72) {
197 System.err.println("Error: density must be >= 72");
198 return;
199 }
200 }
201
202 try {
203 if (density > 0) {
204 // TODO(multidisplay): For now Configuration only applies to main screen.
205 mWm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
206 UserHandle.USER_CURRENT);
207 } else {
208 mWm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
209 UserHandle.USER_CURRENT);
210 }
211 } catch (RemoteException e) {
212 }
213 }
214
215 private void runDisplayOverscan() throws Exception {
216 String overscanStr = nextArgRequired();
217 Rect rect = new Rect();
218 if ("reset".equals(overscanStr)) {
219 rect.set(0, 0, 0, 0);
220 } else {
221 final Pattern FLATTENED_PATTERN = Pattern.compile(
222 "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
223 Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
224 if (!matcher.matches()) {
225 System.err.println("Error: bad rectangle arg: " + overscanStr);
226 return;
227 }
228 rect.left = Integer.parseInt(matcher.group(1));
229 rect.top = Integer.parseInt(matcher.group(2));
230 rect.right = Integer.parseInt(matcher.group(3));
231 rect.bottom = Integer.parseInt(matcher.group(4));
232 }
233
234 try {
235 mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom);
236 } catch (RemoteException e) {
237 }
238 }
239
240 private void runDisplayScaling() throws Exception {
241 String scalingStr = nextArgRequired();
242 if ("auto".equals(scalingStr)) {
243 mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
244 } else if ("off".equals(scalingStr)) {
245 mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
246 } else {
247 System.err.println("Error: scaling must be 'auto' or 'off'");
248 }
249 }
250
251 private void runDismissKeyguard() throws Exception {
252 mWm.dismissKeyguard();
253 }
254
255 private int parseDimension(String s) throws NumberFormatException {
256 if (s.endsWith("px")) {
257 return Integer.parseInt(s.substring(0, s.length() - 2));
258 }
259 if (s.endsWith("dp")) {
260 int density;
261 try {
262 density = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
263 } catch (RemoteException e) {
264 density = DisplayMetrics.DENSITY_DEFAULT;
265 }
266 return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
267 DisplayMetrics.DENSITY_DEFAULT;
268 }
269 return Integer.parseInt(s);
270 }
271}
根据上述源码以wm size 为例,解析下源码:
1.wm 命令的逻辑非常的简单,它实际上是和 WindowManagerService 进行交互,通过 WMS 来修改屏幕相关的一些参数。
这里就不介绍完整的 wm 源码了,就以一个方法举例来看看 wm 的逻辑。这里就以 wm size 命令举例好了。
首先,看看 wm 命令的入口onRun()。
2.这里会初始化 mWm ,并且根据输入的参数,调用不同的方法,如果输入的是 size ,则会调用 runDisplaySize() 方法。
3.在 runDisplaySize() 方法中,会先判断 size 命令后面有没有跟其它参数,如果没有,则认为是一次查询操作,会将查询到的结果输出。
而如果是 reset 方法的话,就将 w 、h 这两个代表宽高的值,设置为 -1,最终会通过 mWm.clearForcedDisplaySize() 方法,将屏幕的 size 还原。
否者就从输入的参数中,通过 x 进行分割,拿到我们输入的宽高。最终通过 mWm.setForcedDisplaySize() 方法,将当前屏幕的 size 修改成我们输入的结果。
在设置size 的时候,还会通过 parseDimens() 方法,重新计算一下输入的尺寸。
4.从这里可以看出,它支持两个尺寸,px 和 dp ,如果如果不指定尺寸,则默认认为是 px 。
到这里就完成了一次通过 wm size 命令,修改设备屏幕参数的全过程
操作注意事项:
1.手机连接电脑并打开UBS调试;
2.运行自己的app;
3.打开电脑命令行进行上述命令设置,即可看到手机不同分辨率。
常用到的手机分辨率及尺寸,仅供参考:
手机型号 density size
一加5 420 1080x1920
红米NOTE. 320 720x1280
华为MATE7. 480 1080x1812
MOTO-G5 480 1080x1920
魅族MX3 440 1080x1800
Nexus5x 420 1080x1794
Nexus6p 560 1440x2392
奇酷360 460 1080x1920
VivoX5 320 720x1280