说说Android---图形系统(四)

40 阅读2分钟

网上有一个总结我觉得很清晰:

image.png

我们今天的主题就是

View是如何绘制出来的?

我们知道我们Application的核心载体是Activity(界面),而View是用户可见的并且可以交互的内容。

我们知道View通过measure确定打下,layout确定位置,draw绘制到屏幕上。

今天就从View如何绘制到屏幕上进行分析

画图

setContentView

// Activity.java 

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
}

其中细节我们不展开,很多文章都说这里的getWindow实现PhoneWindow代表一个Window,我个人认为这个PhoneWindow只是一个方便管理View的类而已,它和Android的Window概念不相关。

setContentView做了几件事情:

  1. 初始化DecoreView
  2. 将layoutResID通过inflate生成一棵View树(后续展开)
  3. 将View添加到DecorView里面(FrameLayout)

在ActivityThread.handleResume方法中真正进行View的展示:

// ActivityThread.java

@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
        boolean isForward, String reason) {
    //伪代码
    WindowManager.LayoutParams l = r.window.getAttributes();
    //wm就是WindowManager
    wm.addView(decor, l);
    
}

到这里我们也开始跳过:

WM.addView做了几件事情:

  1. 创建了ViewRootImpl对象
  2. 调用ViewRootImpl.setView方法

稍微看到这个setView方法:

  1. requestLayout触发performTravals
  2. 通过WindowManangerService添加window
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
           requestLayout();
           res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), userId,
                    mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
                    mTempControls);
    }
}

到这里我们简单总结下:

1、 我们的View创建后通过WindowManagerService封装成一个Window管理起来 2、触发ViewRootImple的performTravals进行我们熟悉的View的三步骤 measure/layout/draw

private void performTraversals() {
    //1 设置window的大小
    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
    //2 测量view的大小
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    //3 设置位置
    performLayout(lp, mWidth, mHeight);
    //4 绘制
    performDraw();
}

在绘制时:

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
        canvas = mSurface.lockCanvas(dirty);
        mView.draw(canvas);
        surface.unlockCanvasAndPost(canvas);
        return true;
    }

具体细节其他文章会分析,到这里 Surface.locaCanvas就是我们熟悉的方法。

1、Surface是ViewRootImpl创建的一个对象,这里可以知道一个Window(这里的Window不是PhoneWindow,而是View的集合概念,WindowManagerService管理的WindowState)对应一个ViewRootImpl,一个Surface。 2、 Surface的lockCanvas方法会通过Native的Surface获取GraphicBuffer对象,再通过Canvas进行数据填充。这就是我们之前写的生产者往BufferQueue中dequeueBuffer获取GraphicBuffer,queueBuffer塞入队列的过程。

里面的细节,我们单开文章再讲解.

未完待续....