之前对自定义View总是有很多的畏惧,不知道从什么地方入手,近期感觉不能再这样一直使用别人的代码了,要试着自己去真正的了解一下Android的底层了。所以买来任教主的《Android开发艺术探索》,仔细一读还真有收获。
现在记录下来,为跟我一样的新手提供一些学习路上的帮助。
在我的理解,自定义View分为以下的几类:
- 继承View重写onDraw方法,一般相对来说比较简单。
- 继承ViewGroup派生出特殊的Layout,这里可以做的更加复杂。
- 继承自特定的View来实现的特定的功能
对于第一种,相对来说要比较简单,一般就是画个圆画个方块之类的,然后复杂一点可能就要加上动画;第二种,可以实现更加复杂的动画效果,动画之间可能会有一些比较复杂的关联之类的;最后一种,继承自某一个View,例如继承自一个Edittext
,我们可以自己加入一些对EditText中内容的特殊处理。
今天只说最简单的一种:继承View并实现动画,以一个加载等待控件为例,实现的效果如下
STEP 1 编写一个类继承自View,并重写构造方法
- 一般在构造方法中,我们会抽取出一个init()方法来进行对View的初始化工作,例如初始化画笔Paint、路径Path等等,总之哪些只有在第一次创建View的时候才会构建的参数都要在这里来初始化。
- 一般会重写三个构造方法,这三个构造方法有不同的参数,其实是代表了View不同的初始化方式,具体如下:
public LoadingPopPoint(Context context) { super(context); //这个是在java代码中构建的时候调用 }
public LoadingPopPoint(Context context, AttributeSet attrs) { super(context, attrs); //这个是在使用xml构建的时候并且没有指定style的时候调用 }
public LoadingPopPoint(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //这是指定了style的时候调用的方法 }
一般都会在构造方法中调用init来保证初始化的完成
STEP 2 重写onDraw方法,在onDraw方法中使用canvas绘图
使用在init方法中初始化的paint对象,来绘制图形,其实动画的原理就和老式的电影放映是一样的,一帧一帧地刷新就好,其实就是在onDraw方法中不停的隔一段时间就重画一下。这时就要postInvalidateDelayed(50)
这个方法登场了,这个方法会每隔指定的时间来调用View的invalidate()
方法,最终会重新调用onDraw
方法,完成一个周期,所以如果想控制动画,我们就可以定义一个全局的progress
变量,在onDraw方法中不断的递增,然后绘制图形的时候根据这个progress来做出相应的调整,图形不就动起来了吗?
好了,上代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < pointNumber; i++) {
progress[i] = progress[i] + STEP;
radius = getRadiusByProgress(progress[i]);
canvas.drawCircle((float) (getWidth() / 2 + x), getHeight() / 2, radius, paints.get(i));
}
//redraw the view per 40 milliseconds ,and show the animation
postInvalidateDelayed(40);
}
这里的这个getRadiusByProgress
方法中做了一些数学的计算,计算了应该在什么地方画圆,以及这个圆的半径之类的信息,而这里正是这个View的核心部分,真心感觉数学很重要啊。。。。这个算法想了好半天才想明白。。。
其实那些比较炫酷的View动画的实现,后面都有很强大的算法在做支撑,总结起来其实就是:
一个好的View动画=强大的算法+对Android系统API的熟练应用
个人感觉这个是新手入门的比较不错的例子,好了,代码是最好的老师,有什么问题看代码咯 GitHub地址