Basic knowledge of Android 26: Analysis of the view rendering process and invalidate () in Android

Source: Internet
Author: User
Tags call back

This article is a summary of the working principle of view, which I have read in Chapter 13th of Android Kernel Analysis. At the same time, I sincerely recommend this book to anyone eager to learn about the android framework layer. I hope you can learn more in Android development.
The drawing process of the entire view tree is in viewroot. the extends mtraversals () function of the Java class is expanded. The execution process of this function is as follows: Determine whether to recalculate the View Size (measure) based on the previously set status), whether to relocate the view location (layout), and whether to re-draw (draw), the Framework process is as follows:

The step is host. layout ()

Next, let's take a look at the structure of the entire view tree. The operation on each specific view object is actually a recursive implementation.

For details about the decorview Root View, refer to my blog:

Adding layout files/views to Window Process Analysis in Android-start with setcontentview ()

StreamProcess 1: mesarue () Process

Main function: calculate the actual size for the entire view tree, that is, set the actual height (corresponding attribute: mmeasuredheight) and width (corresponding attribute: mmeasurewidth ), the actual width and height of each view control are determined by the parent view and its own view.
 
The call chain is as follows:
The viewroot root object property mview (whose type is generally viewgroup) calls the measure () method to calculate the size of the view tree and calls back the onmeasure () method of the View/viewgroup object, this method provides the following functions:
1. Set the final size of the view. This function is implemented by calling the setmeasureddimension () method to set the actual height (corresponding attribute: mmeasuredheight) and width (corresponding attribute: mmeasurewidth );
2. If the view object is of the viewgroup type, you must override the onmeasure () method to traverse the measure () process of its subviews.

2.1 The measure () process for each sub-view is by calling the parent class viewgroup. the measurechildwithmargins () method in the Java class is implemented. This method simply calls the measure () method of the view object. (Because the measurechildwithmargins () method is only a simple method of transition layer, it is to directly call the measure () method of the view object ).

The entire measure call process is a tree-like recursive process.
 
Measure function prototype is view. java. This function cannot be overloaded.

Public final void measure (INT widthmeasurespec, int heightmeasurespec) {//... // callback onmeasure () method onmeasure (widthmeasurespec, heightmeasurespec); // more}

For better understanding, we use the "B programmer" method to describe the Measure Process Using Pseudo code.

// Viewroot in the measure () process. java // initiate the "sender" of measure () in viewroot. java's javasmtraversals () method, mview. measure () Private void extends mtraversals (){//... view mview; mview. measure (H, L );//....} // call back the onmeasure process private void onmeasure (INT height, int width) in the view. {// set the actual width (mmeasuredwidth) of the view to mmeasuredheight) // 1. This method must be called in onmeasure. If not, an exception is reported. Setmeasureddimension (H, L); // 2. If the view is of the viewgroup type, perform the measure () process int childcount = getchildcount () for each of its child views (); for (INT I = 0; I <childcount; I ++) {// 2.1, get each child view object reference view child = getchildat (I); // The entire measure () A process is a recursive process // This method is only a filter, and the measure () process is called at last; or the measurechildwithmargins (child, H, I) methods are all measurechildwithmargins (child, H, i); // In fact, the best way for applications we write is to remove the method in the framework and directly call view. measure (), as follows: // child. measure (H, l) }}// The method is implemented in viewgroup. java. Protected void measurechildwithmargins (view V, int height, int width) {v. Measure (H, L )}

Process 2. layout process:

Main function: place the view tree in a proper position based on the size and layout parameters of the subview.
The call chain is as follows:
Host. layout () starts the layout of the view tree and calls back the layout () method in the View/viewgroup class. The procedure is as follows:

1. The layout method sets the coordinate axes of the view in the parent view, that is, mleft, MTop, mleft, and mbottom (implemented by calling the setframe () function). Next, it calls back onlayout () method (if the view is a viewgroup object, you need to implement this method to layout each subview );

2. If the view is of the viewgroup type, you need to traverse each sub-view, and call the layout () method of the sub-view to set its coordinate value.
 
The layout function is prototype view. java.
 

/* Final identifier, which cannot be overloaded. The parameter is the coordinate axis * @ Param l left position, relative to parent * @ Param t top position of each view, relative to parent * @ Param r right position, relative to parent * @ Param B bottom position, relative to parent */public final void layout (INT L, int T, int R, int B) {Boolean changed = setframe (L, t, R, B); // set the axis of each view in the parent view if (changed | (mprivateflags & layout_required) = layout_requ IRED) {If (viewdebug. trace_hierarchy) {viewdebug. trace (this, viewdebug. hierarchytracetype. on_layout);} onlayout (changed, L, t, R, B); // calls back the onlayout function and sets the layout of each subview mprivateflags & = ~ Layout_required;} mprivateflags & = ~ Force_layout ;}

Similarly, the preceding layout call process is described as follows using pseudocode:

// Viewroot during layout. java // initiate layout () "sender" in viewroot. java's javasmtraversals () method, mview. layout () Private void extends mtraversals (){//... view mview; mview. layout (left, top, right, bottom );//....} // calls back the onlayout process in the view. This method implements private void onlayout (INT left, int top, right, bottom) only by the viewgroup type) {// if the view is not of the viewgroup type, // call the setframe () method to set the coordinate axis setframe (L, t, R, B) of the control on the parent view ); // ------------------------ // if the view is of the viewgroup type, the layout () process int childcount = getchildcount (); For (INT I = 0; I <childcount; I ++) {// 2.1, get each sub-view object reference view child = getchildat (I); // The Entire Layout () process is a recursive process child. layout (L, t, R, B );}}

Process 3. Drawing Process

Call the draw () method of the viewroot object's javasmtraversals () method to draw the view tree. It is worth noting that each time a drawing is initiated, the view of each view tree is not re-drawn, instead, it only re-draws the views that require re-painting. The internal variables of the View class contain a flag drawn. When the view needs to be re-painted, the flag is added to the view.
 
Call process:
Mview. Draw () starts to draw. The functions implemented by the draw () method are as follows:
1. Draw the background of the View
2. Prepare the gradient box (see Figure 5. In most cases, you do not need to change the gradient box)
3. Call the ondraw () method to draw the view itself (this method needs to be reloaded for each view, and viewgroup does not need to implement this method)
4. Call the dispatchdraw () method to draw a subview (if the view type is not viewgroup, that is, the subview is not included, and this method does not need to be reloaded)
It is worth noting that the viewgroup class has already rewritten the dispatchdraw () function implementation for us. In general, applications do not need to rewrite this method, but you can override the parent class function to implement specific functions.
 
4.1 The dispatchdraw () method traverses each subview and calls drawchild () to call back the draw () method of each subview. (Note, the draw () method is called only for views that require redrawing ). It is worth noting that the viewgroup class has already rewritten the dispatchdraw () function implementation for us. In general, applications do not need to rewrite this method, but you can override the parent class function to implement specific functions.

5. Draw a scroll bar
 
Therefore, the entire call chain is recursive in this way.

Similarly, the pseudocode is described as follows:

// Viewroot in the draw () process. java // initiate the "poster" of draw () in viewroot. java's javasmtraversals () method. This method continues to call the draw () method to start drawing private void draw (){//... view mview; mview. draw (canvas );//....} // calls back the onlayout process in the view. This method is implemented only by the viewgroup type private void draw (canvas) {// This method will do the following: // 1. Draw the background of the view. // 2. Prepare the gradient box. // 3. Call ondraw () method to draw the view itself // 4. Call the dispatchdraw () method to draw each sub-view. dispatchdraw () has been implemented in the android framework, in the viewgroup method. // The application generally does not need to override this method, but it can capture the occurrence of this method and do some special things. // 5. Draw a gradient box} // viewgroup. the dispatchdraw () method in Java. Generally, the application does not need to override this method @ override protected void dispatchdraw (canvas) {// Its implementation method is similar to the following: int childcount = getchildcount (); For (INT I = 0; I <childcount; I ++) {view child = getchildat (I); // call drawchild to complete drawchild (child, canvas) ;}// viewgroup. the dispatchdraw () method in Java. Generally, the application does not need to override this method protected void drawchild (view child, canvas ){//.... // simple callback view object The draw () method of is generated by recursion. Child. Draw (canvas );//.........}

For details about how to draw a background image, refer to my other blog:

In-depth analysis on the principle of drawing background images of different States and statelistdrawable usage of views in Android

Note that in these three flows, Google has helped us write the draw () process framework, and the custom viewgroup only needs to implement measure () process and layout () process.
 
There are several factors that may cause the re-drawing of the view tree:
 
1. The View Size changes;
2. Cause the viewgroup to allocate a new position for the subview.
3. View display needs to be re-painted
 
In these three cases, three functions, invalidate (), requsetlaytout (), and requestfocus (), are called directly or indirectly (), then the three functions will eventually call the schedultraversale () method in viewroot. This function then initiates an asynchronous message, and calls the javasmtraverser () method in message processing to traverse the entire view.

Invalidate () method:
Note: You need to re-paint the view tree, that is, the draw () process. If the View Size does not change, the layout () process will not be called and only the "re-painted" views will be drawn, that is, if a view (view, only the view is drawn; viewgroup, the entire viewgroup is drawn), and the view is drawn when the invalidate () method is requested.
 
Generally, the function that causes the invalidate () operation is as follows:
1. Call the invalidate () method directly and request a new draw (), but only the caller is drawn.
2. setselection () method: Request re-draw (), but only the caller itself will be drawn.
3. setvisibility () method: When the visible state of a view is converted to visible in invisible, the invalidate () method is called indirectly and the view is drawn.
4. setenabled () method: request to re-draw (), but will not re-draw any view including the caller itself.
 
Requestlayout () method: the measure () process and layout () process will be called:
Note: The layout process of view tree re-layout includes the measure () and layout () processes. The draw () process is not called, but no view is re-drawn, including the caller.
 
Generally, the function that causes the invalidate () operation is as follows:
1. setvisibility () method:
The requestlayout () and invalidate methods are called indirectly when the visual status of a view is changed from invisible to gone. At the same time, because the size of the entire view tree has changed, the measure () process and the draw () process will be requested. Similarly, only the view that needs to be "re-drawn" will be drawn.
 
Description of the requestfocus () function:
Note: The draw () process of the view tree is requested, but only the view that needs to be re-painted is drawn.
 
 
Below is a simple demo. The main purpose is to demonstrate the drawing process and some functions in each process. As follows:

1. myviewgroup. Java custom viewgroup type

Package COM. qin. customviewgroup; import android. content. context; import android. graphics. canvas; import android. util. attributeset; import android. util. log; import android. view. view; import android. view. viewgroup; import android. widget. button; import android. widget. imageview; import android. widget. textview;/*** @ author http: // http://blog.csdn.net/qinjuning * // custom viewgroup object public class myviewg Roup extends viewgroup {Private Static string tag = "myviewgroup"; private context mcontext; Public myviewgroup (context) {super (context); mcontext = context; Init ();} // attributes defined in XML. The constructor public myviewgroup (context, attributeset attrs) {super (context, attrs); mcontext = context; Init ();} // Add three sub-views for myviewgroup private void Init () {// call the parent class addview () of viewgroup to add a sub-view // child Object 1: button BTN = new button (mcontext); BTN. settext ("I am button"); this. addview (BTN); // child object 2: imageview IMG = new imageview (mcontext); IMG. setbackgroundresource (R. drawable. icon); this. addview (IMG); // child object 3: textview TXT = new textview (mcontext); TXT. settext ("only text"); this. addview (txt); // child object 4: Custom view myview = new myview (mcontext); t His. addview (myview) ;}@ override // measure () for each sub-View: sets the size of each sub-view, that is, the actual width and height protected void onmeasure (INT widthmeasurespec, int heightmeasurespec) {// using the init () method, we have added three views for the viewgroup object: button, imageview, textview int childcount = getchildcount (); log. I (TAG, "the size of this viewgroup is ---->" + childcount); log. I (TAG, "***** onmeasure start *****"); // obtain the actual length and width of the viewgroup. Int S is used for the measurespec class. Pecsize_widht = measurespec. getsize (widthmeasurespec); int specsize_heigth = measurespec. getsize (heightmeasurespec); log. I (TAG, "***** specsize_widht" + specsize_widht + "* specsize_heigth ******" + specsize_heigth); // set the width and height setmeasureddimension (specsize_widht, specsize_heigth); For (INT I = 0; I <childcount; I ++) {view child = getchildat (I); // obtain the reference child for each object. measure (50, 50); // simple setting The width and height of each sub-view object are 50px, 50px // or you can call the viewgroup parent class method measurechild () or measurechildwithmargins () method // This. measurechild (child, widthmeasurespec, heightmeasurespec) ;}@override // layout each sub-view. Protected void onlayout (Boolean changed, int L, int T, int R, int B) {// todo auto-generated method stub // using the init () method, we add three views to the viewgroup object, button, imageview, textview int childcount = getchildcount (); int St Artleft = 0; // set the starting abscissa int starttop = 10 for each sub-view. // set the position of each sub-view to 10px. It can be understood as Android: margin = 10px; log. I (TAG, "***** onlayout start *****"); For (INT I = 0; I <childcount; I ++) {view child = getchildat (I); // obtain the reference child of each object. layout (startleft, starttop, startleft + child. getmeasuredwidth (), starttop + child. getmeasuredheight (); startleft = startleft + child. getmeasuredwidth () + 10; // calibrate the startleft value. The distance between views is set to 10px; log. I (TAG, "***** onlayout startleft *****" + startleft) ;}// the plotting process android has encapsulated for us, here we only want to observe the call process protected void dispatchdraw (canvas) {log. I (TAG, "***** dispatchdraw start *****"); super. dispatchdraw (canvas);} protected Boolean drawchild (canvas, view child, long drawingtime) {log. I (TAG, "***** drawchild start *****"); return Super. drawchild (canvas, child, drawingtime );}}

Myview. Java custom view type, override ondraw () method

Package COM. qin. customviewgroup; import android. content. context; import android. graphics. bitmap; import android. graphics. bitmapfactory; import android. graphics. canvas; import android. graphics. color; import android. graphics. paint; import android. graphics. typeface; import android. graphics. bitmap. config; import android. util. attributeset; import android. util. log; import android. view. view; // custom view object public class myview extends view {private paint = new paint (); Public myview (context) {super (context ); // todo auto-generated constructor stub} public myview (context, attributeset attrs) {super (context, attrs);} protected void onmeasure (INT widthmeasurespec, int heightmeasurespec) {// set the View Size to 80 80 setmeasureddimension (50, 50);} // The canvas object exists, that is, the default display area @ override public void ondraw (canvas) exists) {// todo auto-generated method stub super. ondraw (canvas); log. I ("myviewgroup", "myview is ondraw"); // bold paint. settypeface (typeface. defaultfromstyle (typeface. bold); paint. setcolor (color. red); canvas. drawcolor (color. blue); canvas. drawrect (0, 0, 30, 30, paint); canvas. drawtext ("myview", 10, 40, paint );}}

Main file layout: Main. xml

<? XML version = "1.0" encoding = "UTF-8"?> <Linearlayout xmlns: Android = "http://schemas.android.com/apk/res/android" Android: Orientation = "vertical" Android: layout_width = "fill_parent" Android: layout_height = "fill_parent"> <textview Android: id = "@ + ID/TXT" Android: layout_width = "fill_parent" Android: layout_height = "wrap_content" Android: text = "@ string/Hello"/> <button Android: id = "@ + ID/BTN" Android: layout_width = "fill_parent" Android: layout_margintop = "20px" Android: layout_height = "wrap_content" Android: TEXT = "click to view the log rendering process"> </button> <COM. qin. customviewgroup. myviewgroup Android: Id = "@ + ID/custemviewgroup" Android: layout_height = "fill_parent" Android: layout_width = "fill_parent"> </COM. qin. customviewgroup. myviewgroup> </linearlayout>

The main activity only displays the XML file, so it is no longer difficult here. You can view the log of the viewgroup and carefully analyze the process of drawing the view and the usage of related methods. The logs captured after the first startup are as follows. Some information is found on the Internet. The first view tree is drawn several times. The specific reason may be that some views have changed and the request is re-drawn, however, this does not affect our interface display.

In general, the entire painting process is still very complicated. The implementation of each specific method is difficult for our generation to immediately feel tragedy. There is also pressure on the implementation of some viewgroup objects provided by Android, such as linearlayout and relativelayout layout objects. This article focuses on the process of drawing the entire view tree. We hope that you will have more contact with the source code for further extension.

Example Demo: http://download.csdn.net/detail/qinjuning/3982468

References:
Analysis of view rendering process and invalidate () in Android

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.