版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiantanlong/article/details/82347850
当我们实现自定义的VewGroup控件时,会发现其内部的子View的margin、padding属性失效了,这是因为作为包含子View的控件,如果是直接继承自ViewGroup的话,需要自己处理子View的margin、padding的属性。如果是不包含子View的控件的话,则只要处理padding属性就可以了。这里呢,直接贴出代码,因为实现的逻辑相对简单,也在关键位置做了注释。如果对自定义控件不太熟悉的话,建议自行百度学习一下,这样才能够好的理解这个案例。
package hongzhen.com.defineviewdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* 开发者:hongzhen
* 创建时间: 2018/9/3 13:54
* 公司名称:
* 类名:MyMarginViewGroup.java
* 描述:实现ViewGroup对子view的Margin、padding属性的支持
*/
public class MyMarginViewGroup extends ViewGroup {
private int viewsWidth;
private int viewsHeight;
private int marginLeft;
private int marginTop;
private int marginRight;
private int marginBottom;
private int paddingLeft;
private int paddingTop;
private int paddingRight;
private int paddingBottom;
private int viewGroupWidth;
private int viewGroupHeight;
public MyMarginViewGroup(Context context) {
this(context,null);
}
public MyMarginViewGroup(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MyMarginViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}
public MyMarginViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
inti();
}
/**
* 初始化方法
*/
private void inti() {
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewsWidth = 0;
viewsHeight = 0;
marginLeft = 0;
marginTop = 0;
marginRight = 0;
marginBottom = 0;
paddingLeft = getPaddingLeft();
paddingTop = getPaddingTop();
paddingRight = getPaddingRight();
paddingBottom = getPaddingBottom();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
//转换异常,android.view.ViewGroup$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams
// MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
//自定义MarginLayoutParams的实现类,否则报转换异常
CustomLayoutParams lp = (CustomLayoutParams) childView.getLayoutParams();
/**
* measureChild减去了 ViewGroup的padding 保证child最大可用空间
* measureChildWithMargins减去了ViewGroup的padding和子View的margin 保证child最大可用空间
* 这里要使用measureChildWithMargins
*/
// measureChild(childView, widthMeasureSpec, heightMeasureSpec);
measureChildWithMargins(childView,widthMeasureSpec,0,heightMeasureSpec,0);
//子view自上而下排列,左右边距求最大值,上下边距要求和
viewsHeight += childView.getMeasuredHeight();
viewsWidth = Math.max(viewsWidth, childView.getMeasuredWidth());
marginLeft = Math.max(0,lp.leftMargin);//找出最大的左边距
marginTop += lp.topMargin;//求出所有的上边距之和
marginRight = Math.max(0,lp.rightMargin);//找出最大的右边距
marginBottom += lp.bottomMargin;//求出所有的下边距之和
}
//处理ViewGroup的wrap_content情况,求出最小的大小
viewGroupWidth = paddingLeft + viewsWidth + paddingRight + marginLeft + marginRight;
viewGroupHeight = paddingTop + viewsHeight + paddingBottom + marginTop + marginBottom;
setMeasuredDimension(measureWidth(widthMeasureSpec, viewGroupWidth), measureHeight
(heightMeasureSpec, viewGroupHeight));
}
//margin属性配置----------------------------------------------------------------------------------
/**
* 生成默认的布局设置参数
*/
@Override
protected CustomLayoutParams generateDefaultLayoutParams() {
return new CustomLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
/**
* 生成布局参数 将布局参数包装成我们的自定义的MarginLayoutParams实现类
*/
@Override
protected android.view.ViewGroup.LayoutParams generateLayoutParams(
android.view.ViewGroup.LayoutParams params) {
return new CustomLayoutParams(params);
}
/**
* 生成布局参数 从属性配置中生成自定义的MarginLayoutParams实现类
*/
@Override
public android.view.ViewGroup.LayoutParams generateLayoutParams(
AttributeSet attrs) {
return new CustomLayoutParams(getContext(), attrs);
}
/**
* 检查当前布局参数是否是我们定义的MarginLayoutParams类型
*/
@Override
protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams params) {
return params instanceof CustomLayoutParams;
}
/**
*自定义MarginLayoutParams实现类,否则会出现转换异常
*
*/
public static class CustomLayoutParams extends MarginLayoutParams {
public CustomLayoutParams(MarginLayoutParams source) {
super(source);
}
public CustomLayoutParams(android.view.ViewGroup.LayoutParams source) {
super(source);
}
public CustomLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public CustomLayoutParams(int width, int height) {
super(width, height);
}
}
//margin属性配置----------------------------------------------------------------------------------
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
if (b) {
int childCount = getChildCount();
int mTop = paddingTop;
for (int j = 0; j < childCount; j++) {
View childView = getChildAt(j);
MarginLayoutParams lp = (MarginLayoutParams)childView.getLayoutParams();
int mLeft = paddingLeft + lp.leftMargin;
mTop += lp.topMargin;
childView.layout(mLeft, mTop, mLeft + childView.getMeasuredWidth(), mTop + childView.getMeasuredHeight());
mTop += (childView.getMeasuredHeight() + lp.bottomMargin);
}
}
}
private int measureWidth(int measureSpec, int viewGroupWidth) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = specSize;
break;
case MeasureSpec.AT_MOST:
//将剩余宽度和所有子View + padding的值进行比较,取小的作为ViewGroup的宽度
result = Math.min(viewGroupWidth, specSize);
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
private int measureHeight(int measureSpec, int viewGroupHeight) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = specSize;
break;
case MeasureSpec.AT_MOST:
// 将剩余高度和所有子View + padding的值进行比较,取小的作为ViewGroup的高度
result = Math.min(viewGroupHeight, specSize);
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<hongzhen.com.defineviewdemo.MyPaddingView
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_margin="10dp"
android:padding="10dp"/>
<hongzhen.com.defineviewdemo.MyMarginViewGroup
android:background="#aaa"
android:padding="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<hongzhen.com.defineviewdemo.MyPaddingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ddd"
android:padding="10dp"
android:layout_margin="15dp"/>
<hongzhen.com.defineviewdemo.MyPaddingView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ddd"
android:padding="10dp"
android:layout_margin="15dp"/>
</hongzhen.com.defineviewdemo.MyMarginViewGroup>
</LinearLayout>
效果:请忽略上方的MyPaddingView控件,这个是实现View的padding属性的