Android custom ViewGroup and androidviewgroup
There are two types of views: View and ViewGroup. ViewGroup is a subclass of View. ViewGroup can contain all views (including ViewGroup). View can only be self-described and cannot contain other views.
However, the ViewGroup defined by the system has limited functions and cannot meet all our needs. It is very simple to say that it is impossible for others to consider all the details for you. Therefore, we need to customize ViewGroup.
A very simple view inclusion relationship: ViewGroup1-> ViewGroup2->... -> ViewGroupi->... -> ViewGroupn-> View (-> include)
There are two very important procedures: Dimensional Measurement and placement. After the dimensions are measured, place them in the position.
1. Dimensional Measurement
View has a dimensional measurement method onMeasure (int widthMeasureSpec, int heightMeasureSpec). This method is used to set its own size and send the suggested size to its children (if any ).
2. Placement
Only ViewGroup can have a placement. View has a placement method onLayout (boolean changed, int left, int top, int right, int bottom). This method is used to place the position of its children.
At least there must be a ViewGroup with the beginning, so that the size of the suggestions it sends goes down to its children. View the Activity source code. You can see the following method setContentView (View v ):
@Overridepublic void setContentView(View v) { ensureSubDecor(); ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content); contentParent.removeAllViews(); contentParent.addView(v); mOriginalWindowCallback.onContentChanged();}
This method is usually used in onCreate to set the layout. We can see that the contentParent view with the ID android. R. id. content is used to add the input v. ContentParent is a fixed ViewGroup, because the screen size and status bar size are fixed. We can regard this contentParent as a ViewGroup starting.
Then how to pass the suggested size.
MeasureSpec (widthMeasureSpec or heightMeasureSpec) in onMeasure (int widthMeasureSpec, int heightMeasureSpec) contains two information: Measurement Mode specMode and measurement size specSize. Method:
int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);
SpecMode has three modes:
1. View. EXACTLY: It is recommended that you set it to specSize.
2. View. AT_MOST: It is recommended that you set the specSize at most.
3. View. UNSPECIFIED
For example, a RelativeLayout contains a View:
1. If the layout_width set for this View is 100dp, RelativeLayout combines View. EXACTLY and 100dp into a widthMeasureSpec and sends it to the onMeasure method of the View.
2. If the layout_width set for this View is wrap_content, RelativeLayout combines View. AT_MOST and its available width into a widthMeasureSpec to be sent to the onMeasure method of the View.
3. View. UNSPECIFIED is mostly used in areas where the size is uncontrollable. For example, in the subview of ScrollView, ScrollView will set the View. the UNSPECIFIED and size values are 0 combined into a heightMeasureSpec to be sent to the onMeasure method of the View.
The measurement mode and the measurement size are only built upon each other. It is up to them to determine whether children will adopt them.
The next step is how to place the location.
In onLayout (boolean changed, int left, int top, int right, int bottom), left, top, right, and bottom indicate the offset value relative to the parent view, we can use these values to point out its size, width = right-left; height = bottom-top.
Then they are placed in a loop of children.
Perform the following operations
1. Simple customization:
<Com. besttimer. study. myviewgrouptest. customViewGroup android: layout_width = "300dp" android: layout_height = "100dp"> <com. besttimer. study. myviewgrouptest. customView android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: background = "# ffff0000"/> <com. besttimer. study. myviewgrouptest. customView android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: background = "# ff00ff00"/> <com. besttimer. study. myviewgrouptest. customView android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: background = "# ff0000ff"/> </com. besttimer. study. myviewgrouptest. customViewGroup>
The CustomViewGroup method is as follows:
@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {int measuredWidth = this. getMeasureSize (widthMeasureSpec, 100); int measuredHeight = this. getMeasureSize (heightMeasureSpec, 100); int childCount = this. getChildCount (); int childWidth = measuredWidth/childCount; int childHeight = measuredHeight; for (int index = 0; index <childCount; index ++) {View childView = this. getChildAt (index); // use MeasureSpec. EXACTLY defines the measured value int widthMeasureSpec_child = MeasureSpec. makeMeasureSpec (childWidth, MeasureSpec. EXACTLY); int heightMeasureSpec_child = MeasureSpec. makeMeasureSpec (childHeight, MeasureSpec. EXACTLY); // This method will call childView. onMeasure (int widthMeasureSpec, int heightMeasureSpec) childView. measure (widthMeasureSpec_child, heightMeasureSpec_child);} // you must set the size to specify its size in the parent view. this. setMeasuredDimension (measuredWidth, measuredHeight);}/*** get the measurement size ** @ param measureSpec * @ param defaultValue default size * @ return */private int getMeasureSize (int measureSpec, int defaultValue) {int result = defaultValue; int specMode = MeasureSpec. getMode (measureSpec); // Measurement Mode int specSize = MeasureSpec. getSize (measureSpec); // measurement size switch (specMode) {// if there is no recommended measurement mode, the default value is case MeasureSpec. UNSPECIFIED: result = defaultValue; break; // we recommend that you set case MeasureSpec at most in specSize. AT_MOST: // we recommend that you set case MeasureSpec in specSize. EXACTLY: result = specSize; break;} return result ;}
The mviewgroup placement method is as follows:
@ Overrideprotected void onLayout (boolean changed, int l, int t, int r, int B) {int childCount = this. getChildCount (); int childL = 0; int childT = 0; for (int index = 0; index <childCount; index ++) {View childView = this. getChildAt (index); // After onMeasure calculation, int childMeasureWidth = childView can be obtained in this method. getMeasuredWidth (); int childMeasureHeight = childView. getMeasuredHeight (); // display childView side by side. layout (childL, childT, childL + childMeasureWidth, childT + childMeasureHeight); childL + = childMeasureWidth ;}}
The CustomView measurement method is as follows:
@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {// you only need to set your own size in the View. After all, there is no children int measuredWidth = this. getMeasureSize (widthMeasureSpec, 0); int measuredHeight = this. getMeasureSize (heightMeasureSpec, 0); this. setMeasuredDimension (measuredWidth, measuredHeight);}/*** get the measurement size ** @ param measureSpec * @ param defaultValue default size * @ return */private int getMeasureSize (int measureSpec, int defaultValue) {int result = defaultValue; int specMode = MeasureSpec. getMode (measureSpec); // Measurement Mode int specSize = MeasureSpec. getSize (measureSpec); // measurement size switch (specMode) {// if there is no recommended measurement mode, the default value is case MeasureSpec. UNSPECIFIED: result = defaultValue; break; // we recommend that you set case MeasureSpec at most in specSize. AT_MOST: // we recommend that you set case MeasureSpec in specSize. EXACTLY: result = specSize; break;} return result ;}
2. Add declare-styleable
Attributes like LinearLayout and RelativeLayout all have the padding attributes. We can also add them. :
<? Xml version = "1.0" encoding = "UTF-8"?> <Resources> <declare-styleable name = "CustomViewGroup_attrs"> <attr name = "padding" format = "dimension"/> </declare-styleable> </resources>
There are several types of formats. The dimension type is used now.
Add an initialization method to CustomViewGroup:
Private int padding = 0; private void init (Context context, AttributeSet attrs) {TypedArray a = context. obtainStyledAttributes (attrs, R. styleable. customViewGroup_attrs); // obtain the padding value this. padding = (int). getDimension (R. styleable. customViewGroup_attrs_padding, 0); // remember to recycle. recycle ();}
Methods for modifying CustomViewGroup:
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ... int childCount = this.getChildCount(); int childWidth = (measuredWidth - this.padding * 2) / childCount; int childHeight = measuredHeight - this.padding * 2; ...}
@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = this.getChildCount(); int childL = this.padding; int childT = this.padding; ...}
3. Add LayoutParams
TextView and ImageView have the layout_centerVertical vertical center option in RelativeLayout. This is an attribute in LayoutParams of RelativeLayout. In fact, layout_width and layout_height are also attributes in LayoutParams.
Vertical center:
<Com. besttimer. study. myviewgrouptest. customViewGroup android: layout_width = "300dp" android: layout_height = "100dp" android: background = "# ff000000" app: padding = "10dp"> <com. besttimer. study. myviewgrouptest. customView android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: background = "# ffff0000"/> <com. besttimer. study. myviewgrouptest. customView android: layout_width = "wrap_content" android: layout_height = "50dp" android: background = "# ff00ff00" app: layout_centerVertical = "true"/> <com. besttimer. study. myviewgrouptest. customView android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: background = "# ff0000ff"/> </com. besttimer. study. myviewgrouptest. customViewGroup>
Modify the xxx. xml file
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="CustomViewGroup_attrs"> <attr name="padding" format="dimension" /> <attr name="layout_centerVertical" format="boolean" /> </declare-styleable></resources>
Add custom LayoutParams for CustomViewGroup
Public static class LayoutParams extends ViewGroup. layoutParams {public LayoutParams (Context c, AttributeSet attrs) {super (c, attrs); this. init (c, attrs);} private boolean layout_centerVertical = false; // whether to vertically center private void init (Context c, AttributeSet attrs) {TypedArray a = c. obtainStyledAttributes (attrs, R. styleable. customViewGroup_attrs); // gets the value of layout_centerInParent this. layout_centerVertical =. getBoolean (R. styleable. customViewGroup_attrs_layout_centerVertical, false); // remember to recycle. recycle () ;}@ Overridepublic LayoutParams generateLayoutParams (AttributeSet attrs) {return new LayoutParams (this. getContext (), attrs );}
Modify measurement and placement methods
@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec ){... for (int index = 0; index <childCount; index ++) {View childView = this. getChildAt (index); // use MeasureSpec. EXACTLY defines the measured value int childNewHeight = childHeight; CustomViewGroup. layoutParams layoutParams = (LayoutParams) childView. getLayoutParams (); // if the value is greater than 0, a specific value if (layoutParams. height> 0) {childNewHeight = layoutParams. height;} int widthMeasureSpec_child = MeasureSpec. makeMeasureSpec (childWidth, MeasureSpec. EXACTLY); int heightMeasureSpec_child = MeasureSpec. makeMeasureSpec (childNewHeight, MeasureSpec. EXACTLY); // This method will call childView. onMeasure (int widthMeasureSpec, int heightMeasureSpec) childView. measure (widthMeasureSpec_child, heightMeasureSpec_child );}...}
@ Overrideprotected void onLayout (boolean changed, int l, int t, int r, int B ){... for (int index = 0; index <childCount; index ++) {View childView = this. getChildAt (index); // After onMeasure calculation, int childMeasureWidth = childView can be obtained in this method. getMeasuredWidth (); int childMeasureHeight = childView. getMeasuredHeight (); // place CustomViewGroup side by side. layoutParams layoutParams = (LayoutParams) childView. getLayoutParams (); int childNewT = childT; if (layoutParams. layout_centerVertical) {childNewT + = (B-t-this. padding * 2-childMeasureHeight)/2;} childView. layout (childL, childNewT, childL + childMeasureWidth, childNewT + childMeasureHeight); childL + = childMeasureWidth ;}}
Among them, layout_width and layout_height are the attributes of ViewGroup. LayoutParams. the retrieval logic has been implemented and can be used directly.
Source code: http://files.cnblogs.com/files/linyibiao/AndroidProject.zip