4.1 Getting started with ViewRoot and DecorView

  • ViewRoot corresponds to the ViewRootImpl class and is the link between Windows Manager and the DecorView. View’s three major processes are completed through View Wroot. In ActivityThread, when the Activity object is created, the DecorView is added to the Window, the ViewRootImpl object is created, and the ViewRootImpl object is associated with the DecorView.

  • The rendering process of View starts from performTraversals of ViewRoot, and a View can be drawn only after measure, layout and draw.

    • Measure measures the width and height of the View,
    • Layout is used to determine the position of the View in the parent container,
    • Draw draws the View onto the screen.
  • PerformTraversals invoke performMeasure, performLayout, and performDraw, which performMeasure, layout, and draw of top-level views, respectively. Among them, the measure method will be called in performMeasure, and the onMeasure method will be called in the measure method. In the onMeasure method, the measure process will be performed on all the child elements, thus completing a measure process. The child element repeats the parent’s measure process, iterating through the View count. The other two processes are similar, and the general call process is shown as follows:

  • Measure process determines the width/height of the View that is completed by getMeasuredWidth/getMeasureHeight method to get the View after the measurement of the width/height.
  • The Layout process determines the coordinates of the four vertices of the View and the width and height of the actual View. After completion, you can get the coordinates of the View through getTop, getBotton, getLeft and getRight.
  • The Draw process determines the display of the View before its contents are displayed on the screen.
  • A DecorView is a top-level View that typically contains a vertical LinearLayout inside that is divided into two parts (depending on the Android version and theme) : the title bar and the interior bar. The layout file that is set in the Activity via setContextView is actually loaded into the interior bar.
// Get the contents column
ViewGroup content = findViewById(R.android.id.content);
// Get viewContext.getChildAT (0);
Copy the code

A DecorView is actually a FrameLayout. All View layer events pass through the DecorView before being passed to our View.

4.2 understand MeasureSpec

  • MeasureSpec defines the size of a View. When a View is measured, the layoutParams of the View are converted into a MeasureSpec based on the rules imposed by the parent container.

  • MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec = MeasureSpec

  • There are three types of MpecMode;

    The parent UNSPECIFIED container has no restrictions on the View, and is used internally as UNSPECIFIED. The exact size of the View is determined by the parent container, which is the value specified by SpecSize. It corresponds to match_parent and concrete value modes in LayoutParams. 3. The AT_MOST parent container specifies an available size (SpecSize). The size of the View cannot be larger than this value.

  • When a View is of a fixed width/height, regardless of the parent container’s MeasureSpec, the View’s MeasureSpec is exact and follows the size of Layoutparams.

  • When a View’s width/height is match_parent, if its parent’s mode is exact, the View is exact and the size is the remaining space of the parent; If the parent container is the largest mode, then the View is also the largest mode and the size does not exceed the remaining space of the parent container.

  • When the View’s width/height is wrAP_content, regardless of whether the parent’s schema is accurate or maximized, the View’s schema always maximizes and cannot exceed the remaining space of the parent.

(MeasureSpec) (ScrollView nested ListView conflict) (MeasureSpec) (ScrollView nested ListView conflict

A custom ListView override onMeasure method

@Override protected void onMeasure(int widthMeasureSpec, Int heightMeasureSpec) {/ / reset highly heightMeasureSpec = MeasureSpec makeMeasureSpec (Integer. MAX_VALUE > > 2. MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, heightMeasureSpec); }Copy the code

MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); The method is to generate a MeasureSpec 32-bit int value of type MeasureSpec based on the size and schema passed in. Two bits represent the pattern, and the remaining 30 bits represent the size. So the Integer.MAX_VALUE >> 2 is the maximum size of 30 bits. The mode is MeasureSpec. Equivalent to warp_content, no more than the maximum size effect.

4.3 Workflow of View

This process of measur

A View’s measure process

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }Copy the code
  • The setMeasuredDimension method sets the width/height measurements of the View
  • The getDefaultSize method returns the same size as measureSpec’s specSize, the measured size of the View, which in most cases is the same as the final size of the View (as determined in the Layout stage).
  • GetSuggestedMinimumWidth method, as the first argument (suggested width) to getDefaultSize
    • If the View does not set the background, return the android:minWidth property, which can be 0
    • If there is a background, take the maximum values in Android :minWidth and background
  • A custom control that inherits a View directly needs to override the onMeasure method and set the size of wrap_content, otherwise using wrap_content in the layout is equivalent to using match_parent.
    • Solution: In onMeasure, specify an internal width/height for the View after calculation and set it in WRAP_content. In other cases, use the system measurement value.

Measure procedure of ViewGroup

  • For a ViewGroup, in addition to completing its own measure process, it also iterates to call measure methods of all child elements, and each child element recursively executes the process. Unlike a View, a ViewGroup is an abstract class that does not override the View’s onMeasure method, but provides measureChildren.
  • MeasureChildren method, traversing to get child elements that call the measureChild method
  • MeasureChild (); measureChild (); measureChild (); measureChild (); measureChild (); measureChild (); measureChild (); measureChild ()
  • The ViewGroup does not define its measurement process. Because different ViewGroup subclasses have different layout characteristics (such as LinearLayout and RelativeLayout have different characteristics), the onMeasure method of the measurement process needs to be implemented by each subclass.
  • Once measured, the measureWidth /Height of the View can be measured using the getMeasureWidth/Height method. Note that in extreme cases, the system may need to measure multiple times to determine the final measurement width /Height. A good practice is to get the measurement width/height or final width/height in the onLayout method.

The onMeasure method on the LinearLayout is used to analyze the ViewGroup’s measure process:

Step 1: Call the onMeasure method. Determine whether the layout is horizontal or vertical; Select measureVertical or measureHorizontal based on the layout direction. Step 2: Enter the measureVertical method, traverse the child elements, and execute the measureChildBeforeLayout method on each child element. The measureChildBeforeLayout method internally calls the measure method of the child element. MTotalLength This variable stores the vertical height of the LinearLayout. Step 3: When the child elements are measured, the LinearLayout will measure its size according to the child elements. If the height is match_parent or a specific value, the drawing process is the same as the View. If warp_content is used, So its height is the height of all the children plus the vertical Padding.

How to get View width/height information in an Activity

Because the View’s measure process and the Activity’s life cycle are not synchronized, if the View has not finished measuring, then the width/height obtained is 0. Therefore, the width/height of the View cannot be correctly obtained in the onCreate, onStart and onResume sections of the Activity. Here are four solutions.

  1. The Activity/View# onWindowFocusChanged. The onWindowFocusChanged method means that the VieW is initialized and the width and height are ready. Note that it will be called multiple times, both when the Activity window gains focus and when it loses focus.
  2. The post (runnable). Post a runnable to the end of the message queue. When Looper calls the runnable, the View is initialized.
  3. ViewTreeObserver. This is done using the ViewTreeObserver’s many callbacks, such as the OnGlobalLayoutListener interface, which calls back to the onGlobalLayout method when the state of the View tree changes or the visibility of the View inside the View tree changes. Note that onGlobalLayout will be called back multiple times as the state of the View tree changes.
  4. The measure (int widthMeasureSpec, int heightMeasureSpec). (1). Match_parent: can’t measure the specific width and height, because we don’t know the remaining space of the parent container, we can’t measure the size of the View (2). Specific values (dp/px) :
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(100,MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(100,MeasureSpec.EXACTLY);
view.measure(widthMeasureSpec,heightMeasureSpec);
Copy the code

(3). Wrap_content:

int widthMeasureSpec = MeasureSpec.makeMeasureSpec((1<<30)- 1,MeasureSpec.AT_MOST);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec((1<<30)- 1,MeasureSpec.AT_MOST);
view.measure(widthMeasureSpec,heightMeasureSpec);
Copy the code

4.3.2 layout process

  • The Layout function is used by the ViewGroup to determine the child elements. When the ViewGroup position is confirmed, the Layout will traverse all the child elements and call the onLayout method. The Layout method determines the position of the View itself, while the onLayout method determines the position of all the child elements
  • View layout method to determine its position, the source process is as follows:
    • SetFrame determines the position of the top, bottom, left, and right vertices of the View, which determines the position of the View in the parent container
    • Call onLayout method to determine the location of all child views. Just like onMeasure, the specific implementation of onLayout is related to the layout, so the View and ViewGroup do not really implement onLayout method.

Take the onLayout method on the LinearLayout as an example:

  • The setChildFrame method is called to specify the location of the child elements by iterating through all the child views, with the childTop increasing as the following child elements are placed lower.
  • The setChildFrame method actually calls the layout method of the child View. After calculating its own positioning, the onLayout method calls the layout method of the child element so that the child element can determine its position, forming a recursion and completing the layout process of the View tree.

The difference between the measured width and final width of View:

In the default implementation of a View, the measurement height of the View is the same as the final width, except that the measurement height is formed in the measure process and the final width is formed in the Layout process. But rewriting the Layout method of the View can make them unequal.

4.3.3 View draw process

  • Draw the View onto the screen in the following steps: 1. Draw the background background.draw(canvas) 2. 3. Draw children(dispatchDraw draw method) 4. Draw foreground, scrollbars and other decorations (onDrawScrollBars)
  • The View is drawn using dispatchDraw, which iterates through the draw methods of all child elements.
  • If a View doesn’t need to draw anything, setting setWillNotDraw to true will optimize it accordingly. ViewGroup defaults to true, and if our custom ViewGroup needs to draw content via onDraw, it needs to be explicitly turned off. The call setWillNotDraw (false)

4.4 Customizing a View

4.4.1 User-defined View classification

Inherit View to override onDraw method

The onDraw method is used to achieve some irregular effects, which are not convenient to achieve through the combination of layout. This method needs to support wrap_content itself, and the padding needs to be handled as well.

Inherit from ViewGroup to derive a special layout

To achieve a custom layout, it is necessary to properly handle the measurement and layout of the ViewGroup, as well as the measurement and layout of sub-views.

Inheriting specific View subclasses (e.g. TextView, Button)

It’s easy to extend the functionality of an existing control without having to manage wrap_content and padding yourself.

Inherit a specific ViewGroup subclass (such as LinearLayout)

More common, to achieve the effect of a combination of several views.

4.4.2 Instructions for Customizing views

  • Controls that directly inherit from a View or ViewGroup require special processing of wrAP_content in onMeasure. Specifies the default width/height in wrAP_content mode.
  • Control that directly inherits from the View, the padding property does not work if the padding is not handled in the draw method.
  • Controls that directly inherit from ViewGroup also need to take the padding and child element margin into account in onMeasure and onLayout. Otherwise, the padding and child element margin are invalid.
  • Try not to use handlers in views because you don’t need them. View internal provides a post series of methods, can completely replace the role of the Handler.
  • The View has threads and animations that need to be stopped in the View’s onDetachedFromWindow. Threads and animations also need to be stopped when the View is not visible, otherwise memory leaks can occur.
  • Sliding conflicts need to be handled when a View has sliding nesting

reference

Chapter 4: Exploring the Art of Android Development