概述

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文件中创建和编辑我们自定义控件的实例。

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


定义自定义属性

大部分情况我们的自定义View需要有更多的灵活性,比如我们在xml中指定了颜色大小等属性,在程序运行时候控件就能展示出相应的颜色和大小。所以我们需要自定义属性

class PieChart extends View {//继承View

    public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}//为了支持.xml中进行创建和编辑
}

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

<declare-styleable name="PieChart">
<attr name="showText" format="boolean" />
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>

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

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
<com.example.customviews.charting.PieChart
custom:showText="true"
custom:labelPosition="left" />
</LinearLayout>

使用自定义属性的时候需要指定命名空间,固定写法就是http://schemas.android.com/apk/res/你的包名。如果你是在android studio,也可以用http://schemas.android.com/apk/res/res-auto

获取自定义属性

在xml中设置了控件自定义属性,我们就需要拿到属性做一些事情。否则定义自定义属性就没有意义了。 
固定的获取自定义属性代码如下

public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0); try {
mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
} finally {
a.recycle();
}
}

当我们在 xml中创建了一个view时,所有在xml中声明的属性都会被传入到view的构造方法中的AttributeSet类型的参数当中。 
通过调用Context的obtainStyledAttributes()方法返回一个TypedArray对象。然后直接用TypedArray对象获取自定义属性的值。 
由于TypedArray对象是共享的资源,所以在获取完值之后必须要调用recycle()方法来回收。

添加设置属性事件

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

public boolean isShowText() {
return mShowText;
} public void setShowText(boolean showText) {
mShowText = showText;
invalidate();
requestLayout();
}

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

2、处理View的布局.

测量

一个View是在展示时总是有它的宽和高,测量View就是为了能够让自定义的控件能够根据各种不同的情况以合适的宽高去展示。提到测量就必须要提到onMeasure方法了。onMeasure方法是一个view确定它的宽高的地方。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { }

onMeasure方法里有两个重要的参数, widthMeasureSpec, heightMeasureSpec。在这里你只需要记住它们包含了两个信息:mode和size 
我们可以通过以下代码拿到mode和size

int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

那么获取到的mode和size又代表了什么呢? 
mode代表了我们当前控件的父控件告诉我们控件,你应该按怎样的方式来布局。 
mode有三个可选值:EXACTLY, AT_MOST, UNSPECIFIED。它们的含义是:

EXACTLY:父控件告诉我们子控件了一个确定的大小,你就按这个大小来布局。比如我们指定了确定的dp值和macth_parent的情况。 
AT_MOST:当前控件不能超过一个固定的最大值,一般是wrap_content的情况。 
UNSPECIFIED:当前控件没有限制,要多大就有多大,这种情况很少出现。

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

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

if mode is EXACTLY{
父布局已经告诉了我们当前布局应该是多大的宽高, 所以我们直接返回从measureSpec中获取到的size
}else{
计算出希望的desiredSize
if mode is AT_MOST
返回desireSize和specSize当中的最小值
else:
返回计算出的desireSize
}

上面的代码虽然基本都是固定的,但是需要写的步骤还是有点多,如果你不想自己写,你也可以用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).重写这个方法去处理我们的事件逻辑

 @Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}

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

现在的触控有了更多的手势,比如轻点,快速滑动等等,所以在支持特殊用户交互的时候你需要用到android提供的GestureDetector.你只需要实现GestureDetector中相对应的接口,并且处理相应的回调方法。

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

由于与用户进行交互中涉及到的知识举例子会比较多,所以我在之后的自定义控件文章中再讲解。

5、优化你的自定义View

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

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

1、避免不必要的代码 
2、在onDraw()方法中不应该有会导致垃圾回收的代码。 
3、尽可能少让onDraw()方法调用,大多数onDraw()方法调用都是手动调用了invalidate()的结果,所以如果不是必须,不要调用invalidate()方法。

总结

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

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

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

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

Android自定义控件1的更多相关文章

  1. Android自定义控件之自定义ViewGroup实现标签云

    前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...

  2. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  3. Android自定义控件之自定义属性

    前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性.本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解.有关原理知识请参考Android自定义控 ...

  4. Android自定义控件之基本原理

    前言: 在日常的Android开发中会经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件,今天先大致了解一下自定义控件的要求和实现的基本原理. 自 ...

  5. 一起来学习Android自定义控件1

    概述 Android已经为我们提供了大量的View供我们使用,但是可能有时候这些组件不能满足我们的需求,这时候就需要自定义控件了.自定义控件对于初学者总是感觉是一种复杂的技术.因为里面涉及到的知识点会 ...

  6. [Xamarin.Android] 自定义控件

    [Xamarin.Android] 自定义控件 前言 软件项目开发的过程中,免不了遇到一些无法使用内建控件就能满足的客户需求,例如:时速表.折线图...等等.这时开发人员可以透过自定义控件的方式,为项 ...

  7. android自定义控件实现TextView按下后字体颜色改变

    今天跟大家分享一下Android自定义控件入门,先介绍一个简单的效果TextView,按下改变字体颜色,后期慢慢扩展更强大的功能 直接看图片             第一张是按下后截的图,功能很简单, ...

  8. android 自定义控件(初篇)

    android 自定义控件 在写UI当中很多时候会用到自定义的控件,其实自定义控件就像是定义一个类进行调用就OK了.有些相关的感念可以查看API 下面就用个简单的例子来说明自定义控件: public ...

  9. Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)

    最近一直在学习自定义控件,搜了许多大牛们Blog里分享的小教程,也上GitHub找了一些类似的控件进行学习.发现读起来都不太好懂,就想写这么一篇东西作为学习笔记吧. 一.控件介绍: 进度条在App中非 ...

随机推荐

  1. html/css/js小技巧实例

    一些学习中碰到的小技巧 让div自动撑起来: .clearfix:after{ content: "."; clear: both; display: block; visibil ...

  2. Java魔法堂:解读基于Type Erasure的泛型

    一.前言 还记得JDK1.4时遍历列表的辛酸吗?我可是记忆犹新啊,那时因项目需求我从C#转身到Java的怀抱,然后因JDK1.4少了泛型这样语法糖(还有自动装箱.拆箱),让我受尽苦头啊,不过也反映自己 ...

  3. 开源服务专题之-------rsync数据备份

    RSYNC是Remote Sync 远程同步的简称,与SCP的比较,SCP= 无法备份大量数据,类似windows的复制,而rsync=边复制 ,边统计,边比较,可以备份大量数据.可以镜像保存整个目录 ...

  4. [AngularJS] AngularJS系列(1) 基础篇

    目录 什么是AngularJS? 为什么使用/ng特性 Hello World 内置指令 内置过滤器 模块化开发 一年前开始使用AngularJS(以后简称ng),如今ng已经出2了.虽说2已完全变样 ...

  5. [译]学习IPython进行交互式计算和数据可视化(七)

    第六章:定制IPython 对于高级用户,IPython可以进行定制和扩展.在本章结束之后,你将会知道: 怎样创建和使用自定义配置文件 怎样为高级功能进行IPython扩展 怎样在notebook中使 ...

  6. js中局部变量必须用var去声明

    js中的变量与其他的脚本语言都是很不一样的,在function中你如果不用var 声明一个变量,那么这个变量将在全局可见,也就相当于创建了全局变量.所以在function中声明变量尽量都是用var来声 ...

  7. C#身份证识别相关技术

    最近研究C#相关的OCR技术,图像识别一般C和C++这种底层语言做的比较多,C#主要是依托一些封装好的组件进行调用,这里介绍三种身份证识别的方法. 一:调用大公司API接口,百度.云脉,文通科技都有相 ...

  8. 为什么我不建议你做APP?

    最近迷上了新产品的可行性分析和推演. 有几个朋友也准备跳入创业火坑了,找到我说帮忙做做产品分析和可行性讨论,欣然应允. 我一向厌恶纯凭感觉拍脑袋的方式,所以对于我不了解的行业,都会从行业背景.现状痛点 ...

  9. MEF入门之不求甚解,但力求简单能讲明白(四)

    上一篇我们已经可以获取各种FileHandler的实例和对应的元数据.本篇,我们做一个稍微完整的文件管理器. 1.修改接口IFileHandler,传入文件名 namespace IPart { pu ...

  10. 新平台,新版本,ComponentOne 持续发力

    我们很高兴宣布2016年 V1 版本发布了,可免费下载试用. 今年ComponentOne 将聚焦WinForm.WPF.MVC.UWP平台和核心控件Flex家族. 本次发布主要包括UWP平台:Win ...