Android之自定义ViewGroup

概述

在写代码之前,我必须得问几个问题:

1、ViewGroup的职责是啥?

ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是为用于告诉容器的),我们的宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等;当然还有margin等;于是乎,ViewGroup的职能为:给childView计算出建议的宽和高和测量模式 ;决定childView的位置;为什么只是建议的宽和高,而不是直接确定呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高。

2、View的职责是啥?

View的职责,根据测量模式和ViewGroup给出的建议的宽和高,计算出自己的宽和高;同时还有个更重要的职责是:在ViewGroup为其指定的区域内绘制自己的形态。

3、ViewGroup和LayoutParams之间的关系?

大家可以回忆一下,当在LinearLayout中写childView的时候,可以写layout_gravity,layout_weight属性;在RelativeLayout中的childView有layout_centerInParent属性,却没有layout_gravity,layout_weight,这是为什么呢?这是因为每个ViewGroup需要指定一个LayoutParams,用于确定支持childView支持哪些属性,比如LinearLayout指定LinearLayout.LayoutParams等。如果大家去看LinearLayout的源码,会发现其内部定义了LinearLayout.LayoutParams,在此类中,你可以发现weight和gravity的身影。

2、View的3种测量模式

上面提到了ViewGroup会为childView指定测量模式,下面简单介绍下三种测量模式:
EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。

注:上面的每一行都有一个一般,意思上述不是绝对的,对于childView的mode的设置还会和ViewGroup的测量mode有一定的关系;当然了,这是第一篇自定义ViewGroup,而且绝大部分情况都是上面的规则,所以为了通俗易懂,暂不深入讨论其他内容。

3、从API角度进行浅析

上面叙述了ViewGroup和View的职责,下面从API角度进行浅析。
View的根据ViewGroup传人的测量值和模式,对自己宽高进行确定(onMeasure中完成),然后在onDraw中完成对自己的绘制。
ViewGroup需要给View传入view的测量值和模式(onMeasure中完成),而且对于此ViewGroup的父布局,自己也需要在onMeasure中完成对自己宽和高的确定。此外,需要在onLayout中完成对其childView的位置的指定。

4、完整的例子

1、决定该ViewGroup的LayoutParams
对于我们这个例子,我们只需要ViewGroup能够支持margin即可,那么我们直接使用系统的MarginLayoutParams

@Overridepublic ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs){return new MarginLayoutParams(getContext(), attrs);}@Overrideprotected ViewGroup.LayoutParams generateDefaultLayoutParams(){Log.e(TAG, "generateDefaultLayoutParams");return new MarginLayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);}@Overrideprotected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p){Log.e(TAG, "generateLayoutParams p");return new MarginLayoutParams(p);}

重写父类的该方法,返回MarginLayoutParams的实例,这样就为我们的ViewGroup指定了其LayoutParams为MarginLayoutParams。

onMeasure

在onMeasure中计算childView的测量值以及模式,以及设置自己的宽和高:

/** * 计算所有ChildView的宽度和高度 然后根据ChildView的计算结果,设置自己的宽和高 */  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  {  /** * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式 */  int widthMode = MeasureSpec.getMode(widthMeasureSpec);  int heightMode = MeasureSpec.getMode(heightMeasureSpec);  int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);  int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);  // 计算出所有的childView的宽和高  measureChildren(widthMeasureSpec, heightMeasureSpec);  /** * 记录如果是wrap_content是设置的宽和高 */  int width = 0;  int height = 0;  int cCount = getChildCount();  int cWidth = 0;  int cHeight = 0;  MarginLayoutParams cParams = null;  // 用于计算左边两个childView的高度  int lHeight = 0;  // 用于计算右边两个childView的高度,最终高度取二者之间大值  int rHeight = 0;  // 用于计算上边两个childView的宽度  int tWidth = 0;  // 用于计算下面两个childiew的宽度,最终宽度取二者之间大值  int bWidth = 0;  /** * 根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是warp_content时 */  for (int i = 0; i < cCount; i++)  {  View childView = getChildAt(i);  cWidth = childView.getMeasuredWidth();  cHeight = childView.getMeasuredHeight();  cParams = (MarginLayoutParams) childView.getLayoutParams();  // 上面两个childView  if (i == 0 || i == 1)  {  tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;  }  if (i == 2 || i == 3)  {  bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;  }  if (i == 0 || i == 2)  {  lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;  }  if (i == 1 || i == 3)  {  rHeight += cHeight + cParams.topMargin + cParams.bottomMargin;  }  }  width = Math.max(tWidth, bWidth);  height = Math.max(lHeight, rHeight);  /** * 如果是wrap_content设置为我们计算的值 * 否则:直接设置为父容器计算的值 */  setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth  : width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight  : height);  }  

0-14行,获取该ViewGroup父容器为其设置的计算模式和尺寸,大多情况下,只要不是wrap_content,父容器都能正确的计算其尺寸。所以我们自己需要计算如果设置为wrap_content时的宽和高,如何计算呢?那就是通过其childView的宽和高来进行计算。

17行,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,此行执行完成后,childView的宽和高都已经正确的计算过了

43-71行,根据childView的宽和高,以及margin,计算ViewGroup在wrap_content时的宽和高。

80-82行,如果宽高属性值为wrap_content,则设置为43-71行中计算的值,否则为其父容器传入的宽和高。

onLayout对其所有childView进行定位(设置childView的绘制区域)

// abstract method in viewgroup  @Override  protected void onLayout(boolean changed, int l, int t, int r, int b)  {  int cCount = getChildCount();  int cWidth = 0;  int cHeight = 0;  MarginLayoutParams cParams = null;  /** * 遍历所有childView根据其宽和高,以及margin进行布局 */  for (int i = 0; i < cCount; i++)  {  View childView = getChildAt(i);  cWidth = childView.getMeasuredWidth();  cHeight = childView.getMeasuredHeight();  cParams = (MarginLayoutParams) childView.getLayoutParams();  int cl = 0, ct = 0, cr = 0, cb = 0;  switch (i)  {  case 0:  cl = cParams.leftMargin;  ct = cParams.topMargin;  break;  case 1:  cl = getWidth() - cWidth - cParams.leftMargin  - cParams.rightMargin;  ct = cParams.topMargin;  break;  case 2:  cl = cParams.leftMargin;  ct = getHeight() - cHeight - cParams.bottomMargin;  break;  case 3:  cl = getWidth() - cWidth - cParams.leftMargin  - cParams.rightMargin;  ct = getHeight() - cHeight - cParams.bottomMargin;  break;  }  cr = cl + cWidth;  cb = cHeight + ct;  childView.layout(cl, ct, cr, cb);  }  }  

参考链接

Android 手把手教您自定义ViewGroup(一) - Hongyang - 博客频道 - CSDN.NET

教你搞定Android自定义ViewGroup - 简书

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/494145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 5.0 CallerMemberName CallerFilePath CallerLineNumber 在.NET4中的使用

C# 5.0 给我们带来了三个非常有用的编译器特性 CallerMemberName CallerFilePath CallerLineNumber 在C与C中由下列字符帮助我们实现调试消息的文件行号 01.#define debug_msg printf("%s[%d]:",__FILE__,__LINE__);printf 在.NET 4中与其功能相等的是 new StackTrac…

智慧城市建设:科技创业的下一个浪潮

来源&#xff1a;资本实验室随着全球城市化进程的加速&#xff0c;越来越多的人涌进城市&#xff0c;这为城市建设带来了一系列的挑战&#xff1a;一方面&#xff0c;城市需要面对大量的越来越老化的基础设施&#xff1b;另一方面&#xff0c;需要为新涌入的城市居民提供新的&a…

Android之ViewDragHelper

在自定义ViewGroup中&#xff0c;很多效果都包含用户手指去拖动其内部的某个View(eg:侧滑菜单等)&#xff0c;针对具体的需要去写好onInterceptTouchEvent和onTouchEvent这两个方法是一件很不容易的事&#xff0c;需要自己去处理&#xff1a;多手指的处理、加速度检测等等。 好…

DARPA人工智能技术研究情况一览

来源&#xff1a;一体化指挥调度国家工程实验室、高端装备发展研究中心摘要&#xff1a;20世纪60年代初&#xff0c;DARPA&#xff08;当时为ARPA&#xff09;开始介入自主技术研究&#xff0c;并很快成为该领域的主要研究机构。DARPA意识到&#xff0c;人工智能可以满足大量的…

ListView滑动删除效果实现

通过继承ListView然后结合PopupWindow实现 首先是布局文件&#xff1a; delete_btn.xml&#xff1a;这里只需要一个Button <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/a…

直接读取硬盘扇区

Linux系统下一切都是文件&#xff0c;可以像使用普通文件一样使用设备&#xff0c;可直接操作设备扇区内容,这种方式不经过文件系统。 #include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#in…

深入“肠-脑”神经高速通道,揭开“第六感觉”面纱

来源&#xff1a;中国生物技术网直觉是什么&#xff1f;通常被描述为超感官的第六感觉&#xff0c;它在英文里直译就是肠道感觉。肠道作为“第二大脑”的事实已经家喻户晓了。如果你曾在重要的演讲前感到心慌恶心&#xff0c;或者在一顿大餐后感到头昏眼花&#xff0c;那就是肠…

移动传感器扫描覆盖

移动传感器扫描覆盖摘要&#xff1a;关于传感器网络中的地址覆盖问题&#xff0c;已经做过很多尝试。他们通常归为两类&#xff0c;全覆盖和栅栏覆盖&#xff0c;统称为静态覆盖。在这篇论文中&#xff0c;我们研究一种新的覆盖方案&#xff0c;扫描覆盖&#xff0c;一种不同于…

Andoird自定义ViewGroup实现竖向引导界面

一般进入APP都有欢迎界面&#xff0c;基本都是水平滚动的&#xff0c;今天和大家分享一个垂直滚动的例子。 先来看看效果把&#xff1a; 首先是布局文件&#xff1a; <com.example.verticallinearlayout.VerticalLinearLayout xmlns:android"http://schemas.android.…

科技|全球首款飞行汽车开始量产!下月开始预售,2023年后或可实现一键打“飞车”...

来源&#xff1a; 世界科技创新论坛飞机与汽车结合的产物真的要来了。在2018全球未来出行大会上&#xff0c;吉利副总裁杨学良透露&#xff0c;由吉利控股的全资子公司、全球首家飞行汽车公司美国太力飞行汽车公司推出的“全球首款量产飞行汽车”——Transition&#xff0c;将于…

Android手势锁实现

最终效果如下 整体思路 a、自定义了一个RelativeLayout(GestureLockViewGroup)在里面会根据传入的每行的个数&#xff0c;生成多个GestureLockView&#xff08;就是上面一个个小圈圈&#xff09;&#xff0c;然后会自动进行布局&#xff0c;里面的宽度&#xff0c;间距&#x…

quartz详解

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目&#xff0c;它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个&#xff0c;百个&#xff0c;甚至是好几万个Jobs这样复杂的日程序表。Jobs可以做成标准的Java组件或 EJBs。…

智能连接:5G与人工智能、物联网等技术的超级融合

来源&#xff1a;资本实验室随着新技术的成熟&#xff0c;新型的、先进的应用将来自5G、人工智能&#xff08;AI&#xff09;和物联网&#xff08;IoT&#xff09;的融合。这种融合将创造出一个智能连接的世界&#xff0c;对所有个人、行业、社会和经济产生积极影响。从现在到2…

一个绚丽的loading动效分析与实现!

最终效果如下 从效果上看&#xff0c;我们需要考虑以下几个问题&#xff1a; 1.叶子的随机产生&#xff1b; 2.叶子随着一条正余弦曲线移动&#xff1b; 3.叶子在移动的时候旋转&#xff0c;旋转方向随机&#xff0c;正时针或逆时针&#xff1b; 4.叶子遇到进度条&#xff…

SQL Server中行列转换 Pivot UnPivot (转载)

SQL Server中行列转换 Pivot UnPivot PIVOT用于将列值旋转为列名&#xff08;即行转列&#xff09;&#xff0c;在SQL Server 2000可以用聚合函数配合CASE语句实现PIVOT的一般语法是&#xff1a;PIVOT(聚合函数(列) FOR 列 in (…) )AS P 完整语法&#xff1a; table_source PI…

20岁的谷歌,和它“最成功”的大败笔

来源&#xff1a;大数据文摘编译&#xff1a;张驰、JIN、涂世文、钱天培谷歌20岁了&#xff01;20年中&#xff0c;谷歌打造了无数或成功或流产的产品&#xff0c;其中&#xff0c;这一名为“谷歌光纤”计划的失败或许是它最“成功”的“大败笔”。2010年&#xff0c;谷歌宣布了…

什么是REST?

REST是Representational State Transfer的简称&#xff0c;表征状态转移。它是一种设计风格。 维基上对其风格的表述为&#xff1a; 资源是由URI来指定。对资源的操作包括获取、创建、修改和删除资源&#xff0c;这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。通…

自定义viewgroup实现ArcMenu

最终效果如下 实现思路 通过效果图&#xff0c;会有几个问题&#xff1a; a、动画效果如何实现 可以看出动画是从顶点外外发射的&#xff0c;可能有人说&#xff0c;那还不简单&#xff0c;默认元素都在定点位置&#xff0c;然后TraslateAnimation就好了&#xff1b;这样忽略…

也谈谈Atiyah关于黎曼猜想的证明

来源&#xff1a;潇轩社作者&#xff1a;叶扬波 著名数学家&#xff0c;美国爱荷华大学教授。作为数论学家&#xff0c;他在中国大陆出版有《迹公式与模形式》等专著。以下是他谈Atiyah关于黎曼猜想的证明的文章&#xff0c;观点专业而且独到&#xff0c;转载此文&#xff0c;…

大型Javascript应用架构的模式(译文)

附上翻译好的word文件 http://files.cnblogs.com/lizhug/Patterns_For_Large-Scale_JavaScript_Application_Architecture.zip 作者&#xff1a;Addy Osmani 技术评审&#xff1a;Andree Hansson 翻译&#xff1a;李珠刚 珠刚参上 今天我们将要探讨一系列用于大型Javascri…