标签:
在我们进行android开发的时候虽然官方提供了形形色色的控件,但是有的时候根据不同业务需求我们找不到官方的控件支持,那么这个时候就需要我们自己去定义控件来适应不同的需求了.本篇将和大家一起探讨自定义ViewGrop 的相关知识.
转载请注明出处: http://blog.csdn.net/unreliable_narrator?viewmode=contents
首先我们先来看看官方文档是如何进行描述的:
翻译过来的大体意思就是:一个ViewGroup是一种特殊的视图可以包含其他视图(称为孩子)的视图组基类的布局和视图的容器。这个类也定义了viewgroup.layoutparams类作为基类的布局参数。也就是说ViewGroup实际上就是存放一些控件的容器,比如官方自带的一些Linerlayout,RelativeLayout等等.我们先来讲讲ViewGroup中两个重要的方法:onLayout和onMeasure,onLayout是必须重写实现的.
AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){setMeasureDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));}//可作为模板代码!private int measureWidth(int measureSpec){int result = 0;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if(specMode == MeasureSpec.EXACTLY){//精确值模式,指定具体数值result = specSize;}else{result = 200;//先设置一个默认大小//最大值模式,layout_width 或 layout_height 为 wrap_content 时,控件大小随控件的内容变化而变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。if(specMode == MeasureSpec.AT_MOST){result = Math.min(result, specSize);//取出我们指定的大小和 specSize 中最小的一个来作为最后的测量值}//MeasureSpec.UNSPECIFIED 不指定其大小,View 想多大就多大}return result;}
@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {if (changed) {int childLeft = 0;final int childCount = getChildCount();for (int i = 0; i < childCount; i++) {final View childView = getChildAt(i);if (childView.getVisibility() != View.GONE) {final int childWidth = childView.getMeasuredWidth();childView.layout(childLeft, 0, childLeft + childWidth,childView.getMeasuredHeight());childLeft += childWidth;}}}}
public class MyView extends ViewGroup {
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//调用该方法预先对子控件进行测量
measureChildren(widthMeasureSpec, heightMeasureSpec);
//设置控件的宽高
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
/**
* 返回控件的宽
*
* @param widthMeasureSpec
* @return
*/
private int measureWidth(int widthMeasureSpec) {
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
int result = 0;
//判断是否是包裹内容的模式
if (specMode == MeasureSpec.AT_MOST) {
int size = 0;
//将所有的子控件的宽度进行叠加
for (int x = 0; x < getChildCount(); x++) {
View child = getChildAt(x);
int measuredWidth = child.getMeasuredWidth();
size += measuredWidth;
}
result = size;
} else {
result = specSize;
}
return result;
}
/**
* 返回控件的高
*
* @param heightMeasureSpec
* @return
*/
private int measureHeight(int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int result = 0;
//判断是否是包裹内容
if (heightMode == MeasureSpec.AT_MOST) {
for (int x = 0; x < getChildCount(); x++) {
View child = getChildAt(x);
int measuredHeight = child.getMeasuredHeight();
//取子控件最大的高度
int min = Math.max(result, measuredHeight);
result = min;
}
} else {
result = heightSize;
}
return result;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int left = 0; //左边的距离
View child;
//遍历布局子元素
for (int i = 0; i < childCount; i++) {
child = getChildAt(i);
int width = child.getMeasuredWidth();
child.layout(left, 0, left + width, child.getMeasuredHeight());
left += width;
}
}
}布局文件: <com.dapeng.viewgropdemo.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/black">
<View
android:layout_width="50dip"
android:layout_height="50dip"
android:background="@android:color/holo_green_dark"></View>
<View
android:layout_width="100dip"
android:layout_height="100dip"
android:background="@android:color/holo_red_light"></View>
<View
android:layout_width="200dip"
android:layout_height="200dip"
android:background="@android:color/holo_orange_light"></View>
</com.dapeng.viewgropdemo.MyView>
参数 1. AttributeSet attrs xml解析inflate时生成和容器类型匹配的布局LayoutParams
2. ViewGroup.LayoutParams p 传入viewGroupLayoutParams 然后生成和容器类型匹配的布局LayoutParams
总结:
这个方法主要是用于被子View调用。
生成和此容器类型相匹配的布局参数类。
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams()
{
return new MarginLayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
}
@Override
protected LayoutParams generateLayoutParams(
LayoutParams p)
{
return new MarginLayoutParams(p);
}
cParams = (MarginLayoutParams) childView.getLayoutParams(); int bottomMargin = cParams.bottomMargin; int topMargin = cParams.topMargin; int leftMargin = cParams.leftMargin; int rightMargin = cParams.rightMargin;
/*** 使用自定义LayoutParams必须重写下面的四个方法*/@Overrideprotected boolean checkLayoutParams(ViewGroup.LayoutParams p) {return p instanceof LayoutParams;}@Overrideprotected LayoutParams generateDefaultLayoutParams() {return new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);}@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new LayoutParams(getContext(), attrs);}@Overrideprotected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {return new LayoutParams(p.width, p.height);}
标签:
原文地址:http://blog.csdn.net/unreliable_narrator/article/details/51260561