一、概述
1.ViewGroup的职责是什么?
(1)放置View的容器
(2)给childView计算出建议的宽和高和测量模式(为什么只是建议的宽和高,而不是直接确定呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高)
(3)决定childView摆放的位置(通过调用子View的layout(l,t,r,b)方法,让孩子view在此区域绘制自身)
(4)计算出自身的宽和高
2.View职责是什么?
(1)根据自己的测量模式和ViewGroup建议的宽和高,计算出自己的宽和高
(2)在ViewGroup为其指定的区域内绘制自己的形态
二、ViewGroup大体绘制过程
ViewGroup绘制包括两个步骤:1.measure 2.layout
在两个步骤中分别调用回调函数:1.onMeasure() 2.onLayout()
1.onMeasure() 在这个函数中,ViewGroup会接受childView的请求的大小,
然后通过childView的measure(newWidthMeasureSpec, heightMeasureSpec)函数存储具体宽高到childView中,
以便childView的getMeasuredWidth() 和getMeasuredHeight() 的值可以被后续工作得到。
2.在ViewGroup 的onMeasure的最后要调用setMeasuredDimension()这个方法存储本身的宽高,这个方法决定了
当前ViewGroup的大小。如果不调用次方法,则会抛出IllegalStateException异常。
3.onLayout() 在这个函数中,ViewGroup会拿到childView的getMeasuredWidth() 和getMeasuredHeight(),
用来布局所有的childView。
其中onMeasure中的两个参数,是从父控件传递下来的布局要求。这两个要求是按照View.MeasureSpec编码的,具体可以查看View.MeasureSpec源码。
其实一句话可以总结:这个类包装了从parent传递下来的布局要求,通过int值传递给这个childView。每个MeasureSpec由一个size和mode组成。
三、.View的三种测量模式
EXACTLY:精确值或者match_parent
AT_MOST:wrap_content
UNSPECIFIED:父元素不对子元素宽高进行束缚,AdapterView 中父控件传入
四、自定义样例(一)
- GridView实现分割线的可能方法
因为GridView不像ListView一般有自己的divider,而实际应用过程中,对于GridView分隔线的需求又比较大,所以此次选取了这个样例。
大家可以一起整理下思路,看看有哪些方法或许会实现GridView分割线。
(1)通过设置android:horizontalSpacing和android:verticalSpacing两个属性值以及背景色
这种方式设置背景色填充GridView之间的Spacing,
(2)给每项设置shape,shape用stroke描线
(3)自定义ViewGroup
ViewGroup的绘制严格来说是在dispatchDraw中完成,作为一个容器,绘制自己的孩子该通过dispatchDraw(canvas)实现。但当你的ViewGroup本身没有可绘制的内容时,onDraw方法不会被调用
因此,一般直接重写dispatchDraw来绘制viewGroup。
- GridView实现分割线思想
1、只有第一行,绘制Cell上端的横线
2、只有第一列,绘制Cell左边的竖线
3、如果Cell不是最后一个,绘制右边的竖线以及下边的横线
- 代码实现
<span style="font-size:14px;">@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
View localView1 = getChildAt(0);
int column;
column = COLUMNNUM;
int childCount = getChildCount();
Paint localPaint;
localPaint = new Paint();
localPaint.setStyle(Paint.Style.STROKE);
localPaint.setColor(getContext().getResources().getColor(R.color.line_gray));
for (int i = 0; i < childCount; i++) {
View cellView = getChildAt(i);
if (i < 3) {//第一行
canvas.drawLine(cellView.getLeft(), cellView.getTop(), cellView.getRight(), cellView.getTop(), localPaint);
}
if (i % column == 0) {//第一列
canvas.drawLine(cellView.getLeft(), cellView.getTop(), cellView.getLeft(), cellView.getBottom(), localPaint);
}
if ((i + 1) % column == 0) {//第三列
//画子view底部横线
canvas.drawLine(cellView.getLeft(), cellView.getBottom(), cellView.getRight(), cellView.getBottom(), localPaint);
canvas.drawLine(cellView.getRight(), cellView.getTop(), cellView.getRight(), cellView.getBottom(), localPaint);
} else if ((i + 1) > (childCount - (childCount % column))) {//如果view是最后一行
//画子view的右边竖线
canvas.drawLine(cellView.getRight(), cellView.getTop(), cellView.getRight(), cellView.getBottom(), localPaint);
canvas.drawLine(cellView.getLeft(), cellView.getBottom(), cellView.getRight(), cellView.getBottom(), localPaint);
} else {//如果view不是最后一行
//画子view的右边竖线
canvas.drawLine(cellView.getRight(), cellView.getTop(), cellView.getRight(), cellView.getBottom(), localPaint);
//画子view的底部横线
canvas.drawLine(cellView.getLeft(), cellView.getBottom(), cellView.getRight(), cellView.getBottom(), localPaint);
}
}
}</span>
其实以上方法有一定弊端,每次绘制横线或者竖线的时候需要占据子View1像素的空间,因为1像素不是太大,所以可以忽略。
这种自定义ViewGroup的原理其实就是封装View成一个控件,并实现一定功能。
下面是XML布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/phone_type"
android:layout_width="@dimen/verify_button_width"
android:layout_height="@dimen/common_et_height"
android:layout_alignParentLeft="true"
android:background="@drawable/corners_left_login_bg"
android:gravity="center"
android:text="+86"
android:textColor="@color/white"
android:textSize="16sp" />
<EditText
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="@dimen/common_et_height"
android:layout_alignBottom="@id/phone_type"
android:layout_alignTop="@id/phone_type"
android:layout_toRightOf="@id/phone_type"
android:background="@drawable/corners_right_login_bg"
android:hint="@string/input_phone"
android:numeric="integer"
android:paddingLeft="10dp"
android:singleLine="true"
android:textColor="@color/black"
android:textSize="@dimen/text_size_small" />
<RelativeLayout
android:id="@+id/tmp_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/et_phone"
android:layout_marginTop="15dp"
android:background="@drawable/shape_edit_tv_rec_cor_bor" >
<Button
android:id="@+id/tv_confirm"
style="@style/OrangeDialogButtonStyle"
android:layout_width="108dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:gravity="center"
android:paddingBottom="3dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="3dp"
android:text="@string/confirm" />
<EditText
android:id="@+id/et_confirm_code"
android:layout_width="match_parent"
android:layout_height="@dimen/common_et_height"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/tv_confirm"
android:background="@drawable/shape_edit_tv_rec_cor_bor"
android:hint="@string/input_confirmation_code"
android:numeric="integer"
android:paddingLeft="10dp"
android:singleLine="true"
android:textSize="@dimen/text_size_small"
android:windowSoftInputMode="stateHidden|adjustPan" />
</RelativeLayout>
<TextView
android:id="@+id/tv_get_verify_again"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tmp_layout"
android:layout_centerHorizontal="true"
android:layout_marginTop="18dp"
android:text="@string/get_verify_agin"
android:textColor="@color/edit_hint_gray"
android:textSize="@dimen/text_size_minimal" />
<Button
android:id="@+id/tv_confirm_login"
android:layout_width="match_parent"
android:layout_height="@dimen/common_et_height"
android:layout_below="@id/tv_get_verify_again"
android:layout_marginTop="@dimen/phone_confirm_2_top"
android:background="@drawable/logi_btn_gray_bg"
android:minHeight="40dp"
android:text="@string/confirm_login"
android:textColor="@color/white"
android:textSize="18sp" />
</RelativeLayout>
然后开发者根据以上布局,实现一定功能,并对两个Button定义点击回调接口,一个自定义控件便实现了。
以上两个实例其实并没有涉及到之前说的测量、布局以及事件分发的过程,属于自定义ViewGroup的展示类初级实例。下周会稍微深入的介绍下。