一.View绘制流程:
Android View 绘制流程是指从 ViewRootImpl 开始,经过测量、布局和绘制等多个阶段,最终将真正的绘制内容显示在屏幕上的过程。在面试中,常常会涉及这个知识点,请看下面的问题和答案。
问题1:请简述 Android View 绘制流程的大致过程。
答案1:Android View 绘制流程的大致过程如下:
在 ViewRootImpl 中接收到 VSYNC 信号后触发 performTraversals() 方法。
performTraversals() 方法会依次调用 measure()、layout()、draw() 等方法进行测量、布局和绘制操作。
measure() 方法会调用 onMeasure() 方法,通过 measureChild() 等方法对子 View 进行重新测量,得到它们的大小和位置等信息。
layout() 方法会调用 onLayout() 方法,通过 layoutChild() 等方法对子 View 进行重新布局,得到它们的实际位置。
draw() 方法会调用 onDraw() 方法,将绘制操作交给子 View 进行实际绘制。
最终,系统通过硬件加速、双缓冲等机制将绘制结果显示到屏幕上,完成整个绘制流程。
问题2:请介绍一下如何提高 Android View 绘制的性能?
答案2:提高 Android View 绘制的性能,可以从以下几个方面入手:
硬件加速:使用硬件加速可以加速大量绘制操作,例如使用 Canvas.drawBitmap() 代替 BitmapFactory.decodeResource(),并且避免过度绘制、避免使用大量的图层等。
充分利用缓存:可以使用 View 缓存来避免不必要的测量、布局和绘制操作,例如使用 View.setDrawingCacheEnabled() 方法开启 View 缓存,并及时清理不必要的缓存数据等。
使用优化的绘制代码:尽量避免使用透明度、深度等复杂效果,减少绘制 View 数量和复杂度,避免使用较大的 Bitmap 等。
异步操作:使用异步操作可以将绘制等耗时操作放入后台进行,例如可以使用 Handler.post() 方法或 AsyncTask 等。
优化布局:合理使用 View、ViewGroup、Layout 嵌套等,避免出现过于复杂的布局结构,优化布局可以减少测量和布局的耗时,进而提高绘制性能。
二. View 绘制流程发生在Activity哪个生命周期之后
View 绘制流程发生在 Activity 的生命周期中的 onResume() 方法之后。在 Activity 的 onResume() 方法中,系统会触发对 View 树的测量、布局和绘制操作,最终将内容显示在屏幕上。可以说,Activity 的生命周期是整个 View 绘制流程的触发点和控制点,从 onResume() 开始,到 onPause() 结束,整个过程是一个不断刷新的循环。因此,在开发过程中,我们需要考虑如何合理利用 Activity 的生命周期,以提高 View 绘制的性能和稳定性。
三.Android 获取View宽高方式
获取控件的宽高是 Android 开发中经常会用到的操作,通常需要在代码中获取控件的宽高进行相关计算或设置操作。可以使用以下方式获取控件的宽高:
- 在 View 的 onLayout() 方法中获取宽高
在 View 的 onLayout() 方法中,View 的大小和位置已经计算完成,因此可以通过 View.getWidth() 和 View.getHeight() 方法获取控件的宽高。onLayout() 方法会在 View 布局过程中被调用,因此如果需要获取控件宽高,可以在 onLayout() 方法中执行。
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int width = getWidth();
int height = getHeight();
// 在这里可以获取控件的宽高进行相关计算或设置操作
}
- 在 View 的 onSizeChanged() 方法中获取宽高
在 View 的 onSizeChanged() 方法中,View 的大小已经确定,因此可以通过 View.getWidth() 和 View.getHeight() 方法获取控件的宽高。onSizeChanged() 方法会在 View 大小发生变化时被调用,因此如果需要获取控件宽高,可以在 onSizeChanged() 方法中执行。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int width = getWidth();
int height = getHeight();
// 在这里可以获取控件的宽高进行相关计算或设置操作
}
- 使用 ViewTreeObserver 监听 View 的宽高变化
ViewTreeObserver 是一个用于监听 View 树变化的类,通过添加 OnGlobalLayoutListener 监听器,可以在 View 的大小发生变化时获取它的宽高。这种方式通常适用于需要动态获取 View 大小的情况下。
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
int width = view.getWidth();
int height = view.getHeight();
// 在这里可以获取控件的宽高进行相关计算或设置操作
}
});
需要注意的是,在获取控件的宽高时,应该尽量避免在 View 的构造方法中使用,因为此时控件的宽高还没有得到计算和确定。相反,应该在 View 的 onLayout()、onSizeChanged() 以及 ViewTreeObserver 中进行获取操作。
四.viewRootImpl的常见面试问题
-
什么是viewRootImpl? 答:viewRootImpl是Android中的一个类,用于管理应用程序UI布局中的根视图和其他视图。
-
它的作用是什么? 答:viewRootImpl管理着整个视图树的绘制和交互等操作,是与用户界面操作相关的核心类之一。
-
viewRootImpl是如何创建的? 答:通常由Android系统在应用程序启动时自动创建和管理。
-
viewRootImpl与Activity的关系是什么? 答:一个Activity对应一个viewRootImpl实例。
-
viewRootImpl与布局文件的关系是什么? 答:在应用程序布局文件中定义的视图会构成一棵视图树,其中的根节点就是viewRootImpl。
-
viewRootImpl的绘制流程是怎样的? 答:View的绘制流程首先从ViewRootImpl的performTraversals()方法开始,该方法会递归调用View对象及其子View的draw()方法来进行绘制。在绘制过程中,ViewRootImpl会负责将UI事件分发给对应的View对象。
-
viewRootImpl会影响应用程序的性能吗? 答:是的,在某些情况下,viewRootImpl的性能消耗会对应用程序的性能造成一定影响,特别是在绘制复杂界面或处理用户操作时。因此优化viewRootImpl的性能通常也是Android开发中的一个重要目标。