自己定义View步骤



概述

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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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; 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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; 50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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;  50px; text-align: right; border-right-color: rgb(221, 221, 221); border-right- 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自己定义控件是非常有必要的。当你可以轻松的定义出一个完美的自己定义控件的时候,相信你已经是大牛了。

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

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

原文地址:https://www.cnblogs.com/clnchanpin/p/7298708.html