Several common ViewGroup implementations are available in Android, including LinearLayout, Relativeayout, Framelayout, and more. These viewgroup can meet our general development needs, but for the complexity of the interface requirements, these layouts are stretched. So custom viewgroup are ubiquitous in the applications we've been exposed to.
To implement a custom viewgroup, the first step is to learn to customize the properties that will allow us to configure the layout file more flexibly. A custom property is a Attrs.xml file declared under the value directory.
<?xml version= "1.0" encoding= "Utf-8"? ><resources> <declare-styleable name= "CascadeViewGroup" > <attr name= "verticalspacing" format= "Dimension"/> <attr name= "horizontalspacing" format= "Dimension" /> </declare-styleable> <declare-styleable name = "Cascadeviewgroup_layoutparams" > <attr name= "layout _paddingleft " format=" Dimension "/> <attr name=" Layout_paddintop " format=" Dimension "/> </declare-styleable></resources
Here we declare two custom attribute sets, the properties in Cascadeviewgroup are set for our custom Cascadeviewgroup component, that is, the layout file can be <CascadeViewGroup> The properties that can be used in the label. The other cascadeviewgroup_layoutparams is the property that is set for the child view in Cascadeviewgroup.
Before writing the code, we also set a default width and height for cascadelayout to use. These two properties are defined in Dimens.xml.
<?xml version= "1.0" encoding= "Utf-8"?><resources> <dimen name= "default_horizontal_spacing" >10dp& lt;/dimen> <dimen name= "default_vertical_spacing" >10dp</dimen></resources>
The following is the beginning of writing custom component Cascadelayout.
package com.app.customviewmotion;import android.content.context;import Android.content.res.typedarray;import android.util.attributeset;import android.view.view;import android.view.viewgroup;/** * created by charles on 2015/8/13. */ public class cascadeviewgroup extends viewgroup { // Customize the width and height set in the layout private int mhoriztonalspacing; private int mverticalspacing; public cascadeviewgroup (Context context) { this (context, null); } public cascadeviewgroup (context context, attributeset attrs) { this (context, attrs, 0); } public cascadeviewgroup (context context, attributeset attrs, int defstyle) { super (Context, attrs, defstyle); Typedarray a = context.obtainstyledattributes (Attrs, r.styleable.cascadeviewgroup); try { //get the width of the setting Mhoriztonalspacing = a.getdimensionpixelsize (r.styleable.cascadeviewgroup_horizontalspacing, this.getresources (). Getdimensionpixelsize (r.dimen.default_horizontal_spacing)); //get the height of settings mverticalspacing =&Nbsp;a.getdimensionpixelsize (r.styleable.cascadeviewgroup_verticalspacing, this.getresources (). Getdimensionpixelsize (r.dimen.default_vertical_spacing)); } catch (exception e) { e.printstacktrace (); } finally { a.recycle (); } } @Override protected Void onmeasure (Int widthmeasurespec, int heightmeasurespec) { final int count = this.getchildcount (); int&nbSp;width = this.getpaddingleft (); int height = this.getpaddingtop (); for (int i = 0; i < count; i++) { final view currentview = this.getchildat (i); this.measurechild (currentview, widthmeasurespec, HEIGHTMEASURESPEC); cascadeviewgroup.layoutparams lp = (cascadeviewgroup.layoutparams) Currentview.getlayoutparams (); if ( lp.msettingpaddingleft != 0) { width +=lp.msettingpaddingleft; } if (lp.msettingpaddingtop != 0) { height +=lp.mSettingPaddingTop; } lp.x = width; lp.y = height; width += mhoriztonalspacing; height += mverticalspacing; } width +=getchildat (This.getchildcount () - 1). GetMeasuredWidth () + this.getpaddingright (); height += getchildat (This.getChildCount () - 1). Getmeasuredheight () + this.getpaddingbottom (); this.setmeasureddimension (Resolvesize (Width, widthmeasurespec), resolvesize (height, HEIGHTMEASURESPEC)); } @Override Protected void onlayout (boolean b, int l, int i1, int i2, INT&NBSP;I3) { final int count = This.getchildcount (); for (int i = 0; i < count; i++) { final view currentview = this.getchildat (i); cascadeviewgroup.layoutparams lp = (Cascadeviewgroup.layoutparams) currentview.getlayoutparams (); currentview.layout (LP.X,&NBSP;LP.Y, lp.x + currentview.getmeasuredwidth (), lp.y + Currentview.getmeasuredheight ()); } } public static class LayoutParams extends viewgroup.layoutparams { int x; int y; int msettingpaddingleft; int msettingpaddingtop; public layoutparams (Context c, aTtributeset attrs) { super (c, &NBSP;ATTRS); typedarray a = c.obtainstyledattributes (Attrs, r.styleable.cascadeviewgroup_layoutparams); msettingpaddingleft = a.getdimensionpixelsize ( r.styleable.cascadeviewgroup_layoutparams_layout_paddingleft, 0); msettingpaddingtop = a.getdimensionpixelsize ( r.styleable.cascadeviewgroup_layoutparams_layout_paddintop, 0); a.recycle (); } public layoutparams (int width, int height) { &nbsP; super (width, height); } public layoutparams (Viewgroup.layoutparams source) { super (source); } } @Override protected viewgroup.layoutparams generatedefaultlayoutparams () { return new layoutparams (layoutparams.wrap_content, layoutparams.wrap_content); } @Override protected Viewgroup.layoutparams generatelayoutparams (viewgroup.layoutparams p) { return new layoutparams (P); } @Override pubLic viewgroup.layoutparams generatelayoutparams (attributeset attrs) { return new layoutparams (This.getcontext (), attrs); }}
The code has a slight advantage, but the structure is still very clear.
1) Construct the value of the configuration property in the method or in the XML file. Use the methods in Typedarray to get the properties that we set in layout layouts, and save them in member variables.
2) constructs the custom inner class layoutparams. Constructing this inner class allows us to save their property values when we measure our sub-view so that they can be laid out in the layout stage.
3) Generatelayoutparams (), Generatedefaultparams () and other methods. In these methods, we return our custom layoutparams. As for why to rewrite these methods, it is clear that you can see the AddView () method of the ViewGroup class.
4) Measure stage. In the measure phase, we measure our size, and we also measure the size of the sub-view and save the Child View information in Layoutparams.
5) Layout stage. Layout their locations based on the information of each child view.
Finally, add the layout file.
<?xml version= "1.0" encoding= "Utf-8"?><!--Add custom properties to viewgroup--><!-- The suffix of the newly added namespace must remain consistent with the package name declared in the. xml--><com.app.customviewmotion.cascadeviewgroup xmlns:android= "Http://schemas.android.com/apk/res/android" xmlns:ts= "Http://schemas.android.com/apk/res/com.app.CustomViewMotion" android:layout_width= "Match_parent" android:layout_height= "Match_parent" ts:horizontalspacing = "15DP" ts:verticalspacing= "15DP" > <textview android:layout_width= "100DP" android:layout_height= "100DP" android:gravity= "Center " android:text=" Text1 " android:background= "# 668b8b "/> <textview android:layout_width=" 100DP " android:layout_height= "100DP" android:gravity= "Center" android:text= "Text2" android:background= "#FFDAB9"/> <textview android:layout_width= "100DP" android:layout_height= "100DP" android:gravity= "Center" android:text= "Text3" Android:background= "#43CD80"/>< Add custom sub-view properties to this child view--> <textview Android:layout_width= "100DP" android:layout_height= "100DP" android:gravity= "Center" android:text= "Text4" ts:layout_paddingleft= "100DP" ts:layout_paddintop= "100DP" android:backgroUnd= "#00CED1"/></com.app.customviewmotion.cascadeviewgroup>
The effect of the implementation is as follows:
Implementation of custom ViewGroup in Android