27
2017
09

自定义View从入门到懵逼系列(下)

传送门

系列文章传送门

自定义View从入门到懵逼系列(上)
自定义View从入门到懵逼系列(下)

GitHub自定义View的Demo

这篇是笔记中的下篇,包含的主要内容有:Paint类、绘制顺序、属性动画、自定义View的分类和流程概述、事件的分发、滑动冲突。


1. Paint类(画笔)

我们在上篇中着重讲了绘制类Canvas的一些操作属性,我们知道单单有画布是远远不够的,还有重要的就是我们需要一个画笔用来在画布上绘制东西,所以Paint类就是Android的画笔。

除了最普通的操作对画笔上色调用setColor、setAGB这些操作,画笔对颜色的处理还有其他两个重要操作,Shader着色器和ColorFilter。

1.1 颜色-Shader着色器

5种:LinearGradient(线性渐变)、RadialGradient(辐射渐变)、SweepGradient(扫描渐变)、BitmapShader、ComposeShader(混合着色器)。

1.2 颜色-ColorFilter过滤器

LightingColorFilter,对各个色值进行调节;
ColorMatrixColorFilter,设置饱和度等。

1.3 Xfermode

其实就是要你以绘制的内容作为源图像,以 View 中已有的内容作为目标图像,选取一个 PorterDuff.Mode 作为绘制内容的颜色处理方案,离屏缓存。

1.4 效果

抗锯齿、填充风格、宽度、线头形状、拐角形状等属性。

1.5 色彩优化

抖动、双线性过滤

1.6 轮廓-PathEffect

CornerPathEffect,所有拐角变成圆角;
DiscretePathEffect,线条进行随机的偏离;
DashPathEffect,虚线来绘制线条;
PathDashPathEffect,使用一个 Path 来绘制「虚线」;
SumPathEffect,组合分别绘制;
ComposePathEffect,合并绘制;

1.7 阴影

setShadowLayer、setMaskFilter


2. 绘制顺序

这里写图片描述

这里写图片描述


3. 属性动画Property Animation

3.1 简介

动画包含Animation、Transition两种;
Animation包含视图动画ViewAnimation和属性动画PropertyAnimation;
Transition是过渡,一般不用;
我们99%的场合用的都是PropertyAnimation属性动画,属性动画分为两种,ViewPropertyAnimator和ObjectPropertyAnimator。

3.2 ViewPropertyAnimator

用法直接拿View.animate().xxx使用即可,但是仅限于以下的这些方法,不可自定义。

这里写图片描述

3.3 ObjectPropertyAnimator

ObjectPropertyAnimator比ViewPropertyAnimator高级,它不仅可以使用提供的api,还能根据自己的需求自定义View的属性,一般分为三步。

1.如果是自定义控件,需要添加 setter / getter 方法;
2.用 ObjectAnimator.ofXXX() 创建 ObjectAnimator 对象;
3.用 start() 方法执行动画。

比如我们画一个圆环,百分比进度用float progress来表示,我们就可以用ObjectPropertyAnimator来展示动画。

3.4 插值器Interpolator

有多种插值器。

3.5 多动画执行

PropertyValuesHolder同时执行,AnimatorSet多动画根据设定顺序执行。

3.6 TypeEvaluator

自定义动画的属性,针对特殊属性来做属性动画,比如我们可以将Point来作为属性。


4. 自定义View的分类和流程概述

自定义View应该分为两类:

  • 自定义ViewGroup,一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承自ViewGroup或各种Layout,包含有子View。
  • 自定义View,在没有现成的View,需要自己实现的时候,就使用自定义View,一般继承自View,SurfaceView或其他的View,不包含子View。

4.1 测量View大小-onMeasure()

View的大小不仅由自身所决定,同时也会受到父控件的影响,为了我们的控件能更好的适应各种情况,一般会自己进行测量。

MeasureSpec中的Mode有三种:

  • UNSPECIFIED,子View大小没有任何限制(0, +∞);
  • EXACTLY,父控件明确地指定了子View的大小;
  • AT_MOST,没有具体尺寸,但是最大为父控件大小(0, parentSize)。

4.2 确定View大小-onSizeChanged()

View的大小不仅由View本身控制,而且受父控件的影响,所以我们在确定View大小的时候最好使用系统提供的onSizeChanged回调函数。

4.3 确定子View布局的位置-onLayout()

确定布局的函数是onLayout,它用于确定子View的位置,在自定义ViewGroup中会用到,他调用的是子View的layout函数。

在自定义ViewGroup中,onLayout一般是循环取出子View,然后经过计算得出各个子View位置的坐标值,然后用以下函数设置子View位置。

child.layout(l, t, r, b);

4.4 绘制内容-onDraw()

onDraw是实际绘制的部分,也就是我们真正关心的部分,使用的是Canvas绘图。

这里写图片描述


5. 事件的分发

这里写图片描述

  • 事件分发原理: 责任链模式,事件层层传递,直到被消费;
  • View 的 dispatchTouchEvent 主要用于调度自身的监听器和 onTouchEvent;
  • View的事件的调度顺序是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener ;
  • 不论 View 自身是否注册点击事件,只要 View 是可点击的就会消费事件;
  • 事件是否被消费由返回值决定,true 表示消费,false 表示不消费,与是否使用了事件无关;
  • ViewGroup 中可能有多个 ChildView 时,将事件分配给包含点击位置的 ChildView;
  • ViewGroup 和 ChildView 同时注册了事件监听器(onClick等),由 ChildView 消费;
  • 一次触摸流程中产生事件应被同一 View 消费,全部接收或者全部拒绝;
  • 只要接受 ACTION_DOWN 就意味着接受所有的事件,拒绝 ACTION_DOWN 则不会收到后续内容;
  • 如果当前正在处理的事件被上层 View 拦截,会收到一个 ACTION_CANCEL,后续事件不会再传递过来。

详细参考此文章


6. 滑动冲突

传送门


上一篇:git配置及基础使用 下一篇:ionic2 引入百度ECharts3