This is the 7th day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021

preface

This article is mainly to comb the overall process of View drawing in Android development, to help developers have a deeper understanding of View drawing.

The whole process

View drawing process is divided into three stages: measure, layout and draw. Measure: Calculates the size according to MeasureSpec passed by the parent view. Layout: Puts the child View in an appropriate position according to the size and layout parameters obtained by the measure child View. Draw: Draws a View object onto the screen. So where is the entry point to initiate drawing?

The window component of an Activity has a window member inside the Activity, which is an instance of PhoneWindow. The PhoneWindow has an inner class called DecorView, which holds the layout file. It has the TitleActionBar and the layout file that we passed in as setContentView

  • The Window class is an abstract class that provides an API for drawing Windows
  • A PhoneWindow is a concrete class that inherits Windows and contains a DecorView object that is the root View of all application Windows
  • DecorView inherits FrameLayout, where id= Content is the layout view we passed in

A Window is an electronic screen, a PhoneWindow is an electronic screen, a DecorView is what the electronic screen displays, and an Activity is where the electronic screen is installed.

1. SetContentView process

SetContentView the entire process is mainly how to add the Activity layout file or Java View to the window, summarized as:

  1. Create a DecorView object mDecor that will serve as the root view for the entire application window.

  2. Create a different window decoration layout file based on a style theme such as Feature, and use findViewById to get the Activity layout file where it should be stored (FrameLayout content in the window decoration layout file).

  3. Add the Activity layout file to FrameLayout with id content.

  4. The Activity’s onContentChanged method is called back when the setContentView setting says OK. The findViewById() method of an Activity’s various views can be placed in this method, and the system will help call back.

Second, Android View drawing

View drawing mainly includes three aspects:

  • Measure Measures the size of a component
  • Layout determines the position of the component in the view
  • Draw draws components based on location and size
  • The starting point for rendering a view is the performTraversals() method of the ViewRootImpl class, which does the following: According to the previous state, determine whether to recalculate the test view size (measure), is to reposition the view position (layout) and whether to redraw the view (draw), part of the source code is as follows:
private void performTraversals() { ...... // Lp.width and lp.height are MATCH_PARENT int when creating ViewGroup instances childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); . mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); . mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); . mView.draw(canvas); . }Copy the code

Measure Calculates the view size

Almost all components inherit from View class. For the measurement work of View, measure and onMeasure are the two most commonly used methods in daily development. Measure cannot be rewritten. Inside the method we must complete the mMeasuredWidth and mMeasuredHeight measurements of the component, And the size of the need to measure flow parent and child views from the root view measure traverse the entire view tree structure Also note the view size MeasureSpec is a combination of size, it is a 32 bit value, high two is specMode size model, low 30 is size value, We can make use of the provided acoustic library method for convenient size combination and disassembly:

The specModes are as follows: MeasureSpec.EXACTLY indicates the exact size, MeasureSpec.AT_MOST indicates the maximum size, and MeasureSpec.UNSPECIFIED

int measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); Int specMode = MeasureSpec. GetMode (MeasureSpec); Int specSize = MeasureSpec. GetSize (MeasureSpec);Copy the code
Copy the code

In view measurement Meause, the parent passes the child a combined size, so we can take out the size and generate a new size based on other conditions. Set this value to mMeasuredWidth and mMeasuredHeight with setMeasuredDimension to complete the measurement.

Summary of Measure Principle

  • MeasureSpec (the inner class of the View) measures the size as an int, and the value is made up of specMode for the high 2 bits and specSize for the low 30 bits. SpecMode has only three values:

    MeasureSpec.EXACTLY // The parent View expects the child View to be EXACTLY the size determined by the specSize; MeasureSpec.AT_MOST // The parent View expects the child View to be at most the size specified by specSize; MeasureSpec.UNSPECIFIED // The mode is UNSPECIFIED, and the parent View is determined solely by the design values of the child View.Copy the code
  • The measure method of a View is final and cannot be overloaded. A View subclass can only override onMeasure to complete its own measurement logic.

  • The size of a child measured in the top-level DecorView is determined by the getRootMeasureSpec method in ViewRootImpl (LayoutParams = MATCH_PARENT, specMode = EXACTLY, SpecSize indicates the physical screen size.

  • The ViewGroup class provides measureChild, measureChild, and measureChildWithMargins methods to simplify sizing parent-child views.

  • Any subclass of ViewGroup must require LayoutParams to inherit child MarginLayoutParams, otherwise the layout_margin parameter cannot be used.

  • The layout size of a View is determined by both parent and child views.

  • Use the View’s getMeasuredWidth() and getMeasuredHeight() methods to get the width measured by the View. Both methods must be called after the onMeasure process to return a valid value.

Layout Determines the position of the view

Layout process is mainly traversing the entire view tree structure, call view.layout(int L, int T, int R, int B) to determine the specific coordinate position of view

When we customize a component, we usually rewrite the onLayout method, which implements its own logic, and finally call the Layout method to determine the position of the view. If we customize a component with a ViewGroup, we also need to traverse each child to determine the size

Summary of Layout Principle

  • The whole layout process is easy to understand. From the above analysis, it can be seen that layout is also the process of recursively calling view.layout method from the top parent View to the child View, that is, the layout size and layout parameters obtained by the parent View according to measure child View in the previous step. Put the child View in the appropriate position. The specific layout core mainly includes the following points:

  • The view. layout method can be overloaded, viewGroup. layout is final and viewgroup. onLayout is abstract, and subclasses must override their position logic.

  • MeasuredWidth and measuredHeight are measured for each View. MLeft, mTop, mRight, and mBottom are measured for each View. These values are relative to the parent View.

  • Any layout_XXX layout property is basically for the ViewGroup containing the child View, It makes no sense to set the layout_XXX attribute to a View that has no parent container.

  • Use the View’s getWidth() and getHeight() methods to get the width and height measured by the View. These methods must be called after the onLayout process to return valid values.

The draw draw

Once measure and Layout are complete, the code in ViewRootImpl creates a Canvas object and calls the View’s Draw () method to do the actual drawing. So it’s back to the tree-like recursive draw process of ViewGroup and View

Summary of DRAW principle

As you can see, the drawing process is to draw the View object onto the screen. The whole draw process needs to pay attention to the following details:

  • If the View is a ViewGroup, you need to recursively draw all the child views it contains.

  • A View does not draw anything by default; the actual drawing is done in a subclass.

  • The View is drawn using the Canvas class passed in by the onDraw method.

  • Distinguish between View animation, which refers to the View’s own animation and can be added by setAnimation, and ViewGroup layout animation, which is set specifically for the ViewGroup to display its internal child views. You can set the ViewGroup to a layoutAnimation property in an XML layout file.

  • The padding is automatically removed when fetching the Canvas clipping area (the Canvas passed in each View’s draw). The child View fetching the Canvas does not have to worry about this logic, just how to draw it.

  • Default following the View of ViewGroup. DrawChild drawing order and the View is added order, but you can overload ViewGroup. GetChildDrawingOrder () method provides a different order.

View provides an API that controls the view’s methods

Invalidate and postInvalidate method source analysis

To request that the view be redrawn, call Draw

  • Invalidate is called in the main thread

  • PostInvalidate is called on a non-main thread

View requestLayout method

The requestLayout() method calls the measure and layout procedures, does not call the Draw procedure, and does not redraw any View including the caller itself.

At this point, the whole View of the drawing process we finished analysis. If there is not clear or inaccurate in the article, I hope you can point out, thank you 🙂