Android基础UI篇

在Android开发中,由于Android碎片化严重,屏幕分辨率千奇百怪,而想要在各种分辨率的设备上显示基本一致的效果,
适配成本越来越高。虽然Android官方提供了dp单位来适配,但其在各种奇怪分辨率下表现却不尽如人意,因此下面探索
一种简单且低侵入的适配方式。

官方术语:

屏幕密度

屏幕物理区域中的像素量;通常称为 dpi(每英寸 点数)。例如, 与“正常”或“高”密度屏幕相比,“低”密度屏幕在给定物理区域的像素较少。为简便起见,Android 将所有屏幕密度分组为六种通用密度: 低、中、高、超高、超超高和超超超高。

方向

从用户视角看屏幕的方向,即横屏还是 竖屏,分别表示屏幕的纵横比是宽还是高。请注意, 不仅不同的设备默认以不同的方向操作,而且 方向在运行时可随着用户旋转设备而改变。

分辨率

屏幕上物理像素的总数。添加对多种屏幕的支持时, 应用不会直接使用分辨率;而只应关注通用尺寸和密度组指定的屏幕 尺寸及密度。

密度无关像素 (dp)

在定义 UI 布局时应使用的虚拟像素单位,用于以密度无关方式表示布局维度 或位置。

密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是 系统为“中”密度屏幕假设的基线密度。在运行时,系统 根据使用中屏幕的实际密度按需要以透明方式处理 dp 单位的任何缩放 。dp 单位转换为屏幕像素很简单:px = dp * (dpi / 160)。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定义应用的 UI 时应始终使用 dp 单位 ,以确保在不同密度的屏幕上正常显示 UI。

六种通用的

密度:

  • ldpi(低)~120dpi
  • mdpi(中)~160dpi
  • hdpi(高)~240dpi
  • xhdpi(超高)~320dpi
  • xxhdpi(超超高)~480dpi
  • xxxhdpi(超超超高)~640dpi

官方推荐适配策略:

  1. 在 XML 布局文件中指定尺寸时使用 wrap_contentmatch_parentdp 单位 。
  2. 不要在应用代码中使用硬编码的像素值
  3. 不要使用 AbsoluteLayout(已弃用)
  4. 为不同屏幕密度提供替代位图可绘制对象

UI适配策略

1、 今日头条适配方案:
通过修改系统DisplayMetrics中的density的值来实现适配,以下为设计图为宽为360dp的适配代码
摘自今日头条技术博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private static float aNoncompatDensity;
private static float aNoncompatScaledDensity;

private static void setCustomDensity(Activity activity,final Application applicaiton){
final DisplayMetrics appDisplayMetircs = appilcation.getResources.getDisplayMetrics();
if(aNoncompatDensity ==0){
aNoncompatDensity = appDisplayMetircs.density;
aNoncompatScaledDensity = appDisplayMetircs.scaleDensity;
applicaiton.registComponentCallbacks(new ComponentCallbacks(){
@Override
public void onConfigurationChanged(Configuration newConfig){
if(newConfig !=null && newConfig.fontScale >0){
aNoncompatScaledDensity = application.getResources().getDisplayMetrics().scaleDensity;
}
}
@Override
public void onLowMemory(){

}
});
}

final float targetDensity = appDisplayMetircs.widthPixels /360;
final float targetScaledDensity = targetDensity * (aNoncompatScaledDensity/aNoncompatDensity);
final int targetDensityDpi = (int) (160*targetDensity);
appDisplayMetircs.density = targetDensity;
appDisplayMetircs.scaledDensity = targetScaledDensity;
appDisplayMetircs.densityDpi = targetDensityDpi;

final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}

2、 使用最小宽度限定符 如sw-360dp的方式去实现适配。

Android UI优化建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1.1布局优化
a>: 避免复杂的View层级。布局越复杂就越臃肿,就越容易出现性能问题,寻找最节省资源的方式去展示嵌套的内容;
b>: 尽量避免在视图层级的顶层使用相对布局 RelativeLayout 。相对布局 RelativeLayout 比较耗资源,因为一个相对布局 RelativeLayout 需要两次度量来确保自己处理了所有的布局关系,而且这个问题会伴随着视图层级中的相对布局 RelativeLayout 的增多,而变得更严重;
c>: 布局层级一样的情况建议使用线性布局 LinearLayout 代替相对布局 RelativeLayout*,因为线性布局 LinearLayout 性能要更高一些;确实需要对分支进行相对布局 RelativeLayout 的时候,可以考虑更优化的网格布局 GridLayout ,它已经预处理了分支视图的关系,可以避免两次度量的问题;
d>:相对复杂的布局建议采用相对布局 RelativeLayout ,相对布局 RelativeLayout 可以简单实现线性布局 LinearLayout 嵌套才能实现的布局;
e>:不要使用绝对布局 AbsoluteLayout ;
f>:将可重复使用的组件抽取出来并用 </include> 标签进行重用。如果应用多个地方的 UI 用到某个布局,就将其写成一个布局部件,便于各个 UI 重用。官方详解 「 戳我 」
g>:使用 merge 标签减少布局的嵌套层次,官方详解 「 戳我 」;
h>:去掉多余的不可见背景。有多层背景颜色的布局,只留最上层的对用户可见的颜色即可,其他用户不可见的底层颜色可以去掉,减少无效的绘制操作;
i>:尽量避免使用 layout_weight 属性。使用包含 layout_weight 属性的线性布局 LinearLayout 每一个子组件都需要被测量两次,会消耗过多的系统资源。在使用 ListView 标签与 GridView 标签的时候,这个问题显的尤其重要,因为子组件会重复被创建。平分布局可以使用相对布局 RelativeLayout 里一个 0dp 的 view 做分割线来搞定,如果不行,那就……;
j>:合理的界面的布局结构应是宽而浅,而不是窄而深;

1.2 优化逻辑处理
a>:按需载入视图。某些不怎么重用的耗资源视图,可以等到需要的时候再加载,提高UI渲染速度;
b>:使用 ViewStub 标签来加载一些不常用的布局;
c>:动态地 inflation view 性能要比用 ViewStub 标签的 setVisiblity 性能要好,当然某些功能的实现采用 ViewStub 标签更合适;
d>:尽量避免不必要的耗资源操作,节省宝贵的运算时间;
e>:避免在 UI 线程进行繁重的操作。耗资源的操作(比如 IO 操作、网络操作、SQL 操作、列表刷新等)耗资源的操作应用后台进程去实现,不能占用 UI 线程,UI 线程是主线程,主线程是保持程序流畅的关键,应该只操作那些核心的 UI 操作,比如处理视图的属性和绘制;
f>:最小化唤醒机制。我们常用广播来接收那些期望响应的消息和事件,但过多的响应超过本身需求的话,会消耗多余的 Android 设备性能和资源。所以应该最小化唤醒机制,当应用不关心这些消失和事件时,就关闭广播,并慎重选择那些要响应的 Intent 。
h>:为低端设备考虑,比如 512M 内存、双核 CPU 、低分辨率,确保你的应用可以满足不同水平的设备。
i>:优化应用的启动速度。当应用启动一个应用时,界面的尽快反馈显示可以给用户一个良好的体验。为了启动更快,可以延迟加载一些 UI 以及避免在应用 Application 层级初始化代码。

1.3 优化工具

另外有些强大但可能少用的工具在测试性能渲染时辅助分析,比如:
1):HierarchyViewer:这个工具常用来查看界面的视图结构是否过于复杂,用于了解哪些视图过度绘制,又该如何进行改进;
2):Tracer for OpenGL:这个工具收集了所有UI界面发给GPU的绘制命令。常用于辅助开发人员 DEBUG 、定位一些 HierarchyViewer 工具定位不了的疑难渲染细节问题。