ViewGroup初探,自定义LinearLayout

这篇文章我们开始向ViewGroup进军,打造一个简单的LinearLayout,当然该LinearLayout还不能用于项目中,只是为了学习使用。

首先new一个class, 让它继承自ViewGroup, 默认会让你重写onLayout方法,因为该方法在ViewGroup中是abstract的:

@Override
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);

但是只重写onLayout还是不够的, 还要重写onMeasured方法用来测量子view和保存自己最终的大小。


哦,对了,还记得layout_width、layout_height吗? 这些其实不是view的属性,而是ViewGroup的, 即view告诉ViewGroup我要的宽度和高度,我们的LinearLayout也需要一个LayoutParams:

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
	return new MarginLayoutParams(getContext(), attrs);
}
重写ViewGroup的generateLayoutParams方法,直接返回一个简单的MarginLayoutParams,因为我们需要view的margin。


好吧, 看看我们在onMeasured方法中做了什么,onMeasure方法主要的工作就是测量子view的大小,并确定自己的大小:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	final int childCount = getChildCount();
//	measureChildren(widthMeasureSpec, heightMeasureSpec);
		
	int widthMode = MeasureSpec.getMode(widthMeasureSpec);
	int heightMode = MeasureSpec.getMode(heightMeasureSpec);
	int widthSize = MeasureSpec.getSize(widthMeasureSpec);
	int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		
	int width = 0;
	int height = 0;
		
	for(int i=0;i<childCount;i++) {
		View view = getChildAt(i);
		if(view.getVisibility() == View.GONE) {
			continue;
		}
			
		measureChild(view, widthMeasureSpec, heightMeasureSpec);
		MarginLayoutParams p = (MarginLayoutParams) view.getLayoutParams();
		width = Math.max(width, view.getMeasuredWidth() + p.leftMargin + p.rightMargin);
		height += view.getMeasuredHeight() + p.topMargin + p.bottomMargin;
	}
		
	// 如果mode是EXACTLY, 则设置为父布局传过来的值
	// 如果是AT_MOST, 则设置为自己测量的结果
	setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize
				: width, heightMode == MeasureSpec.EXACTLY ? heightSize
				: height);
	}

6~9行,使用MeasureSpec的两个静态方法,分别获取了width的mode、size和height的mode、size,mode是干什么的?如果mode==MeasureSpec.EXACTLY的话,我们就确定ViewGroup的大小为父布局给我们建议的大小,如果是 MeasureSpec.AT_MOST的话,就需要我们自己测量了。

14~24行,遍历所有的子view,首先调用ViewGroup的一个方法measueChild来测量一下该view,要么view.getMeasuredXXX()获取到的肯定是0。然后获取该view的宽度和高度,让ViewGroup的宽度等于当前ViewGroup宽度和该view宽度的最大值,让ViewGroup的高度累加上当前View的高度。

最后调用setMeasuredDimension来设置测量结果,可以看到这里有一个条件,表示如果mode是MeasureSpec.EXACTLY的话,我们不去理会自己的测量结果,直接将父布局给出的建议大小保存起来就ok。


onLayout需要做的是确定子view的位置:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
	final int childCount = getChildCount();
	int height = 0;
	for(int i=0;i<childCount;i++) {
		View view = getChildAt(i);
		if(view.getVisibility() == View.GONE) {
			continue;
		}
			
		MarginLayoutParams p = (MarginLayoutParams) view.getLayoutParams();
		MarginLayoutParams p = (MarginLayoutParams) view.getLayoutParams();
		view.layout(p.leftMargin, height + p.topMargin,view.getMeasuredWidth() + p.rightMargin,height + view.getMeasuredHeight() + p.topMargin + p.bottomMargin);
		height += view.getMeasuredHeight() + p.topMargin + p.bottomMargin;
	}
}



代码结果和onMeasure很相似,只是在循环中,调用了view.layout方法来设置该view在布局中的位置,逻辑也很简单,就是将height的值累加。


在布局文件中使用MyLinearLayout:

<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <org.loader.mylinearlayout.MyLinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_green_light" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text1" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text2" />
        <TextView
			android:layout_marginTop="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text3" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text4" />
        <TextView
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="text" />
    </org.loader.mylinearlayout.MyLinearLayout>

</RelativeLayout>


看看效果:



达到我们的效果了。当然这还是最简单的ViewGroup,google官方的api中也有一个demo,很适合我们学习,感兴趣的可以看看。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亓斌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值