最近在 HenCoder 学习了扔物线大神的关于自定义控件的文章。
于是找到我司设计师。
“之前跟你说来不及做的那些效果,现在有空做了”
“好,你打开支付宝看看那个转圈的笑脸加载图吧,我觉得挺不错”
(打开支付宝...瞅...)
“...好吧...我突然想起手里还有点事”
∴做个小练习吧
本人属于愚钝不堪型,懵逼1分钟后,决定还是先把脸画出来,能不能动再说吧。
第一步.画脸
两个点,一个圆弧
private Point leftEye = new Point();
private Point rightEye = new Point();
画圆弧要用的矩形
private RectF arcRectF = new RectF();
view中心点
private Point center = new Point();
开始测量(没有考虑mode,在用的时候直接xml中写死了高宽)
private static final double ANGLE_EYE = 35;// 眼睛Point与center的连线 和 中线的夹角,35度
...
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
initViewSize(width, height);
}
private void initViewSize(int width, int height) {
center.set(width / 2, height / 2);
int strokeWidth = width / 8;//圆弧的宽
paint.setStrokeWidth(strokeWidth);
// 下面开始中二几何计算
int halfStroke = strokeWidth / 2;
float radius = width / 2 - halfStroke;
double cos = Math.cos(Math.PI / 180 * ANGLE_EYE);
int y = (int) (radius * (1 - cos) + halfStroke);
double sin = Math.sin(Math.PI / 180 * ANGLE_EYE);
leftEye.set((int) (radius * (1 - sin) + halfStroke), y);
rightEye.set((int) (radius * (1 + sin) + halfStroke), y);
arcRectF.set(halfStroke, halfStroke, width - halfStroke, height - halfStroke);
}
提笔开画
private void init(Context context) {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(ContextCompat.getColor(context, R.color.green));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
}
@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPoint(leftEye.x, leftEye.y, paint);
canvas.drawPoint(rightEye.x, rightEye.y, paint);
canvas.drawArc(arcRectF, 0, 180, false, paint);//从0度开始,扫过180度.
}
上面代码贴上去,笑脸差不多就出来了 :)

第二步.动起来
怎么笑呢?我瞪大了一双程序猴眼,瞅到流泪。
哎,算了,做不了人家支付宝那么细致了。
肝个差不多的得了。
嗯 我就是这样一个喜欢拖鞋滴仁 :)
于是,我就做了个看起来大概这样子的动画:
- 从 :)开始,)顺时针转动,头转到6点钟位置,头不走了,身子收短到0;
- 化作一个点从9点钟位置开始,顺时针走,走到3点钟位置;
- 点从3点钟位置开始变长,最后变回:)
总共两圈,停一下下,开始重复
效果就是这样:

代码:
首先
来个属性progress,进度,每100表示一圈。
用这个来做动画。
然后呢
这个表示嘴的弧形绘制,依赖两个参数:开始点(.a) & 扫过的角度(swipeAngle)。
顺时针转动时,这个.a其实是尾巴。
∴ 第一段动画,.a是从3点钟位置开始,转到了第二圈的6点钟位置,即 1又4分之一圈,progress就是125。
∴ 动画这样写
Animator animator0 = ObjectAnimator.ofFloat(this, "progress", 125);
animator0.setInterpolator(new LinearInterpolator());
animator0.setDuration(2500);
onDraw里这样
@Override protected void onDraw(Canvas canvas) {
canvas.drawPoint(leftEye.x, leftEye.y, paint);
canvas.drawPoint(rightEye.x, rightEye.y, paint);
float per = getSwipeAnglePercent(progress);
canvas.drawArc(arcRectF, progress * RATIO_ANGLE_PROGRESS, per * 180, false, paint);
}
private float getSwipeAnglePercent(float progress) {
if (progress < 75) {//进度75之前,保持扫过的角度不变
return 1f;
} else {
return (125 - progress) / 50f;//之后扫过的角度匀速变小,125的时候扫过0度
}
}
// 不好意思,尽是magic number,我知道两个周后我自己也看不懂
嘛淡,以后自己看不懂。
画图好了 。

后面两段动画代码都差不多。
只是
加了一个变量 mode,0表示第一段动画,1表示第二,三段动画
加了一个角度变量 float swipeAngle,用来做第三段动画。
其实不太需要,只是自己脑袋比较笨,干脆两种mode分开写。
(度数设为1,来表示点)
然后根据progress的值做判断,是否需要画眼睛。
∴ onDraw就变成了一坨:
@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mode == 0) {
float per = getSwipeAnglePercent(progress);
if (progress < 50) {
canvas.drawPoint(leftEye.x, leftEye.y, paint);
canvas.drawPoint(rightEye.x, rightEye.y, paint);
}
canvas.drawArc(arcRectF, progress * RATIO_ANGLE_PROGRESS, per * 180, false, paint);
} else {
if (270 - progress * RATIO_ANGLE_PROGRESS < ANGLE_EYE) {
canvas.drawPoint(leftEye.x, leftEye.y, paint);
}
if (progress * RATIO_ANGLE_PROGRESS - 270 > ANGLE_EYE) {
canvas.drawPoint(rightEye.x, rightEye.y, paint);
}
canvas.drawArc(arcRectF, progress * RATIO_ANGLE_PROGRESS, swipeAngle * 1, false, paint);
}
}
//哎,尽是magic number ...
第2,3段:
/* 第二段动画:Mode = 1,以点的方式转动半周*/
Animator animator1 = ObjectAnimator.ofFloat(this, "progress", 50, 100);
animator1.setInterpolator(new LinearInterpolator());
animator1.setDuration(DURATION_TIME_1);
/* 第三段动画:Mode = 1,从点变成嘴 :)*/
Animator animator2 = ObjectAnimator.ofFloat(this, "swipeAngle", 180);
animator2.setInterpolator(new LinearInterpolator());
animator2.setDuration(DURATION_TIME_2);
/* 三段动画依次播放,结束时重置动画参数,并再次启动动画*/
animatorSet = new AnimatorSet();
animatorSet.playSequentially(animator0, animator1, animator2);
animatorSet.addListener(animatorSetListener);
完 完整代码
菜猴一只,还望路过的大神们,多多斧正 :)
懒人偶尔也想写博客
网友评论