首页 > 代码库 > 【自定义控件】自定义ViewGroup 在ViewGroup中显示TextView
【自定义控件】自定义ViewGroup 在ViewGroup中显示TextView
需求:在ViewGroup中显示一个TextView
1、继承ViewGroup
必须要实现其构造方法和一个onLayout方法
构造函数的处理:
public CusViewGroup(Context context) {
this(context, null);
}
public CusViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CusViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
注意构造函数中的this。自定义ViewGroup也能够有自己的属性,对于属性的操作和自定义View一致。(在Style中添加自定义属性,在构造函数中获取到layout中设置的自定义属性的值)
onLayout():
疑问:为什么必须要重写onLayout方法?
–自定义ViewGroup 相当于是一个容器,里面能够放置很多View,这些View的位置由onLayout来确定。必须指定位置,才能显示到容器的对应位置上。
疑问:onLayout的几个参数是什么意思
l、t、r、b 是自定义ViewGroup父控件中设置的Padding 值。
2、自定义ViewGroup的执行过程
构造函数—–获取自定义属性
onMeasure—控件宽、高以及measure子控件
onLayout —–控件的位置,子View的位置
onDraw —— 画画咯
onMeasure:
自定义ViewGroup onMeasure代码
/**
* 确定ViewGroup的宽高
*
* @param widthMeasureSpec 宽参数
* @param heightMeasureSpec 高参数
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//ViewGroup主要是一个容器,当ViewGroup的宽高是确切的值的时候,控件的宽高就是它本身设置的值
//主要是考虑ViewGroup Wrap_content的时,需要计算控件的宽高,控件的宽高根据子View的布局来计算
int width;
int height;
int mWidthMeasureMode = MeasureSpec.getMode(widthMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);//初始化所有子View的宽高
if (mWidthMeasureMode == MeasureSpec.AT_MOST) {//Wrap_content的情况
//测量子View的宽 怎么测量子View的宽
//由于这里只有一个控件,暂时从这个一个控件开始学习
View childView = getChildAt(0);//获取到这个控件
width = childView.getMeasuredWidth();
} else {
width = MeasureSpec.getSize(widthMeasureSpec);
}
int mHeightMeasureMode = MeasureSpec.getMode(heightMeasureSpec);
if (mHeightMeasureMode == MeasureSpec.AT_MOST) {
View childView = getChildAt(0);
height = childView.getMeasuredHeight();
} else {
height = MeasureSpec.getSize(heightMeasureSpec);
}
setMeasuredDimension(width, height);
}
测试布局文件:
<com.tjstudy.cusviewgroupdemo.customerview.CusViewGroup
android:layout_width="wrap_content"
android:layout_height="100dp"
android:background="#ccc">
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#FF4444"
android:gravity="center"
android:text="0"
android:textColor="#FFFFFF"
android:textStyle="bold" />
</com.tjstudy.cusviewgroupdemo.customerview.CusViewGroup>
这里只设置了自定义ViewGroup 的宽度wrap_content
效果:
宽度此时就为TextView的宽度 高度还是自定义控件的高度
这个时候,你发现TextView根本没有显示到界面上,进入下一步
onLayout
疑问:onLayout():控件摆放到什么位置?
—onLayout的几个参数是父控件的padding值
疑问:是否能够改变padding值?
—不能,子控件怎么能够设置父控件的属性呢!!
疑问:怎么将TextView显示到界面上?
/**
* 必须实现的方法,自定义ViewGroup的child位置布局
*
* @param changed
* @param l ViewGroup父类 paddingLeft
* @param t paddingTop
* @param r paddingRight
* @param b paddingBottom
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.e(TAG,"paddingLeft="+l+";paddingTop="+t+";paddingRight="+r+";paddingBottom="+b);
//是否能对自定义ViewGroup再进行位置的变换?不能!!!!!
//将TextView显示出来
View childView = getChildAt(0);
childView.layout(0,0,childView.getMeasuredWidth(),childView.getMeasuredHeight());
}
显示结果:
到此就基本完成了ViewGroup的简单示例
Demo下载:
http://download.csdn.net/detail/u012391876/9677845
练习:在ViewGroup中显示2个TextView 并实现指定布局
效果图:
自定义ViewGroup代码:
ViewGroup:
package com.tjstudy.cusviewgroupdemo.customerview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* 自定义ViewGroup 按效果显示两个TextView
*/
public class CusViewGroup extends ViewGroup {
public static final String TAG = "CusViewGroup";
private int childCount;
public CusViewGroup(Context context) {
this(context, null);
}
public CusViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CusViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 必须实现的方法,自定义ViewGroup的child位置布局
*
* @param changed
* @param l ViewGroup父类 paddingLeft
* @param t paddingTop
* @param r paddingRight
* @param b paddingBottom
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int cl = 0;
int ct = 0;
int cr = 0;
int cb = 0;
int h = 0;//由于两个控件垂直放置,需要记录高度
//为每一个子View指定位置 所需效果:查看mipmap里面的效果图
for(int i=0;i<childCount;i++){
View childView = getChildAt(i);
if(i==0){//第一个放到上面的中间
cl = (getMeasuredWidth()-childView.getMeasuredWidth())/2;
ct = 0;
cr = cl+childView.getMeasuredWidth();
cb = ct+childView.getMeasuredHeight();
h = ct+childView.getMeasuredHeight();
}
if(i==1){
cl=0;
ct=h;
cr=cl+childView.getMeasuredWidth();
cb=cb+childView.getMeasuredHeight();
}
childView.layout(cl,ct,cr,cb);
}
}
/**
* 确定ViewGroup的宽高
*
* @param widthMeasureSpec 宽参数
* @param heightMeasureSpec 高参数
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//ViewGroup主要是一个容器,当ViewGroup的宽高是确切的值的时候,控件的宽高就是它本身设置的值
//主要是考虑ViewGroup Wrap_content的时候,此时就需要计算控件的宽高了,控件的宽高就是子View的总的宽高
int width = 0;
int height = 0;
int mWidthMeasureMode = MeasureSpec.getMode(widthMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);//初始化所有子View的宽高
childCount = getChildCount();
if (mWidthMeasureMode == MeasureSpec.AT_MOST) {//Wrap_content的情况
//测量子View的宽 怎么测量子View的宽
//由于是垂直放置 这里需要最大的那个宽度
int maxWidth = 0;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
width = childView.getMeasuredWidth();
maxWidth = (width>maxWidth)?width:maxWidth;
}
width = maxWidth;
} else {
width = MeasureSpec.getSize(widthMeasureSpec);
}
int mHeightMeasureMode = MeasureSpec.getMode(heightMeasureSpec);
if (mHeightMeasureMode == MeasureSpec.AT_MOST) {
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
height += childView.getMeasuredHeight();
}
} else {
height = MeasureSpec.getSize(heightMeasureSpec);
}
setMeasuredDimension(width, height);
}
}
layout:
<?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"
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="com.tjstudy.cusviewgroupdemo.MainActivity">
<com.tjstudy.cusviewgroupdemo.customerview.CusViewGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ccc">
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#ED1C24"
android:gravity="center"
android:text="0"
android:textColor="#FFFFFF"
android:textStyle="bold" />
<TextView
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#00A2E8"
android:gravity="center"
android:text="1"
android:textColor="#FFFFFF"
android:textStyle="bold" />
</com.tjstudy.cusviewgroupdemo.customerview.CusViewGroup>
</LinearLayout>
运行结果:
疑问:对于自定义ViewGroup ,应该还会有margin、padding等的设置,这些设置是否会对ViewGroup或者子控件的位置有影响?
原代码+打印信息打印结果:
设置margin+padding
android:padding="10dp"
android:layout_margin="60dp"
打印信息:
效果图:
效果有变化,但是在自定义ViewGroup中打印的log信息确是相同的
理解:在自定义ViewGroup中,onLayout中默认情况下已经计算了padding的值,margin是自定义控件本身到自定义控件父控件的位置,不会对子View有影响。
Demo下载:
http://download.csdn.net/detail/u012391876/9677964
【自定义控件】自定义ViewGroup 在ViewGroup中显示TextView