Android屏幕相关

[TOC]

基础概念

  • 屏幕大小
    • 对角线长度,如5.5寸手机
  • 分辨率
    • 手机屏幕的像素点个数
  • PPI
    • 每英寸像素Pixels Per Inch, 又被称为Dots Per Inch,由对角线的像素点除以屏幕的大小得到

度量单位

  • dp、dip
    • 设备独立像素
  • px、pixels
    • 像素,不同设备显示效果相同
  • pt、point
    • 标准长度单位
  • sp:scaled pixels
    • 放大像素,主要用于字体显示
  • in
    • 英寸
  • mm

转换

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
/**
* This method converts dp unit to equivalent pixels, depending on device density.
*
* @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
* @param context Context to get resources and device specific display metrics
* @return A float value to represent px equivalent to dp depending on device density
*/
public static float convertDpToPixel(float dp, Context context){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float px = dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
return px;
}

/**
* This method converts device specific pixels to density independent pixels.
*
* @param px A value in px (pixels) unit. Which we need to convert into db
* @param context Context to get resources and device specific display metrics
* @return A float value to represent dp equivalent to px value
*/
public static float convertPixelsToDp(float px, Context context){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float dp = px / ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
return dp;
}

Android渲染机制

Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染

渲染

UI卡顿解决方法:

  1. 通过Hierarchy Viewer去检测渲染效率,去除不必要的嵌套
  2. 通过show GPU Overdraw去检测Overdraw
    1. getWindow().setBackgroundDrawable(null)
    2. canvas.clipRect
    3. 移除不必要背景
    4. 减少没有必要的layouts、invalidations、overdraw

鸿洋的博客

Activity的组成

  • Activity
    • 基本的页面单元,Activity包含一个Window,window上可以绘制各种view
  • Window
    • 表示顶层窗口,管理界面的显示和事件的相应;每个Activity均会创建一个
    • 当前只有PhoneWindow作为唯一的实现类
    • 独占一个Surface实例的显示区域,每个窗口的Surface由WindowManagerService分配。可以把Surface看做一个画布,应用通过Canvas或OpenGL在上面作画
    • Activity调用attach,创建了Window
  • PhoneWindow
    • Activity和整个View系统交互的接口
    • phoneWindow是把一个FrameLayout进行了一定的包装,并提供了一组通用的窗口操作接口
  • DecorView
    • 是PhoneWindow中的一个内部类
  • ViewRoot
    • 每个DecorView都有一个与之关联的ViewRoot对象,在ActivityThread.handleResumeActivity()方法中建立了它们两者的关联关系。
    • 负责绘制
    • 调用performTraversals进行绘制
  • View
    • 最基本的UI组件,表示屏幕上的一个矩形区域

Activity Constructure

这里面所有View的监听事件,都通过WindowManagerService来进行接收,并通过Activity对象来回调对应的onClickListener

如果调用requestWindowFeature(Window.FEATURE_NO_TITLE),视图树的布局中就仅有Content了,但是requestWindowFeature必须在setContentView之前才能生效

在onCreate()中调用setContentView()后,ActivityManagerService会回调onResume(),此时系统才会把整个DecorView添加到PhoneWindow上,最终完成界面的绘制。(不调用setContentView也会调用onResume)

绘制流程

整个View树的绘图流程是在ViewRoot.java的performTraversals()函数展开的,该函数做的执行过程可简单概括为根据之前设置的状态:

  • 判断是否需要重新计算视图大小(measure)
  • 判断是否需要安置视图的位置(layout)
  • 是否需要重绘(draw)

Android View的绘制流程

measure

为整个View树计算实际的大小,即设置实际的高(mMeasuredHeight和mMeasureWidth),每个View控件的实际宽高都是由父视图和本身视图决定的

Measure

layout

根据子视图的大小以及布局参数将View树放到合适的位置上

onlayout在View的位置发生改变后调用,内容是对子View重新布局

View#onLayout

所以View的onLayout方法默认为空

ViewGroup#onLayout

调用子View的layout

draw

ViewRoot对象的performTraversals()方法调用draw()方法发起绘制该View树,判断DRAWN位,看是否需要重绘

View#draw
  1. 绘制背景
  2. 通过onDraw绘制自身内容
  3. 通过dispatchDraw()绘制子View
  4. 绘制滚动条
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void draw(Canvas canvas) {
. . .
// 绘制背景,只有dirtyOpaque为false时才进行绘制,下同
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}

. . .

// 绘制自身内容
if (!dirtyOpaque) onDraw(canvas);

// 绘制子View
dispatchDraw(canvas);

. . .
// 绘制滚动条等
onDrawForeground(canvas);

}

requestLayout()

调用measure()和layout()

具体细节推荐此文

0%