Analyze view drawing from Android source

Source: Internet
Author: User

In the development process, we often come from defining the view. It is a basic component of user interaction, responsible for displaying images and handling events, and is often inherited as a base class for custom components. So today, let's go through the source code to carefully analyze how the view was created and what happened during the drawing process.

Create

First, there are four overloaded forms of the View public constructor:

    • The view (context context) uses this constructor when creating a view from code, and through the context parameter, you get to the subject, resource, and so on.
    • View (context context, AttributeSet Attrs) This constructor is used when creating a view through an XML layout file, and a constructor method that calls 3 parameters is called.
    • View (context context, AttributeSet attrs, int defstyleattr) Creates a view from an XML layout file and takes the style specified in the property. This view's constructor allows its subclasses to use their own style when they are created. The following four-parameter constructor is called.
    • View (context context, AttributeSet attrs, int defstyleattr, int defstyleres) The constructor can create a view from an XML layout file, either with a theme attribute or a style The style specified by the resource file.

Parameters:

      • Context:view run context information from which you can get information such as the current theme, resource file, and so on.
      • Attributeset:xml the collection of properties specified under the View tab in the layout file.
      • Defstyleattr: A property in the current theme that contains a reference to a style in the theme resource file. The default value is 0.
      • Defstyleres: A style resource file that represents the ID of a style, a value of 0, or a default when the corresponding theme resource is not found.

In summary, the constructor of the single parameter creates a view from the code, and the rest calls the four-parameter constructor to create a view from the XML layout file. We can specify attribute values in different places, for example:

The attrs value that is specified directly in the XML tag can be obtained from the AttributeSet.

    • By specifying the resource file in the Label property "style".
    • The default defstyleattr.
    • The default defstyleres.
    • The default value in the current theme.

The code for the constructor is too long, it is not posted here, the main work is to obtain the properties of each system definition, and then initialize the view's individual member variables and events based on the property values.

In general, when we customize the view, when we rewrite the constructor according to the actual situation, if we only create from code, we can only implement the single parameter. If you need to create from an XML layout file, you need to implement a single parameter and a multi-parameter, because the default of multiple parameters calls the constructor of the four parameter, and then get to the custom property for processing is OK.

At this point, the creation and initialization of the view is complete and then the work of drawing the view is started. So how does the Android system draw the view?

Draw

After the activity acquires the focus, the Android framework is asked to draw according to its layout file, the activity needs to provide the root node of the sketched layout file, and then the tree structure of the layout is traversed side by side to draw. Where ViewGroup is responsible for drawing its child nodes, and view is responsible for drawing its own. The entire traversal process, from top to bottom, requires a size measurement (measure function) and positioning (layout function) throughout the process before drawing. Let's take a look at how these work:

Set size

The size of the view is first determined in the measure method. This method is defined as the final type and cannot be overridden. There is a static inner class Measurespec in view that encapsulates the layout parameters that the parent view will pass to the child view, which is composed of size and mode. The size is sized and mode represents the mode. There are a total of three modes:

    • UNSPECIFIED: The parent view does not specify the size of the child view, optionally specifying the view size according to the developer's needs.
    • Exactly: Parent view strictly specifies the size of the child view
    • At_most: The size of the child view does not exceed this value
 Public Final voidMeasureintWidthmeasurespec,intHeightmeasurespec) {        BooleanOptical = islayoutmodeoptical ( This); //whether to use visual boundary layout           if(Optical! =islayoutmodeoptical (mparent)) {//  when View and its parent viewgroup are inconsistent with visual boundary layout Insets Insets=getopticalinsets (); intOwidth = Insets.left +Insets.right; intOheight = Insets.top +Insets.bottom; Widthmeasurespec= Measurespec.adjust (Widthmeasurespec, optical?-owidth:owidth); Heightmeasurespec= Measurespec.adjust (Heightmeasurespec, optical?-oheight:oheight); }                LongKey = (Long) Widthmeasurespec << 32 | (Long) Heightmeasurespec & 0xffffffffL; if(Mmeasurecache = =NULL) Mmeasurecache =NewLongsparselongarray (2); if((Mprivateflags & pflag_force_layout) = = Pflag_force_layout | |Widthmeasurespec! = Moldwidthmeasurespec | |Heightmeasurespec!=Moldheightmeasurespec) {            //First clears the measured dimension flagMprivateflags &= ~Pflag_measured_dimension_set;            Resolvertlpropertiesifneeded (); intCacheIndex = (Mprivateflags & pflag_force_layout) = = Pflag_force_layout? -1: Mmeasurecache.indexofkey (key); if(CacheIndex < 0 | |Signoremeasurecache) {                //measure ourselves, this should set the measured dimension flag backonmeasure (Widthmeasurespec, Heightmeasurespec); MPRIVATEFLAGS3&= ~pflag3_measure_needed_before_layout; } Else {                LongValue =mmeasurecache.valueat (CacheIndex); //Casting a long to int drops, no mask neededSetmeasureddimensionraw ((int) (Value >> 32), (int) value); MPRIVATEFLAGS3|=pflag3_measure_needed_before_layout; }            //flag not set, Setmeasureddimension () is not invoked, we raise//An exception to warn the developer            if((Mprivateflags & pflag_measured_dimension_set)! =Pflag_measured_dimension_set) {                Throw NewIllegalStateException ("Onmeasure () did not set the" + "measured dimension by calling" + "setmeasureddimension ()"); } mprivateflags|=pflag_layout_required; } Moldwidthmeasurespec=Widthmeasurespec; Moldheightmeasurespec=Heightmeasurespec; Mmeasurecache.put (Key, (Long) mmeasuredwidth) << 32 |                (Long) Mmeasuredheight & 0xffffffffL);//Suppress sign extension}

The two parameters received by the method Widthmeasurespec and Heightmeasurespec represent the width and height of the view, which is passed after the parent view of the previous layer is computed. The measurement of the view size is done in the red Onmeasure method. We often need to override this method when customizing view to set the size of the view's final display based on the incoming view size and its contents.

protected void onmeasure (intint  heightmeasurespec) {        setmeasureddimension (getdefaultsize (Getsuggestedminimumwidth (), Widthmeasurespec),                getdefaultsize (Getsuggestedminimumheight (), Heightmeasurespec));    }

When overriding this method, we need to call the Setmeasureddimension method to store the measured dimensions (which are used by default in the Getdefalutsize), only after calling this method. To get to dimensions through the Getmeasuredwidth method and the Getmeasuredheight method. Together, we want to ensure that the final size is not less than the minimum size of the view. The Ok,measure method is complete. However, we can find that the actual measurement of the view size is not done in this method, here is just a measurement framework, based on a variety of different situations to judge, to complete the necessary steps. These steps are necessary and cannot be changed by the developer, and need to be customized according to the circumstances of the work in the Onmeasure by the developer to complete. This ensures the execution of the drawing process, and flexibly satisfies the various requirements, is a typical template method model.

Because there may be multiple child view under a parent view, the measure method not only executes once, but takes all child view in the parent view, and then iterates through the measure method that invokes the child view.

Positioning

When the size of the view is already set, you need to determine the location of the view in its parent view. The parent view invokes the layout method of the child view:

 Public voidLayoutintLintTintRintb) {if((MPRIVATEFLAGS3 & pflag3_measure_needed_before_layout)! = 0) {onmeasure (Moldwidthmeasurespec, Moldheightmeasurespec); MPRIVATEFLAGS3&= ~pflag3_measure_needed_before_layout; }        intOLDL =Mleft; intOldt =Mtop; intOldb =Mbottom; intOldr =Mright;          // determines whether the layout has changed or if it needs to be redrawn.         BooleanChanged = Islayoutmodeoptical (mparent)?setopticalframe (L, T, R, B): Setframe (L, T, R, b);
         //redraw required.                  if(Changed | | (Mprivateflags & pflag_layout_required) = =pflag_layout_required) {onlayout (changed, L, T, R, b); // determine the position of the view in the layout mprivateflags&= ~pflag_layout_required; Listenerinfo Li=Mlistenerinfo; if(Li! =NULL&& Li.monlayoutchangelisteners! =NULL) {ArrayList<OnLayoutChangeListener> listenerscopy =(ArrayList<OnLayoutChangeListener>) Li.mOnLayoutChangeListeners.clone (); intNumlisteners =listenerscopy.size ();  for(inti = 0; i < numlisteners; ++i) {listenerscopy.get (i). Onlayoutchange ( This, L, T, R, B, Oldl, Oldt, Oldr, OLDB); } }} mprivateflags&= ~pflag_force_layout; MPRIVATEFLAGS3|=pflag3_is_laid_out; }

The method receives four parameters that are the upper and lower left and right positions of the child view relative to the parent view. However, we find that the default implementation of the OnLayout method called to is empty. This is because determining the location of the view in the layout should be done by layout according to its own characteristics. Any layout definition overrides its OnLayout method and sets the location of the child view in it.

Draw

After measuring the size and positioning, you can finally start drawing. View drawing requires a call to the draw method, which is divided into six steps:

    1. Draw background
    2. If necessary, save the canvas's hierarchy to prepare for edge fades.
    3. Drawing the contents of a view
    4. Draw Child View
    5. If necessary, draw the fade edge and store the layer.
    6. Draw decorative parts, such as scroll bars.
 Public voidDraw (canvas canvas) {Final intPrivateflags =Mprivateflags; Final BooleanDirtyopaque = (Privateflags & pflag_dirty_mask) = = Pflag_dirty_opaque &&(Mattachinfo==NULL|| !mattachinfo.mignoredirtystate); Mprivateflags= (Privateflags & ~pflag_dirty_mask) |Pflag_drawn;        //Step 1, drawing the background        intSavecount; if(!Dirtyopaque)        {drawbackground (canvas); }        //Skip steps 2 and 5 if you don't want to        Final intViewFlags =Mviewflags; BooleanHorizontaledges = (ViewFlags & fading_edge_horizontal)! = 0; BooleanVerticaledges = (ViewFlags & fading_edge_vertical)! = 0; if(!verticaledges &&!)horizontaledges) {            //Step 3, draw the content            if(!dirtyopaque) OnDraw (canvas); //Step 4, Draw sub ViewDispatchdraw (canvas); //Step 6, drawing decorative partsondrawscrollbars (canvas); if(Moverlay! =NULL&&!Moverlay.isempty ())            {Moverlay.getoverlayview (). Dispatchdraw (canvas); }            //Complete            return; }    }

We choose the general drawing process, not the 2, 5 steps.

The first step is to call Drawbackground to draw the background pattern:

Private void drawbackground (canvas canvas) {        final drawable background = Mbackground;
         //gets the background of the current view, which is a drawable object         if(Background = =NULL) {            return; }        if(mbackgroundsizechanged) {//  Determines whether the background size changes, or sets the background border Background.setbounds (0, 0, Mright-mleft, Mbottom-mtop); Mbackgroundsizechanged=false; MPRIVATEFLAGS3|=Pflag3_outline_invalid; }        //attempt to use a display list if requested.        if(canvas.ishardwareaccelerated () && mattachinfo! =NULL&& Mattachinfo.mhardwarerenderer! =NULL) {Mbackgroundrendernode=Getdrawablerendernode (background, mbackgroundrendernode); FinalRendernode displaylist =Mbackgroundrendernode; if(Displaylist! =NULL&&Displaylist.isvalid ())                {setbackgrounddisplaylistproperties (displaylist);                ((Hardwarecanvas) canvas). Drawrendernode (Displaylist); return; }} //  Call the drawing method of the Drawable object to finish drawing         Final intSCROLLX =Mscrollx; Final intscrolly =mscrolly; if((scrollx | scrolly) = = 0) {Background.draw (canvas); } Else{canvas.translate (SCROLLX, scrolly);            Background.draw (canvas); Canvas.translate (-SCROLLX,-scrolly); }    }

In the third step, the OnDraw method is called to draw the view content, because different view content is different, so the subclass needs to be rewritten.

Fourth step, draw the sub view, here still need the current layout of the Dispatchdraw method to complete the child view of the drawing.

Sixth step, draw the scroll bar.

In general, we customize the view, the OnDraw method to draw the contents of our defined view.

Summarize

By studying the source of the view class, we can find that in the entire view drawing process we need to complete the measurement size, layout positioning, drawing these three steps. Android in the design process, the fixed process is designed to be immutable template method, however, depending on the circumstances of the content will be given to the developer to complete the rewrite, in the template method called. This design ensures the integrity of the entire process and provides flexibility for the development effort. At the same time, different flags are defined in the class according to different circumstances, to meet the requirements of different situations, and then have the opportunity to study the specific significance of these flags.

Analyze view drawing from Android source

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.