概述

Android已经为我们提供了大量的View供我们使用,可是可能有时候这些组件不能满足我们的需求,这时候就须要自己定义控件了。自己定义控件对于刚開始学习的人总是感觉是一种复杂的技术。

由于里面涉及到的知识点会比較多。

可是不论什么复杂的技术后面都是一点点简单知识的积累。

通过对自己定义控件的学习去能够更深入的掌握android的相关知识点,所以学习android自己定义控件是非常有必要的。

记得曾经学习总是想着去先理解非常多知识点。然后再来学着自己定义控件。可是每次写自己定义控件的时候总是不知道从哪里下手啊。后来在学习的过程中发现自己跟着去写一些简单的自己定义控件,然后在这个过程中遇到了没有掌握的知识点再去学习。

不仅自己定义控件的能力有所提高。其他的知识也有了非常好的巩固和认识。所以,今天写的是怎么去自己定义一个控件。而不是里面涉及到的细化知识点。一个东西我们先知道怎么用,再去问为什么。

自己定义控件须要考虑的点

依据Android Developers官网的介绍。自己定义控件你须要下面的步骤。

(依据你的须要。某些步骤能够省略)

1、创建View

2、处理View的布局

3、绘制View

4、与用户进行交互

5、优化已定义的View

上面列出的五项就是android官方给出的自己定义控件的步骤。每一个步骤里面又包括了非常多细小的知识点。

我们可以记住这五个点,而且了解每一个点里包括的小知识点。再加上一些自己定义控件的练习。不断的将这些知识熟练于心。相信我们每一个人都可以定义出优秀的自己定义控件。接下来我们開始对上面列出的5个要点进行细化讲解

自己定义控件5个要点具体说明

1、创建View

继承View

对Android有一些了解的朋友都知道,android为我们提供的非常多View都是继承与View的。所以我们自己定义的View当然也是继承于View,当然假设你要自己定义的View拥有某些android已经提供的控件的功能,你能够直接继承于已经提供的控件。

我们在使用android提供的控件的时候,我们在.xml文件里编辑了一个控件,在执行的时候就行看到和获得这个控件。我们自己定义的控件当然也要支持配置和一些自己定义属性。所以以下的构造方法就必须有了。

这个构造方法同意我们在.xml文件里创建和编辑我们自己定义控件的实例。

上面说了那么多事实上就是以下一段代码。

<code class="hljs axapta has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;"><span class="hljs-class" style="margin: 0px; padding: 0px; box-sizing: border-box;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">PieChart</span> <span class="hljs-inheritance" style="margin: 0px; padding: 0px; box-sizing: border-box;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">extends</span></span> <span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">View</span> {</span><span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">//继承View</span>

    <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">public</span> PieChart(Context context, AttributeSet attrs) {
<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">super</span>(context, attrs);
}<span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">//为了支持.xml中进行创建和编辑</span>
}</code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li></ul>

定义自己定义属性

大部分情况我们的自己定义View须要有很多其它的灵活性,比方我们在xml中指定了颜色大小等属性,在程序执行时候控件就能展示出对应的颜色和大小。所以我们须要自己定义属性

自己定义属性通常写在在res/values/attrs.xml文件里 以下是自己定义属性的标准写法

<code class="hljs xml has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;"> <span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">declare-styleable</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">name</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"PieChart"</span>></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">attr</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">name</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"showText"</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">format</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"boolean"</span> /></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">attr</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">name</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"labelPosition"</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">format</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"enum"</span>></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">enum</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">name</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"left"</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">value</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"0"</span>/></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">enum</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">name</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"right"</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">value</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"1"</span>/></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">attr</span>></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">declare-styleable</span>></span></code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li></ul>

这段代码声明了两个自己定义属性。showText和labelPosition。它们都是属于styleable PieChart的,为了方便,一般styleable的name和我们自己定义控件的类名一样。自己定义控件定义好了之后就是使用了。

使用代码演示样例

<code class="hljs xml has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;"> <span class="hljs-pi" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"><?xml version="1.0" encoding="utf-8"?

></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">LinearLayout</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">xmlns:android</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"http://schemas.android.com/apk/res/android"</span>
<span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">xmlns:custom</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"http://schemas.android.com/apk/res/com.example.customviews"</span>></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"><<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">com.example.customviews.charting.PieChart
</span> <span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">custom:showText</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"true"</span>
<span class="hljs-attribute" style="margin: 0px; padding: 0px; color: rgb(102, 0, 102); box-sizing: border-box;">custom:labelPosition</span>=<span class="hljs-value" style="margin: 0px; padding: 0px; color: rgb(0, 136, 0); box-sizing: border-box;">"left"</span> /></span>
<span class="hljs-tag" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;"></<span class="hljs-title" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">LinearLayout</span>></span></code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li></ul>

使用自己定义属性的时候须要指定命名空间,固定写法就是http://schemas.android.com/apk/res/你的包名。

假设你是在android studio,也能够用http://schemas.android.com/apk/res/res-auto

获取自己定义属性

在xml中设置了控件自己定义属性,我们就须要拿到属性做一些事情。否则定义自己定义属性就没有意义了。 

固定的获取自己定义属性代码例如以下

<code class="hljs avrasm has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;">public PieChart(Context context, AttributeSet attrs) {
super(context, attrs)<span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
TypedArray a = context<span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.getTheme</span>()<span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.obtainStyledAttributes</span>(
attrs,
R<span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.styleable</span><span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.PieChart</span>,
<span class="hljs-number" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-number" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;">0</span>)<span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">;</span> try {
mShowText = a<span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.getBoolean</span>(R<span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.styleable</span><span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.PieChart</span>_showText, false)<span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
mTextPos = a<span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.getInteger</span>(R<span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.styleable</span><span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.PieChart</span>_labelPosition, <span class="hljs-number" style="margin: 0px; padding: 0px; color: rgb(0, 102, 102); box-sizing: border-box;">0</span>)<span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
} finally {
a<span class="hljs-preprocessor" style="margin: 0px; padding: 0px; color: rgb(68, 68, 68); box-sizing: border-box;">.recycle</span>()<span class="hljs-comment" style="margin: 0px; padding: 0px; color: rgb(136, 0, 0); box-sizing: border-box;">;</span>
}
}</code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">8</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">9</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">10</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">11</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">12</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">13</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">14</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">8</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">9</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">10</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">11</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">12</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">13</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">14</li></ul>

当我们在 xml中创建了一个view时,全部在xml中声明的属性都会被传入到view的构造方法中的AttributeSet类型的參数其中。 

通过调用Context的obtainStyledAttributes()方法返回一个TypedArray对象。然后直接用TypedArray对象获取自己定义属性的值。 

因为TypedArray对象是共享的资源,所以在获取完值之后必需要调用recycle()方法来回收。

加入设置属性事件

在xml中指定的自己定义属性仅仅有在view被初始化的时候可以获取到,有时候我们可能在执行时做一些操作,这样的情况就须要我们为自己定义属性设置getter和setter方法,下面代码展示了自己定义控件暴露的set 和get方法

<code class="hljs java has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="margin: 0px; padding: 0px; box-sizing: border-box;">isShowText</span>() {
<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mShowText;
} <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="margin: 0px; padding: 0px; box-sizing: border-box;">setShowText</span>(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> showText) {
mShowText = showText;
invalidate();
requestLayout();
}</code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">8</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">9</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">8</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">9</li></ul>

重点看setShowText方法,在为mShowText赋值之后。调用了invalidate()和requestLayout()方法,我们自己定义控件的属性发生改变之后,控件的样子也可能发生改变,在这样的情况下就须要调用invalidate()方法让系统去调用view的onDraw()又一次绘制。相同的,控件属性的改变可能导致控件所占的大小和形状发生改变,所以我们须要调用requestLayout()来请求測量获取一个新的布局位置。

2、处理View的布局.

測量

一个View是在展示时总是有它的宽和高,測量View就是为了可以让自己定义的控件可以依据各种不同的情况以合适的宽高去展示。

提到測量就必需要提到onMeasure方法了。onMeasure方法是一个view确定它的宽高的地方。

<code class="hljs java has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;"><span class="hljs-annotation" style="margin: 0px; padding: 0px; color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="margin: 0px; padding: 0px; box-sizing: border-box;">onMeasure</span>(<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">int</span> widthMeasureSpec, <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">int</span> heightMeasureSpec) { }</code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li></ul>

onMeasure方法里有两个重要的參数, widthMeasureSpec, heightMeasureSpec。

在这里你仅仅须要记住它们包括了两个信息:mode和size 

我们能够通过下面代码拿到mode和size

<code class="hljs cs has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">int</span> specMode = MeasureSpec.getMode(measureSpec);
<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">int</span> specSize = MeasureSpec.getSize(measureSpec);
</code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li></ul>

那么获取到的mode和size又代表了什么呢? 

mode代表了我们当前控件的父控件告诉我们控件,你应该按如何的方式来布局。 

mode有三个可选值:EXACTLY, AT_MOST, UNSPECIFIED。它们的含义是:

EXACTLY:父控件告诉我们子控件了一个确定的大小,你就按这个大小来布局。比方我们指定了确定的dp值和macth_parent的情况。 

AT_MOST:当前控件不能超过一个固定的最大值。通常是wrap_content的情况。 

UNSPECIFIED:当前控件没有限制,要多大就有多大,这样的情况非常少出现。

size事实上就是父布局传递过来的一个大小,父布局希望当前布局的大小。

以下是一个重写onMeasure的固定伪代码写法:

<code class="hljs cs has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;"><span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">if</span> mode <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">is</span> EXACTLY{
父布局已经告诉了我们当前布局应该是多大的宽高, 所以我们直接返回从measureSpec中获取到的size
}<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{
计算出希望的desiredSize
<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">if</span> mode <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">is</span> AT_MOST
返回desireSize和specSize其中的最小值
<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">else</span>:
返回计算出的desireSize
}
</code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">8</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">9</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">10</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">6</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">7</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">8</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">9</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">10</li></ul>

上面的代码尽管基本都是固定的。可是须要写的步骤还是有点多。假设你不想自己写,你也能够用android为我们提供的工具方法:resolveSizeAndState,该方法须要传入两个參数:我们測量的大小和父布局希望的大小。它会返回依据各种情况返回正确的大小。

这样我们就能够不须要实现上面的模版,仅仅须要计算出想要的大小然后调用resolveSizeAndState。

之后在做自己定义View的时候我会展示用这种方法来确定view的大小。

计算出height和width之后在onMeasure中别忘记调用setMeasuredDimension()方法。

否则会出现执行时异常。

计算一些自己定义控件须要的值 onSizeChange()

onSizeChange() 方法在view第一次被指定了大小值、或者view的大小发生改变时会被调用。所以一般用来计算一些位置和与view的size有关的值。

3、绘制View(Draw)

一旦自己定义控件被创建而且測量代码写好之后,接下来你就能够实现onDraw()来绘制View了,onDraw方法包括了一个Canvas叫做画布的參数,onDraw()简单来说就两点: 

Canvas决定要去画什么 

Paint决定怎么画

比方。Canvas提供了画线方法,Paint就来决定线的颜色。Canvas提供了画矩形,Paint又能够决定让矩形是空心还是实心。

在onDraw方法中開始绘制之前,你应该让画笔Paint对象的信息初始化完成。这是由于View的又一次绘制是比較频繁的,这就可能多次调用onDraw。所以初始化的代码不应该放在onDraw方法里。

Canvas和Paint提供的非常多方法在本文中就不一一列举了。大家能够自己去查看api,之后的文章中我们也会用到,如今你仅仅须要理解定义的大体步骤,然后再慢慢锻炼加深理解。

4、与用户进行交互

或许某些情况你的自己定义控件不只不过展示一个美丽的内容。还须要支持用户点击,拖动等等操作。这时候我们的自己定义控件就须要做用户交互这一步骤了。

在android系统中最常见的事件就是触摸事件了,它会调用view的onTouchEvent(android.view.MotionEvent).重写这种方法去处理我们的事件逻辑

<code class="hljs java has-numbering" style="background: none; padding: 0px; border-radius: 0px; color: inherit; font-family: 'Source Code Pro', monospace;font-size:undefined; display: block; white-space: pre; -ms-word-wrap: normal; box-sizing: border-box;">  <span class="hljs-annotation" style="margin: 0px; padding: 0px; color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>
<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="margin: 0px; padding: 0px; box-sizing: border-box;">onTouchEvent</span>(MotionEvent event) {
<span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="margin: 0px; padding: 0px; color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onTouchEvent(event);
}
</code><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li></ul><ul class="pre-numbering" style="list-style: none; margin: 0px; padding: 6px 0px 40px; left: 0px; top: 0px; width: 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right-width: 1px; border-right-style: solid; position: absolute; box-sizing: border-box; background-color: rgb(238, 238, 238);"><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">1</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">2</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">3</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">4</li><li style="margin: 0px; padding: 0px 5px; box-sizing: border-box;">5</li></ul>

对与onTouchEvent方法相信大家都有一定了解,假设不了解的话,你就先记住这是处理Touch的地方。

如今的触控有了很多其它的手势。比方轻点。高速滑动等等,所以在支持特殊用户交互的时候你须要用到android提供的GestureDetector.你仅仅须要实现GestureDetector中相相应的接口。而且处理相应的回调方法。

除了手势之外,假设有移动之类的情况我们还须要让滑动的动画显示得比較平滑。动画应该是平滑的開始和结束。而不是突然消失突然開始。在这样的情况下,我们须要用到属性动画 property animation framework

因为与用户进行交互中涉及到的知识举样例会比較多。所以我在之后的自己定义控件文章中再解说。

5、优化你的自己定义View

在上面的步骤结束之后,事实上一个完好的自己定义控件已经出来了。接下来你要做的仅仅是确保自己定义控件执行得流畅,官方的说法是:为了避免你的控件看得来迟缓。确保动画始终保持每秒60帧.

以下是官网给出的优化建议:

1、避免不必要的代码 

2、在onDraw()方法中不应该有会导致垃圾回收的代码。 

3、尽可能少让onDraw()方法调用,大多数onDraw()方法调用都是手动调用了invalidate()的结果,所以假设不是必须,不要调用invalidate()方法。

总结

到这里基本上自己定义控件的大致步骤和可能涉及到的知识点都说完了。

看一张图。

图片基本描写叙述了自己定义控件的大致流程。右边是相相应的流程所涉及到的一些知识点。可以看到自己定义控件包含了非常多android知识。所以学习android自己定义控件是非常有必要的。当你可以轻松的定义出一个完美的自己定义控件的时候,相信你已经是大牛了。

本篇文章仅仅对自己定义控件的步骤进行了大体的介绍。假设你对自己定义的流程还不清楚,请你记住上面所说的步骤。至少也要有个大致的印象。在之后的文章中,我在依照这个步骤去解说一些自己定义控件,用这里列出的步骤去一步步实现我们想要的自己定义控件。

假设文章有错误的地方还请评论指正,看完了文章顺便动手点个赞评论下呗!谢谢。

自己定义View步骤的更多相关文章

  1. [原] Android 自定义View步骤

    例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能 ...

  2. Android 它们的定义View (一)

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 非常Android入门程序员AndroidView.可能都是比較恐 ...

  3. Android 自己定义View (二) 进阶

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24300125 继续自己定义View之旅.前面已经介绍过一个自己定义View的基础 ...

  4. 【android自己定义控件】自己定义View属性

    1.自己定义View的属性 2.在View的构造方法中获得我们自己定义的属性 3.重写onMesure 4.重写onDraw 3这个步骤不是必须,当然了大部分情况下还是须要重写的. 1.自己定义Vie ...

  5. 自己定义View Controller转换动画

    原文链接 : Introduction to Custom View Controller Transitions and Animations 原文作者 : joyce echessa 译文出自 : ...

  6. 自己定义View Layout过程 - 最易懂的自己定义View原理系列(3)

    前言 自己定义View是Android开发人员必须了解的基础 网上有大量关于自己定义View原理的文章.但存在一些问题:内容不全.思路不清晰.无源代码分析.简单问题复杂化等等 今天,我将全面总结自己定 ...

  7. Android自己定义view之measure、layout、draw三大流程

    自己定义view之measure.layout.draw三大流程 一个view要显示出来.须要经过測量.布局和绘制这三个过程,本章就这三个流程具体探讨一下.View的三大流程具体分析起来比較复杂,本文 ...

  8. 安卓自己定义View进阶-Canvas之绘制基本形状

    Canvas之绘制基本形状 作者微博: @GcsSloop [本系列相关文章] 在上一篇自己定义View分类与流程中我们了解自己定义View相关的基本知识,只是,这些东西依然还是理论,并不能拿来(zh ...

  9. 【Android】自己定义View、画家(画布)Canvas与画笔Paint的应用——绘图、涂鸦板app的实现

    利用一个简单的绘图app来说明安卓的图形处理类与自己定义View的应用. 例如以下图,有一个供用户自己随意绘图.涂鸦的app. 这里不做那么花俏了,仅提供黑白两色.但能够改变笔尖的粗细. 实质上这里的 ...

随机推荐

  1. 【Java】SpringBoot入门学习及基本使用

    SpringBoot入门及基本使用 SpringBoot的介绍我就不多说了,核心的就是"约定大于配置",接下来直接上干货吧! 本文的实例: github-LPCloud,欢迎sta ...

  2. django验证码配置与使用

    1.安装django-simple-captcha pip install django-simple-captcha 2.配置settings.py ##加app列表INSTALLED_APPS = ...

  3. 【BZOJ 3771】 3771: Triple (FFT+容斥)

    3771: Triple Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 547  Solved: 307 Description 我们讲一个悲伤的故事. ...

  4. 【洛谷】1972:[SDOI2009]HH的项链【莫队+树状数组】

    P1972 [SDOI2009]HH的项链 题目背景 无 题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含 ...

  5. bzoj 2733 Splay 启发式合并,名次树

    题意:给定一个带点权的无向图,有两种操作: 1.将两个连通分量合并. 2.查询某个连通分量里的第K大点. 题解: 用并查集维护连通关系,一开始建立n棵splay树,然后不断合并,查询. 处理技巧: 1 ...

  6. bzoj 1674: [Usaco2005]Part Acquisition -- dijkstra(堆优化)

    1674: [Usaco2005]Part Acquisition Time Limit: 5 Sec  Memory Limit: 64 MB Description The cows have b ...

  7. unix/linux中如何在vi编辑器中方便的跳转到首行和末行?

    1.跳转到首行,:0/:1 2.跳转到末行,shift +g/G/:$

  8. SQL Server 2008 Windows身份验证改为混合模式身份验证 及修改sa密码

    由于需要,要把SQL2008单一的Windows身份验证改为混合模式身份验证.在此做一备忘. 步骤: 1.用Windows身份验证方式进入SQL2008,在[对象资源管理器]右键击[根目录]:

  9. 函数调用过程中,函数参数的入栈顺序,why?

    C语言函数参数入栈顺序为从右至左.具体原因为:C方式参数入栈顺序(从右至左)的好处就是可以动态变化参数个数.通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底.除非知道参数个数,否则是无法通 ...

  10. Jenkins使用jenkins-cli.jar进行远程调用时出现“ERROR: No such job 'test'”或者权限不够等问题解决(Windows)

    网上最提倡的解决办法是用SSH的key进行登录,但是我发觉Linux上非常容易实现,但是Windows压根不知道在哪里设置. 原文:https://issues.jenkins-ci.org/brow ...