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,很适合我们学习,感兴趣的可以看看。

浙公网安备 33010602011771号