Android---自定义View

 当 Android SDK 中提供的系统 UI 控件无法满足业务需求时,需要考虑自己实现 UI 控件。掌握自定义控件,是理解整套 Android 渲染体系的基础。自定义 UI 控件有2种方式:

\bullet 继承系统提供的成熟控件(比如 LinearLayout、RelativeLayout、ImageView等)

\bullet 直接继承自系统 View 或者 ViewGroup,并自绘显示内容。

继承现有控件

相对而言,这是一种比较简单的实现方式。因为大部分工作,比如核心控件的测量、控制位置的摆放等相关计算,在系统中都已经实现并封装好了。开发人员只要在其继承上进行扩展,并按照自己的意图显示相关元素。比如下面的代码

CustomToolBar 继承自 RelativeLayout,构造函数中通过 addView() 方式,分别添加两个 ImageView 和 一个 TextView。显示效果如下

自定义属性

想在 XML 布局中使用 CustomToolBar 时,希望能在 XML 文件中直接指定 title 的显示内容,字体颜色,leftImage 和 rightImage 的显示图片等,就需要使用自定义属性

自定义属性步骤:

步骤1:在 attrs.xml 中声明自定义属性

在 res 目录的 values 目录下的 attrs.xml 文件中(如果没有就新建一个),使用 <declare-styleable> 标签自定义属性。

<declare-styleable> 标签代表定义一个自定义属性集合,一般会与自定义控件结合使用。<attr>标签是某一条具体是属性,name 是属性名称,format 代表属性格式。 

在 XML 中使用自定义属性

需要先添加命名空间,然后通过命名空间 app 引入自定义属性。

在 CustomToolBar 中,获取自定义属性的引用值

如上图所示,主要通过 context.obtainStyleAttributes() 方法获取到自定义属性集合。然后从从这个集合中取出相应的自定义属性。

直接继承自 View 或 ViewGroup

使用这种方式可以解决更加复杂的 UI 界面。使用这种实现方式需要解决以下介个问题:

\bullet 如何根据相应的属性将 UI 元素绘制到界面(onDraw 方法解决)

\bullet 自定义控件的大小,也就是宽和高分别设置多少(onMeasure 方法实现)

\bullet 如果是 ViewGroup,如何合理安排其内部子 View 的摆放位置(onLayout)

因此,自定义 View 的重点工作就是复写并合理的实现 onDraw()、onMeasure()、onLayout() 这3个方法。注意:并不是每个自定义 view 都需要同时实现这3个方法。大多数情况下,只需要实现2个或其中一个方法也能满足需求。

onDraw() 方法

onDraw 方法接收一个 Canvas 类型的参数。Canvas 可以理解为一个画布,在这个画布上可以绘制各自类型的 UI 元素。系统提供了一些列 canvas 的操作方法,如下:

 从上图中可以看出,每一个操作方法都需要传入一个 Paint 对象。Paint 为画笔,可以通过设置相关属性,来实现不同的绘制效果,比如绘制图像的颜色、线条的粗细等

实例代码

 定义 PieImageView 继承自 View。在 onDraw() 方法中分别使用 canvas 的 drawArs() 和 drawCircle() 方法来绘制弧度和圆形。

在 xml 中直接使用自定义的控件 PieImageView,并设置宽高。如下图所示

也可在 Activity 中设置 PieImageView 的相关内容 

PieImageView pieImageView = findViewById(R.id.pieImageView);
pieImageView.setProgress(45);

setProgress 为 PieImageView 内定义的方法。

运行显示效果,如下

如果在上面代码中的布局文件中,将 PieImageView 的宽高设置为 wrap_content,重新运行则显示效果:

很显然,PieImageView 并没有正常显示。问题的原因就是,PieImageView 并没在 onMeasure() 方法中重新测量,并重新设置宽高。

onMeasure() 方法

首先,我们需要弄明白自定义 View 为什么需要重新测量。正常情况下,我们直接在 XML 文件中定义好 View 宽高,然后让自定义 View 在此区域内显示即可。但是,为了更好的兼容不同尺寸的屏幕,Android 提供了 wrap_content 和 match_parent 属性来规范控件的显示规则。

wrap_content代表自适应大小match_parent代表填充父视图的大小,但是这两个属性并没有指定具体的大小,因此,需要在 onMeasure() 方法中过滤出这两种情况。真正的测量出自定义 View 应该显示的宽高大小,都是在 onMeasure 方法中完成。方法定义如下

方法传入两个参数 widthMeasureSpec 和 heightMeasureSpec。这两个参数是从父视图传个子 view 的两个参数,看起来很像宽高。但是,它们表示的不仅仅是宽和高,还有一个非常重要的测量模式

3种测量模式:

\bullet EXACTLY:表示在 XML 布局文件中宽高使用 match_parent 或者固定大小的宽高。

\bullet AT_MOST:表示在 XML 布局文件中宽高使用 wrap_content

\bullet UNSPECIFIED:父容器没有对当前 View 有任何限制,当前 View 可以取任意尺寸,比如 ListView 中的 item。

具体值和测量模式都可以通过 Android SDK 中提供的 MeasureSpec 类获取

为什么 widthMeasureSpec/heightMeasureSpec 这种 int 类型数据可以代表 2 种意义呢?

实际上,widthMeasureSpec/heightMeasureSpec 都是使用二进制高2位表示测量模式低30位表示宽高具体大小

重新回到 PieImageView,在 PieImageView 中并没有复写 onMeasure() 方法,因此,默认使用父类(View)中的 onMeasure 方法,代码如下

蓝色框中的 setMeasureDimension 是一个非常重要的方法。这个方法传入的值直接决定了 View 的宽高。也就是说,如果直接调用 setMeasureDimension(100, 200),最终 View 显示的宽100 * 高200 的矩形范围。

getDefaultSize() 返回的是默认大小,默认为父视图的剩余可用空间。这也是 PieImageView 显示异常的原因。虽然我们在 xml 中指定的是 wrap_content,但是实际使用的宽高却是父视图剩余的可用空间。从代码中可用看出是整个屏幕的宽高。

问题原因找到了,解决方法就是复写 onMeasure 方法,过滤出 wrap_content 的情况,并主动调用 setMeasureDimension() 方法设置正确的宽高即可。

ViewGroup 中的 onMeasure

如果我们自定义的控件是一个容器,onMeasure 方法会更加复杂一些。因为 ViewGroup 在测量自己宽高时需要先确定其内部子 view 的大小,然后才能确定自己的大小。比如如下一段代码

LinearLayou 的宽高为 wrap_content,表示由子控件大小确定。而三个子控件的宽度分别为300、200、100,最终LinearLayout 的宽度显示如下

可用看出 LinearLayout 的最终宽度,由其内部最大的子 View 确定。

当我们定义一个 ViewGroup 时,也需要在 onMeasure 方法中综合考虑子 view 的宽度。比如,要实现一个流式布局,效果如下

在大多数 App 搜索界面通常会使用流式布局来展示历史搜索记录或热门搜索事件。FlowLayout 每一行上的 item 个数不确定,当每一行的 item 累计宽度操作了屏幕的总宽度,则需重起一行来放 item 项。

因此,我们需要在 onMeasure 方法中主动分行,计算出 FlowLayout 最终高度。

onLayout()

上面的 onMeasure 方法只是测量出 ViewGroup 的最终显示宽高,但是并没有规定一个子 View 应该显示在何处位置。要定义 ViewGroup 内部子 View 的显示规则,则需要复写并实现 onLayout 方法。ViewGroup 中的 onLayout 方法声明如下:

这是一个抽象方法,也就是说每一个自定义 ViewGroup 都必须主动实现如何排列子 View。具体就是遍历每一个子 View,调用 child的l、t、r、b 方法来为每一个子 View 设置具体的位置。l、t、r、b分别代表左、山、右、下的坐标位置。

总结

介绍了自定义View的几个知识点,要自定义一个控件主要包含几个方法:

● onDraw:主要负责绘制UI元素;

● onMeasure: 主要负责测量自定义控件具体显示的宽高

● onLayout: 主要是在自定义ViewGroup中复写,并实现子View的显示位置
并在其中介绍了自定义属性的使用方法.

 

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

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

相关文章

项目管理软件排行榜:点赞榜TOP5揭晓!

通过项目管理软件企业可以快速、高效地管理项目、整合团队成员以及资源。现如今市场上各类项目管理软件层出不穷&#xff0c;因此选择一款适合自身企业需求的软件显得尤为重要。本文将为大家介绍项目管理软件排行榜点赞榜&#xff0c;为大家选购提供一些参考。 1.Zoho Project…

app查看 证书公钥和md5

获取App的公钥和MD5是一项重要的安全操作&#xff0c;需要谨慎处理。一般情况下&#xff0c;我们无法直接从已安装的App中获取其公钥和MD5信息。如果你是App的开发者&#xff0c;你可以通过以下方式获取&#xff1a; 在你的项目中&#xff0c;找到生成APK的地方&#xff08;一…

C/C++笔试易错与高频题型图解知识点(二)—— C++部分(持续更新中)

目录 1.构造函数初始化列表 1.1 构造函数初始化列表与函数体内初始化区别 1.2 必须在初始化列表初始化的成员 2 引用&引用与指针的区别 2.1 引用初始化以后不能被改变&#xff0c;指针可以改变所指的对象 2.2 引用和指针的区别 3 构造函数与析构函数系列题 3.1构造函数与析…

el-checkbox-group变成竖着的样式

加 style"display: block; padding-top: 10px; margin-left: 27px" <el-checkbox:indeterminate"isIndeterminate"v-model"checkAll"change"handleCheckAllChange">全选&#xff08;{{ memberList.length }}&#xff09;</el…

七大排序 (9000字详解直接插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序)

一&#xff1a;排序的概念及引入 1.1 排序的概念 1.1 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在…

Nginx:反向代理(示意图+配置)

示意图&#xff1a; 反向代理 反向代理&#xff08;Reverse Proxy&#xff09;是代理服务器的一种&#xff0c;它代表服务器接收客户端的请求&#xff0c;并将这些请求转发到适当的服务器。当请求在后端服务器完成之后&#xff0c;反向代理搜集请求的响应并将其传输给客户端。…

基于龙格-库塔优化的BP神经网络(分类应用) - 附代码

基于龙格-库塔优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于龙格-库塔优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.龙格-库塔优化BP神经网络3.1 BP神经网络参数设置3.2 龙格-库塔算法应用 4.测试…

香港高才通通过后要做什么?- 2 缴费

今天缴费系统已经可以使用&#xff0c;登录缴费一下 打开从邮箱下载的确认邮件 打开文件中提供的【入境处网站】链接 单击【缴费和领取电子签证】&#xff0c;单击链接【线上付款】 在付款页面单击【电子签证&#xff0c;线上付款】 单击【开始】 输入档案编号&#xff0c…

【Java基础面试十二】、说一说你对面向对象的理解

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a; 说一说你对面向对象的理…

使用allure如何生成自动化测试报告 ?一文详解allure的使用 。

网上介绍allure报告的很多 &#xff0c;但个人总感觉还是不够整体 &#xff0c;不够详细 &#xff0c;所看到的都是局部 。故本人花了些时间 &#xff0c;将这个allure详细的整理了一遍 。整体且涉及每个细节 。 1.allure介绍 它是一个生成HTML测试报告的工具包 使用java开发…

CSS Vue/RN 背景使用opacity,文字在背景上显示

Vue <div class"training_project_tip"> <div class"tip">展示的文字</div> </div> .training_project_tip { font-size: 12px; font-weight: 400; text-align: left; color: #ffffff; margin-top: 8px; position: relative; dis…

解码自然语言处理之 Transformers

自 2017 年推出以来&#xff0c;Transformer 已成为机器学习领域的一支重要力量&#xff0c;彻底改变了翻译和自动完成服务的功能。 最近&#xff0c;随着 OpenAI 的 ChatGPT、GPT-4 和 Meta 的 LLama 等大型语言模型的出现&#xff0c;Transformer 的受欢迎程度进一步飙升。这…