Home >Java >javaTutorial >Summary of using custom ViewGroup in Android
Category
Custom Layout can be divided into two situations.
Customize ViewGroup and create some ViewGroups different from LinearLayout, RelativeLayout, etc. For example: GridLayout added after API 14, CoordinatorLayout in the design support library, etc.
Customize some existing Layouts and add some special functions. For example: TableLayout and PercentFrameLayout in percent support library, etc.
Process
The process of custom View is: onMeasure()->onLayout()->onDraw(). When customizing ViewGroup, it is generally not necessary to implement onDraw. Of course, there may be special requirements, such as: CoordinatorLayout.
So onMeasure and onLayout can basically do most of the ViewGroups we come into contact with. But it's not enough to just know how to measure the size of ViewGroup in onMeasure and calculate the position of Child View in onLayout.
For example: How can I set properties for the Child View of ViewGroup?
one example.
Write a custom ViewGroup and add an attribute to control the size (length and width) of the Child View in proportion to the ViewGroup.
Assuming it is a LinearLayout, then define a CustomLinearLayout first.
public class CustomLinearLayout extends LinearLayout { public CustomLinearLayout(Context context) { super(context); } public CustomLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } }
Apart from anything else, we need to add attributes to control the size of the Child View. Then define that attribute in value/attr.xml first (use CustomLinearLayout_Layout to distinguish it from CustomLinearLayout, of course the name is arbitrary).
<declare-styleable name="CustomLinearLayout_Layout"> <!-- 定义比例 --> <attr name="inner_percent" format="float"/> </declare-styleable>
This method will eventually be called when ViewGroup calls addView().
public void addView(View child, int index, LayoutParams params)
This params represents the configuration of View. ViewGroup.LayoutParams contains width and height, LinearLayout.LayoutParams adds weight attributes, etc. Then we should implement a LayoutParams. So that's it for now.
public class CustomLinearLayout extends LinearLayout { public CustomLinearLayout(Context context) { super(context); } public CustomLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } public static class LayoutParams extends LinearLayout.LayoutParams { private float innerPercent; private static final int DEFAULT_WIDTH = WRAP_CONTENT; private static final int DEFAULT_HEIGHT = WRAP_CONTENT; public LayoutParams() { super(DEFAULT_WIDTH, DEFAULT_HEIGHT); innerPercent = -1.0f; } public LayoutParams(float innerPercent) { super(DEFAULT_WIDTH, DEFAULT_HEIGHT); this.innerPercent = innerPercent; } public LayoutParams(ViewGroup.LayoutParams p) { super(p); } @TargetApi(Build.VERSION_CODES.KITKAT) public LayoutParams(LinearLayout.LayoutParams source) { super(source); } @TargetApi(Build.VERSION_CODES.KITKAT) public LayoutParams(LayoutParams source) { super(source); this.innerPercent = source.innerPercent; } public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); init(c, attrs); } private void init(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLinearLayout_Layout); innerPercent = a.getFloat(R.styleable.CustomLinearLayout_Layout_inner_percent, -1.0f); a.recycle(); } } }
Now we can use our attributes in xml.
<?xml version="1.0" encoding="utf-8"?> <com.egos.samples.custom_layout.CustomLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="200dp" android:layout_height="200dp" android:id="@+id/test_layout" android:background="#ffff0000" android:gravity="center" android:orientation="vertical"> <ImageView android:text="Egos" android:layout_width="match_parent" android:layout_height="match_parent" android:onClick="add" android:background="#ff00ff00" app:inner_percent="0.8"/> </com.egos.samples.custom_layout.CustomLinearLayout>
It’s just soft and has no effect.
So how do you control the size of Child View? Of course it is controlled in onMeasure. addView will execute the following code.
requestLayout(); invalidate(true);
In this case, onMeasure() and onLayout() will be walked over again. After implementing the onMeasure() method, I will directly handle the size of the Child View. Because I inherit LinearLayout, measureChildBeforeLayout() will actually be processed. The final step is to handle the size of the Child View when measuringChildBeforeLayout.
@Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { // 在xml强制写成match_parent,然后在这里强制设置成 if (child != null && child.getLayoutParams() instanceof LayoutParams && ((LayoutParams) child.getLayoutParams()).innerPercent != -1.0f) { parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentWidthMeasureSpec) * ((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentWidthMeasureSpec)); parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (MeasureSpec.getSize(parentHeightMeasureSpec) * ((LayoutParams) child.getLayoutParams()).innerPercent), MeasureSpec.getMode(parentHeightMeasureSpec)); super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); } else { super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); } }
In this way, the initial requirements can be achieved.
In fact, there are still some details that need to be processed, the following code is.
/** * 当checkLayoutParams返回false的时候就会执行到这里的generateLayoutParams */ @Override protected LinearLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { return super.generateLayoutParams(lp); } /** * 当addView的时候没有设置LayoutParams的话就会默认执行这里的generateDefaultLayoutParams */ @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(); } /** * 写在xml中属性的时候就会执行这里的generateLayoutParams */ @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); }
To summarize
What you need to pay more attention to when customizing View and ViewGroup are onMeasure, onLayout, and onDraw.
Separate the properties of the ViewGroup itself and the properties of the Child View.
You can refer to the code in the support package, and debugging is also very convenient.
When doing Android development, there are indeed many places where you need to customize Views, but most of them have corresponding open source libraries. But we still need to be proficient in knowing how to customize a ViewGroup.
For more articles related to the summary of using custom ViewGroup in Android, please pay attention to the PHP Chinese website!