metrics.top, 500, metrics.top, paint);
// bottom
paint.setColor(Color.GREEN);
canvas.drawLine(0, metrics.bottom, 500, metrics.bottom, paint);
首先是在画布的(600,200)处画上文字,为了方便观察(600,200)在文字的什么部位,我在(600,200)处画了一个半径3像素的圆圈。然后平移画布到(600,200)的地方然后依次画出了文字的边框图以及FontMetrics信息里的top、ascent、descent、bottom信息
我把运行结果截图做了处理,方便大家看
文字的各个边界
从结果看(600,200)那个蓝色的点并不是在文字的左上角,而是左下角,这个点所在的y坐标即是大家常说的BaseLine的位置,那现在这个函数Canvas.drawText( String text, float x, float y, Paint paint)就可以理解为——将文字的基准点放在(x,y)处,那么这个基准点可以改变吗?答案是肯定的,可以通过绘制文字的画笔的setTextAlign(Align align)方法设置为Paint.Align.CENTER或者Paint.Align.RIGHT,如果不设置的话默认是Paint.Align.LEFT。读者朋友们有兴趣的话可以试试设置成CENTER之后(600,200)的蓝圈圈是不是跑到了文字的中部呢?从上图我们也可以看出,整个文字是介于FontMetrics.top和FontMetrics.bottom之间。
好了,贴上文字居中的代码,相信认真看上边那段话的朋友一定能轻松读懂
- private void drawCenterText(Canvas canvas, Paint textPaint, String text) {
- Rect rect = new Rect(0, 0, mWidth, mHeight);
- textPaint.setTextAlign(Paint.Align.CENTER);
-
- Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
- //文字框最高点距离baseline的距离(负数)
- float top = fontMetrics.top;
- //文字框最低点距离baseline的距离(正数)
- float bottom = fontMetrics.bottom;
-
- int centerY = (int) (rect.centerY() - top / 2 - bottom / 2);
-
- canvas.drawText(text, rect.centerX(), centerY, textPaint);
- }
分析好上边的代码 我们就能绘制出一个静态的小球了,动画既然要动,肯定就像汽车一样需要一个"引擎",在上面说到的绘制波浪路径的函数中我们忽略了getActionPath(float percent)的参数percent,这个参数即是当前动画的进度,那么我们如何来制造这个进度呢?需要怎样把这个动画“引擎”点燃呢。我们可以通过各种手段计时,生成一个计时Thread或者自己写一个Handler等等,只要能均匀的生成进度即可。
本文中用到一个巧妙的定时器ValueAnimator 大家常说的属性动画ObjectAnimator就是它的一个子类,使用它来作为动画的引擎再方便不过了,从字面翻译"ValueAnimator"那就是“值动画者”直译虽然low但是恰恰更好理解,就是让数值动起来,从什么值动到什么值呢?
- ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
这句话就是定义一个值从0变化到1的一个animator,我们的percent值就是从0变化到1的中间过程值,那么怎么得到这个过程值呢?——监听器!对!
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float percent = animation.getAnimatedFraction();
- }
- });
那么数值从0变到1需要多久呢?怎么能无限重复呢?重复的时候是重头开始还是反转进行呢?别急下面三句话就是让动画无限重复,每次从头开始,一个周期1000毫秒
- animator.setDuration(1000);
- animator.setRepeatCount(ValueAnimator.INFINITE);
- animator.setRepeatMode(ValueAnimator.RESTART);
好了,引擎设置好了,发动
animator.start();
上效果
鬼畜版
WTF!这是什么鬼,为什么鬼畜地慢几拍?
打印出来横坐标看看
- 07-09 18:18:47.308 E/Jcs: getActionPath: -21
- 07-09 18:18:47.326 E/Jcs: getActionPath: -15
- 07-09 18:18:47.342 E/Jcs: getActionPath: -10
- 07-09 18:18:47.359 E/Jcs: getActionPath: -5
- 07-09 18:18:47.375 E/Jcs: getActionPath: -2
- 07-09 18:18:47.392 E/Jcs: getActionPath: 0
- 07-09 18:18:47.409 E/Jcs: getActionPath: 0
最后几拍的数值差好像不太对呀!拍拍脑门突然一想,我的动画不均匀是忘记设置一个均匀的插值器了!哎!
- animator.setInterpolator(new LinearInterpolator());
补上一个线性插值器,整个世界都顺畅了
百度Loading小球Github源码
三、结语
第一次写文章,不免有些疏漏之处,望多多指教!后续我会不定期更新新的内容,争取把写文章当成自己生活的一部分。
后记(2017年7月27日15:02:39)
有不少读者问到关于小球和边缘锯齿的问题,我分别用如下方式实现loading小球
1、Canvas的clip方式限制波浪边界(本文提到的方法)
2、使用Xfermode方式限制波浪和圆形的边界
3、用Xfermode方式限制白色文字,用shader方式限制圆形的边界
下边是效果预览图,代码已经提交到github上了,讲解部分尽快补到此文中
三种方式对比