Android custom control inheritance ViewGroup creating a new container

Source: Internet
Author: User

Welcome to learn this section, the previous sections we have learned several other custom controls, the audio bar of the Andriod custom control and the creation of a andriod custom control can be reused. Students who have not yet learned, please go to study, because this section will use the content described in the previous sections.

Before learning something new, let's start by figuring out two questions:
1. What is ViewGroup?

ViewGroup is a container. It contains 0 or more view and sub-view.

2. What role does ViewGroup have?

The ViewGroup can be used to hold multiple view controls and measure the view child control based on its own measurement mode, and to determine the location of the view child control. This will step you through how it measures and determines the size and position of the child controls.

OK, to figure out these two questions, let's learn the custom viewgroup below.

First, as in the previous sections, inherit ViewGroup and rewrite their construction methods.

public class CustomViewGroup extends ViewGroup{    public CustomViewGroup(Context context) {        this(context,null);    }    public CustomViewGroup(Context context, AttributeSet attrs) {        this(context, attrs,0);    }    public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }}

In the above two questions, we know that viewgroup it is a container, it is used to store and manage child controls, and the child control is measured according to its measurement mode, so we have to rewrite its onmeasure (), in this method to measure the size of the child view, The code is as follows:

 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int childCount = getChildCount();        for(int i = 0 ; i < childCount ; i ++){            View children = getChildAt(i);            measureChild(children,widthMeasureSpec,heightMeasureSpec);        }    }

On the code, we rewrite the Onmeasure (), in the method, we first get the number of child view in the ViewGroup, and then traverse all of its child view, get each sub-view, call Measurechild () put, to measure the pair view. The measurement of the sub-view mentioned earlier is based on the measurement mode provided by ViewGroup, so in the Measurechild () method, the ViewGroup Widthmeasurespec and Heightmeasurespec with the child view, we can go in and see if it's the same as what we said.

Measurechild () method source code:

    protected void measureChild(View child, int parentWidthMeasureSpec,            int parentHeightMeasureSpec) {        final LayoutParams lp = child.getLayoutParams();        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,                mPaddingLeft + mPaddingRight, lp.width);        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,                mPaddingTop + mPaddingBottom, lp.height);        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);    }

Measurechild () Source code method inside very good understanding, it first get sub-view of Layoutparams, and then according to ViewGroup Pass in the wide high attribute value and its own layoutparams The value of the wide-height attribute and the value of its own padding attribute are called by the Getchildmeasurespec () method to obtain a measurement of the child view. By this method we also know that when measuring the size of sub-view in ViewGroup, the measurement results are determined by the measurement mode of the parent node and the layoutparams and padding of the child view itself respectively.

Let's take a look at the source code of the Getchildmeasurespec () method and see how it obtains the measurement results.

Getchildmeasurespec () method source code:

    public static int Getchildmeasurespec (int spec, int padding, int childdimension) {int specmode = MEASURESPEC.G        Etmode (spec);        int specsize = measurespec.getsize (spec);        int size = Math.max (0, specsize-padding);        int resultsize = 0;        int resultmode = 0; Switch (Specmode) {///Parent has imposed a exact size on US case MeasureSpec.EXACTLY:if (child                Dimension >= 0) {resultsize = childdimension;            Resultmode = measurespec.exactly; } else if (childdimension = = layoutparams.match_parent) {//child wants to is our size.                So is it.                resultsize = size;            Resultmode = measurespec.exactly; } else if (childdimension = = layoutparams.wrap_content) {//child wants to determine its own size.                It can ' t be//bigger than us.                resultsize = size;     Resultmode = Measurespec.at_most;       } break;                Parent have imposed a maximum size on US case MeasureSpec.AT_MOST:if (childdimension >= 0) {                Child wants a specific size ... so is it resultsize = childdimension;            Resultmode = measurespec.exactly; } else if (childdimension = = layoutparams.match_parent) {//child wants to being our size, but we size is no                T fixed.                Constrain bigger than us.                resultsize = size;            Resultmode = Measurespec.at_most; } else if (childdimension = = layoutparams.wrap_content) {//child wants to determine its own size.                It can ' t be//bigger than us.                resultsize = size;            Resultmode = Measurespec.at_most;        } break; Parent asked to see how big we want to is case MeasureSpec.UNSPECIFIED:if (childdimension >= 0) {//child wants a specific size ... let him has it resultsize = childdimension;            Resultmode = measurespec.exactly;  } else if (childdimension = = layoutparams.match_parent) {//child wants to is our size ... find out how big It should//be resultsize = View.susezerounspecifiedmeasurespec?                0:size;            Resultmode = measurespec.unspecified;  } else if (childdimension = = layoutparams.wrap_content) {//child wants to determine its own size .... find How about//big it should be resultsize = View.susezerounspecifiedmeasurespec?                0:size;            Resultmode = measurespec.unspecified;        } break;    } return Measurespec.makemeasurespec (ResultSize, Resultmode); }

The method is also well understood: the first is to get the measurement mode of the parent node (here is ViewGroup) and the size of the measurement, and to compare the value of the measured size to the Padding property value of the sub view itself to get the maximum value of a size.
Then according to the parent node's measurement mode to determine the sub-view of the Layoutparams attribute value, according to the Layoutparams attribute value to obtain the sub-view measurement size and mode, know that the Ziview measurement mode and size can determine the size of the sub-view.

OK, sub-view measurement we have fully understood, then, we will analyze how viewgroup to sub-view positioning, first we must first rewrite the OnLayout () method, the code is as follows:

@Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int childCount = getChildCount();        int preHeight = 0;        for(int i = 0 ; i < childCount ; i ++){            View children = getChildAt(i);            int cHeight = children.getMeasuredHeight();            if(children.getVisibility() != View.GONE){                children.layout(l, preHeight, r,preHeight += cHeight);            }        }    }

Well understood, to the sub-view location, we must first know how many sub-view to line, so we first get the number of child view, and then traverse to get each child view. In fact, in the positioning sub-view layout () method, the system does not give a specific positioning method, but gives us the maximum limit to define their own, the following view of the layout of the source:

public void layout (int l, int t, int r, int b) {if (MPRIVATEFLAGS3 & pflag3_measure_needed_before_layout)! =            0) {onmeasure (Moldwidthmeasurespec, Moldheightmeasurespec);        MPRIVATEFLAGS3 &= ~pflag3_measure_needed_before_layout;        } int oldl = Mleft;        int Oldt = Mtop;        int oldb = Mbottom;        int oldr = Mright;                Boolean changed = Islayoutmodeoptical (mparent)?        Setopticalframe (L, T, R, B): Setframe (L, T, R, b); if (changed | |            (Mprivateflags & pflag_layout_required) = = pflag_layout_required) {onlayout (changed, L, T, R, b);            Mprivateflags &= ~pflag_layout_required;            Listenerinfo li = mlistenerinfo; if (Li! = null && li.monlayoutchangelisteners! = null) {arraylist<onlayoutchangelistener> Li                Stenerscopy = (arraylist<onlayoutchangelistener>) li.mOnLayoutChangeListeners.clone (); InchT numlisteners = Listenerscopy.size ();  for (int i = 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; }

In the above section of the code, the most critical is setframe (L, T, R, b); This method, it is mainly to locate the sub-view of the four vertices around the coordinates, and then the key positioning method is in the OnLayout (changed, L, T, R, b); In this method, take a look inside.

    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {    }

A look startled, empty, haha, this is what I said above, the system gave us the greatest freedom, let us define according to the needs.
And I've got them arranged in a vertical order based on the height of the sub-view.

    View children = getChildAt(i);    int cHeight = children.getMeasuredHeight();    if(children.getVisibility() != View.GONE){    children.layout(l, preHeight, r,preHeight += cHeight);

Defines a variable that records the height of the previous view, and each time it is traversed, it is added to the current view height so that each sub-view is arranged vertically and sequentially, enabling the definition of the child view.

OK, so much so, now let's look at the effect, and we'll take the custom view we made earlier as its sub-view:

Custom_viewgroup.xml file:

<?xml version= "1.0" encoding= "Utf-8"? ><com.sanhuimusic.mycustomview.view.customviewgroup android: Background= "#999999" xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:custom= "http// Schemas.android.com/apk/res-auto "android:id=" @+id/customviewgroup "android:layout_width=" Match_parent "android:l ayout_height= "Match_parent" > <com.sanhuimusic.mycustomview.view.compositeviews android:background= "#999999        "Android:id=" @+id/topbar "android:layout_width=" wrap_content "android:layout_height=" Wrap_content " custom:titletext= "@string/titletext" custom:titlecolor= "#000000" custom:titletextsize= "@dimen/titletex Tsize "custom:titlebackground=" #999999 "custom:lefttext=" @string/lefttext "custom:lefttextcolor=" #FFF FFF "custom:leftbackground=" #666666 "custom:lefttextsize=" @dimen/lefttextsize "custom:righttext=" @str Ing/righttext "Custom:righttextcolor=" #FFFFFF"Custom:rightbackground=" #666666 "custom:righttextsize=" @dimen/righttextsize "/> <com.sanhu        Imusic.mycustomview.view.AudioBar android:layout_width= "match_parent" android:layout_height= "Wrap_content" /></com.sanhuimusic.mycustomview.view.customviewgroup>

Mainactivity:

  public class Mainactivity extends Appcompatactivity {private compositeviews topbar;    Private Context Mcontext;    Private Customviewgroup Mviewgroupcontainer;        @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);        Setcontentview (R.layout.custom_viewgroup);        Mcontext = this;    Init ();        } private void Init () {Mviewgroupcontainer = (customviewgroup) Findviewbyid (R.id.customviewgroup);        Topbar = (compositeviews) Findviewbyid (R.id.topbar); Topbar.setontopbarclicklistener (New Compositeviews.topbarclicklistener () {@Override public void left            Clicklistener () {Toastutil.maketext (Mainactivity.this, "You clicked the Back key", Toast.length_short). Show (); } @Override public void Rightclicklistener () {Toastutil.maketext (Mainactivity.this, "            You clicked the search key ", Toast.length_short). Show ();    }        }); }}


Haha, is not each sub-view in accordance with what we say in the vertical sequence down. is happy, and then suddenly came out of an idea, learned andriod custom control audio Bar This article, you will remember that when the definition of a new view, when we are using the layout file is Wrap_content, view is not directly supported, Need our special handling to be able to support correctly, and our present viewgroup is also like this, hurry to try. A try, broken, sure enough not to support wrap_content.

So, when customizing ViewGroup, we have to pay attention to the following issues:

1. You must let ViewGroup support the layout of the wrap_content scenario.
2. You also need to support your own padding attribute.

OK, let's make it perfect for 1.1 points.

1. We let IT support wrap_content first.

This requires that we make some more necessary changes in the Onmeasure () method. Let IT support itself wrap_content that would require us to wake it up to measure, get to the measured size, and then call Setmeasureddimension () to determine the display size.

@Override protected void onmeasure (int widthmeasurespec, int heightmeasurespec) {super.onmeasure (widthmeasuresp        EC, HEIGHTMEASURESPEC);        int childCount = Getchildcount ();            for (int i = 0; i < childCount; i + +) {View children = getchildat (i);        Measurechild (CHILDREN,WIDTHMEASURESPEC,HEIGHTMEASURESPEC);        }/** * Let IT support itself wrap_content */int widthspecmode = Measurespec.getmode (Widthmeasurespec);        int widthspecsize = measurespec.getsize (Widthmeasurespec);        int heightspecmode = Measurespec.getmode (Heightmeasurespec);        int heightspecsize = measurespec.getsize (Heightmeasurespec);        int mwidth = 0;        int mheight = 0;        int mmaxwidth = 0; if (Widthspecmode = = Measurespec.at_most && Heightspecmode = = measurespec.at_most) {for (int i = 0; I &l T ChildCount;                i + +) {View children = getchildat (i); Mwidth + = Children.getmeasuredwidth ();            Mheight + = Children.getmeasuredheight ();        } setmeasureddimension (Mwidth, mheight);  } else if (Widthspecmode = = Measurespec.at_most) {for (int i = 0; i < childCount; i + +) {View                Children = Getchildat (i);            Mmaxwidth = Math.max (Mmaxwidth,children.getmeasuredwidth ());        } setmeasureddimension (Mmaxwidth,heightspecsize); } else if (Heightspecmode = = Measurespec.at_most) {for (int i = 0; i < childCount; i + +) {Vie                W children = Getchildat (i);            Mheight + = Children.getmeasuredheight ();        } setmeasureddimension (Widthspecsize,mheight); }    }

We have added a code that supports wrap_content based on the original, and then gets the size according to the specific situation. It is divided into three situations:

    1. When the width and height attributes are wrap_content, the width and height of the child view are obtained respectively, and the total width and height are obtained, and the Setmeasureddimension (Mwidth, mheight) is directly set.
    2. When the wide attribute is wrap_content, the width of the child view is obtained and the maximum value is obtained, and the Setmeasureddimension (mmaxwidth,heightspecsize) is directly set.
    3. When the high attributes are wrap_content, the high and sum of the sub-view are obtained to get the total height, which can be set directly in the call Setmeasureddimension (Widthspecsize,mheight).

Well, let's see if we can meet our requirements.

It is clear that the goal has been achieved.

2. You need to support the Padding property of itself.

First we get the padding value first, as follows:

        leftPadding = getPaddingLeft();        topPadding = getPaddingTop();        rightPadding = getPaddingRight();        bottomPadding = getPaddingBottom();

These attribute values are then added to the set size, as follows:

if (Widthspecmode = = Measurespec.at_most && Heightspecmode = = measurespec.at_most) {for (int i = 0; I &l T ChildCount;                i + +) {View children = getchildat (i);                Mwidth + = Children.getmeasuredwidth ();            Mheight + = Children.getmeasuredheight ();        } setmeasureddimension (Mwidth + leftpadding + rightpadding, Mheight + toppadding + bottompadding);                } else if (Widthspecmode = = Measurespec.at_most) {for (int i = 0; i < childCount; i + +) {                View children = Getchildat (i);            Mmaxwidth = Math.max (Mmaxwidth,children.getmeasuredwidth ());        } setmeasureddimension (Mmaxwidth + leftpadding + rightpadding, heightspecsize + toppadding + bottomPadding);                } else if (Heightspecmode = = Measurespec.at_most) {for (int i = 0; i < childCount; i + +) {                View children = Getchildat (i); Mheight + = Children.getmeasureDheight ();        } setmeasureddimension (widthspecsize + leftpadding + rightpadding, Mheight + toppadding + bottompadding); }

Finally, add the attribute value in the OnLayout () method:

@Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int childCount = getChildCount();        int preHeight = topPadding;        for(int i = 0 ; i < childCount ; i ++){            View children = getChildAt(i);            int cHeight = children.getMeasuredHeight();            if(children.getVisibility() != View.GONE){                children.layout(l + leftPadding, preHeight, r + rightPadding, preHeight += cHeight);            }        }    }

The code is very simple, no longer let Preheight = 0, but directly set to toppadding, and finally in the layout of the property values are added to see the results.

In fact, in addition to the above two questions need attention, there are other also need to pay attention to, such as support sub-view of the margin attribute, and to solve the same padding attributes, we can try to achieve the next.

Well, the entire custom ViewGroup content is finished, of course, we just tell the UI display, and did not talk about the functionality of the addition and implementation. As can be seen from the above, custom ViewGroup is much more complex than custom view, but it can be implemented in a step-by-step, different UI presentation.

From these sections of custom control learning, you must learn a lot of knowledge, and then the custom control is not so afraid, but also can achieve their own desired UI, and then I will summarize the custom control needs to use the other technology and knowledge, so that everyone better to deepen the impression.

Well, learn to come here today, happy!.

For more information please pay attention to the platform, blog updates will be timely notification. Love to learn love technology.

Android custom control inheritance ViewGroup creating a new container

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.