在Android中,控件可以分为ViewGroup控件与View控件。自定义View控件,我之前的文章已经说过。这次我们主要说一下自定义ViewGroup控件。ViewGroup是作为父控件可以包含多个View控件,并管理其中包含的View控件。

一般自定义ViewGroup的流程如下:

  • onMeasure()
  • onLayout()

    我们一般不需要像自定义View一样重写onDraw(),这里需要多写一个onLayout来摆放子View的位置。除了onLayout方法之外,我们还需要确定LayoutParams,这个是子View告诉父布局的一些参数信息,比如MarginLayoutParams,则表明该ViewGroup支持margin,当然这个也可以没有。

    下面我们通过一个例子来说明简单的自定义ViewGroup

一个简单的例子

这个例子,我们将写一个ViewGroup,该ViewGroup中4个button排成一列。这个例子主要说明onMeasure和onLayout的写法。

首先我们新建一个MyViewGroup继承ViewGroup,然后重写onMeasure方法。

   @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
measureChildren(widthMeasureSpec,heightMeasureSpec); }

这个重写非常的简单,调用父类的测量方法,然后测量所有的子控件的,只要子控件不是wrap_content都会测量精准。这里为了简单,没有去考虑wrap_content的情况,后面我们完善的时候会说道。

然后重写onLayout()方法

  @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int height = 0;
int count = getChildCount();
View child;
Log.e("ri", count + "");
for(int i = 0 ;i < count;i++) {
child = getChildAt(i);
child.layout(0, height, child.getMeasuredWidth(),height + child.getMeasuredHeight());
height += child.getMeasuredHeight(); }
}

这里的代码很好理解,因为我们要实现4个button一列显示,然后每个子View的宽度是一样的,并且每个子View的left和right是一样的。所以每个子View只有top和bottom不一样。我们首先定义个高度height初始为0,然后得到所有子View的个数,依次设置每个子View的top和bottom。top就是定义的height,bottom则为height加上子View的高度。设置完后height累加。

xml中布局如下:

 <com.example.byhieg.viewpratice.MyViewGroup android:layout_height="match_parent"
android:layout_width="match_parent"> <Button
android:text="1"
android:layout_width="50dp"
android:layout_height="80dp" />
<Button
android:text="2"
android:layout_width="50dp"
android:layout_height="80dp" />
<Button
android:text="3"
android:layout_width="50dp"
android:layout_height="80dp" />
<Button
android:text="4"
android:layout_width="50dp"
android:layout_height="80dp" />
</com.example.byhieg.viewpratice.MyViewGroup>

效果如下:



下面,我们继续完善这个控件,让他适应wrap_content。这里,我们主要重写onMeasure()

   @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
measureChildren(widthMeasureSpec,heightMeasureSpec);
//开始处理wrap_content,如果一个子元素都没有,就设置为0
if (getChildCount() == 0) {
setMeasuredDimension(0,0);
} else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
//ViewGroup,宽,高都是wrap_content,根据我们的需求,宽度是子控件的宽度,高度则是所有子控件的总和
View childOne = getChildAt(0);
int childWidth = childOne.getMeasuredWidth();
int childHeight = childOne.getMeasuredHeight();
setMeasuredDimension(childWidth, childHeight * getChildCount());
} else if (widthMode == MeasureSpec.AT_MOST) {
//ViewGroup的宽度为wrap_content,则高度不需要管,宽度还是自控件的宽度
View childOne = getChildAt(0);
int childWidth = childOne.getMeasuredWidth();
setMeasuredDimension(childWidth,heightSize);
} else if (heightMode == MeasureSpec.AT_MOST) {
//ViewGroup的高度为wrap_content,则宽度不需要管,高度为子View的高度和
View childOne = getChildAt(0);
int childHeight = childOne.getMeasuredHeight();
setMeasuredDimension(widthSize, childHeight * getChildCount());
}
}

主要通过注释,就可以很明白wrap_content的情况下,如何计算viewGroup的高度和宽度。在viewGroup的onMeasure,我们是不需要在这里面考虑每一个View的宽高,这个通过measureChildren来通知每一个子View自己测量的。我们只需要考虑viewGroup的宽高在自适应的情况下,该是多大。

LayoutParams

在上面这个简单的ViewGroup中,我们是没有设置margin的,也就是说,即使我们在子View中设置了margin也是没有效的。我们需要修改我们的自定义ViewGroup来适应margin的情况。这里我们为了简化情况,只设定第一个button有一个android:layout_margin="30dp"的属性。

这里,我们修改onMeasure方法,让viewGroup的宽度变为原来的宽度加上margin的宽度,高度也是原来的高度加上margin的高度。代码如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec); MarginLayoutParams params = null;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
measureChildren(widthMeasureSpec,heightMeasureSpec); //开始处理wrap_content,如果一个子元素都没有,就设置为0
if (getChildCount() == 0) {
setMeasuredDimension(0,0);
} else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
//ViewGroup,宽,高都是wrap_content,根据我们的需求,宽度是子控件的宽度,高度则是所有子控件的总和
View childOne = getChildAt(0);
params = (MarginLayoutParams) childOne.getLayoutParams();
int childWidth = childOne.getMeasuredWidth();
int childHeight = childOne.getMeasuredHeight();
setMeasuredDimension(childWidth + params.leftMargin + params.rightMargin,
childHeight * getChildCount() + params.topMargin + params.bottomMargin); } else if (widthMode == MeasureSpec.AT_MOST) {
//ViewGroup的宽度为wrap_content,则高度不需要管,宽度还是自控件的宽度
View childOne = getChildAt(0);
params = (MarginLayoutParams) childOne.getLayoutParams();
int childWidth = childOne.getMeasuredWidth();
setMeasuredDimension(childWidth + params.leftMargin + params.rightMargin,heightSize);
} else if (heightMode == MeasureSpec.AT_MOST) {
//ViewGroup的高度为wrap_content,则宽度不需要管,高度为子View的高度和
View childOne = getChildAt(0);
params = (MarginLayoutParams) childOne.getLayoutParams();
int childHeight = childOne.getMeasuredHeight();
setMeasuredDimension(widthSize, childHeight * getChildCount() + params.topMargin + params.bottomMargin);
}
}

这里,注意这个语句params = (MarginLayoutParams) childOne.getLayoutParams(); 如果不重写layoutParams相关的代码,这样直接转换会出现问题。所以,我们需要重写如下代码:让他返回MarginLayoutParams类型的对象

@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);
}

同样,我们已经测量得到了viewGroup的宽和高,接下来,需要对添加了margin的view,重新摆放。主要的摆放规则,左边的坐标为Leftmargin,第一个view的上面的坐标为topMargin,同时,第二个view的上面的坐标要加上bottomMargin。这个只是一个简单的例子来说明放入margin之后要怎么考虑,一般不会这么具体到只计算第一个view的Margin。代码看看就好

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int height = 0;
int count = getChildCount();
View child;
Log.e("ri", count + "");
child = getChildAt(0);
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
int c1 = params.leftMargin;
int c2 = params.topMargin;
int c3 = c1 + child.getMeasuredWidth();
int c4 = c2 + child.getMeasuredHeight();
child.layout(c1,c2, c3,c4);
height = c4 + params.bottomMargin;
for(int i = 1 ;i < count;i++) {
child = getChildAt(i);
child.layout(c1, height, c3, height + child.getMeasuredHeight());
height += child.getMeasuredHeight();
}
}

总结

这是自定义ViewGroup的第一篇文章,自定义ViewGroup是比自定义View知识点更多,而且应用也更广泛。这篇只是简单的介绍了自定义ViewGroup中需要复写的方法,但是一般的viewGroup不会是这样的。上面的东西,用一个LinearLayout布局实现,然后include该布局更方便。一般viewGroup都需要我们来实现view事件的分发以及滑动的处理,接下来的文章,讲介绍滑动的多种方式。

简单的例子了解自定义ViewGroup(一)的更多相关文章

  1. [安卓] 18、一个简单的例子做自定义动画按钮和自定义Actionbar

    在做安卓UI的时候有时候需自定义具有动画效果的按钮或需要自定义一下actionbar~ 本节用一个简单的demo讲如何自定义具有动画效果的按钮,以及个性化的actionbar 下面是效果: 其中: △ ...

  2. android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu

    示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这里我简单说明一下用自定义ViewGroup来实现. 实现方法:我们自定义一个ViewGroup实现左右滑动, ...

  3. android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu[转]

    http://blog.csdn.net/jj120522/article/details/8095852 示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这 ...

  4. Jsp2.0自定义标签(第一天)——一个简单的例子

    今天是学习自定义标签的第一天 Jsp2.0以来,自定义标签的实现比传统标签的实现容易了很多,一般只要extends类SimpleSupport重写doTag()方法即可. 先看最简单的例子,输出一个H ...

  5. 简单的自定义ViewGroup

    自定义ViewGroup需要重写onMeasure, onLayout等方法.下面是一个实例,4个View分别显示在四个角. public class MyGroup extends ViewGrou ...

  6. android 手把手教您自定义ViewGroup(一)

    1.概述 在写代码之前,我必须得问几个问题: 1.ViewGroup的职责是啥? ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属 ...

  7. Android之自定义ViewGroup

    概述 在写代码之前,我必须得问几个问题: 1.ViewGroup的职责是啥? ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性, ...

  8. Android ViewDragHelper完全解析 自定义ViewGroup神器

    Android ViewDragHelper完全解析 自定义ViewGroup神器   转载请标明出处: http://blog.csdn.net/lmj623565791/article/detai ...

  9. Kotlin 第一弹:自定义 ViewGroup 实现流式标签控件

    古人学问无遗力, 少壮工夫老始成.纸上得来终觉浅, 绝知此事要躬行. – 陆游 <冬夜读书示子聿> 上周 Google I/O 大会的召开,宣布了 Kotlin 语言正式成为了官方开发语言 ...

随机推荐

  1. 在jekyll模板博客中添加网易云模块

    最近使用GitHub Pages + Jekyll 搭建了个人博客,作为一名重度音乐患者,博客里面可以不配图,但是不能不配音乐啊. 遂在博客里面引入了网易云模块,这里要感谢网易云的分享机制,对开发者非 ...

  2. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  3. java设计模式之--单例模式

    前言:最近看完<java多线程编程核心技术>一书后,对第六章的单例模式和多线程这章颇有兴趣,我知道我看完书还是记不住多少的,写篇博客记录自己所学的只是还是很有必要的,学习贵在坚持. 单例模 ...

  4. JQuery的基础和应用

    <参考文档>   1.什么是?    DOM的作用:提供了一种动态的操作HTML元素的方法.    jQuery是一个优秀的js库.用来操作HTML元素的工具.    jQuery和DOM ...

  5. svnserver hook python

    在使用中可能会遇到的错误排除 :1.Error: svn: 解析"D:\www\test"出错,或svn: E020024: Error resolving case of 'D: ...

  6. 端盘子的服务生到月薪一万五的IT精英,你能相信吗

    一直以来,我都觉得自己不是一个有故事的人. 以前的我,是个乖宝宝,对父母言听计从,特别内向,甚至一度感觉到自卑.不上学之后,我干过送货员,去工地除泥搬砖,当过油漆工,去过工厂,还去饭店当过端盘子的服务 ...

  7. 写个Fragment方便的抽象基类 BaseFragment

    package com.zb.zhihuianyang.base; import android.app.Activity; import android.os.Bundle; import andr ...

  8. CYQ.Data V5 从入门到放弃ORM系列:教程 - MProc类使用

    MProc介绍 MProc:是一个用于执行SQL或存储过程的数据库操作类,它轻量高性能地类似于Dapper. MProc:它出现的场景很少,因为MAction自身就能处理掉90%-100%的数据操作( ...

  9. 如玫瑰一般的PHP与C#混合编程

    故事背景是这样的,有一套项目,服务器端是用C#写的,为了完成某种事情,它需要使用到一个组件,这个组件很小但很重要,很不巧的是,这个这个组件是用PHP语言写的,如果为了使用这个组件而专门搭建一个PHP的 ...

  10. ASP.NET跨平台最佳实践

    前言 八年的坚持敌不过领导的固执,最终还是不得不阔别已经成为我第二语言的C#,转战Java阵营.有过短暂的失落和迷茫,但技术转型真的没有想象中那么难.回头审视,其实单从语言本身来看,C#确实比Java ...