一图胜千言
偷过别人能量的小伙伴都熟悉这个加载效果,下面就讲解一下实现过程。
1,自定义view
2,这里要用到蚂蚁森林的图标,如图
通过canvas.drawBitmap()画出图片。
3,通过PorterDuff.Mode.SRC_IN,给图片填充想要的颜色。
4,通过ValueAnimator实现往复动画。
下面从第二步开始讲解。
一,绘制图标
通过BitmapFactory.decodeResource()获取到资源文件中的图片资源,并获取图片宽高,后面要用到
dstBmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ant_forest);
// 图片宽度的一半
dstWidth = dstBmp.getWidth() / 2;
// 图片高度的一半
dstHeight = dstBmp.getHeight() / 2;
然后通过canvas.drawBitmap()绘制图片
// 画背景色
canvas.drawColor(Color.parseColor("#01507d"));
// 坐标原点移动到屏幕中心
canvas.translate(mWidth / 2, mHeight / 2);
// 绘制图片
canvas.drawBitmap(dstBmp, -dstWidth, -dstHeight, mPaint);
其中dstWidth和dstHeight分别是dstBmp的宽高的一半。
这样就在屏幕中心绘制出了图片。
二,绘制填充色
中间的填充色其实也是一张图片,如图
1,同样我们需要先获取到该图片
srcBmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ant_forest_wave);
2,并根据上面的dstWidth和dstHeight生成srcRect,也就是srcBmp的显示范围
srcRect = new RectF(-dstWidth, -dstHeight, dstWidth, 3 * dstHeight);
这里为什么Rect的bottom是3*dstHeight? 如图。
红色框以屏幕为中心,宽高分别是2*dstWidth和2*dstHeight,所以绿色框的范围应该是-dstWidth, -dstHeight, dstWidth, 3 * dstHeight。
然后需要根据srcRect绘制srcBmp
canvas.drawBitmap(srcBmp, null, srcRect, mPaint);
3,这两个图片都绘制出来了,我们就需要把他们叠加一下,通过PorterDuff.Mode.SRC_IN方式
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
// 保存当前图层
int saveCount = canvas.saveLayer(dstRect, mPaint, Canvas.ALL_SAVE_FLAG);
// 绘制蚂蚁森林的图片
canvas.drawBitmap(dstBmp, -dstWidth, -dstHeight, mPaint);
mPaint.setXfermode(mXfermode);
// 绘制填充物图片
canvas.drawBitmap(srcBmp, null, srcRect, mPaint);
mPaint.setXfermode(null);
// 恢复上次保存的图层
canvas.restoreToCount(saveCount);
这样就产生了叠加效果。
PorterDuff.Mode的各种效果:
三,让叠加效果动起来
如图,我们只需要不断改变srcBmp的显示区域,定时刷新界面就可以实现加载的动画效果了。
这里用了ValueAnimator从0到2*dstHeight,改变srcRect,然后刷新界面。
if (valueAnimator == null) {
valueAnimator = ValueAnimator.ofFloat(0.0f, 2 * dstHeight);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
valueAnimator.setDuration(2000);
valueAnimator.setStartDelay(0);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
srcRect = new RectF(-dstWidth, -dstHeight - value, dstWidth, 3 * dstHeight - value);
AliLoadingView.this.invalidate();
}
});
valueAnimator.start();
}
最后别忘了在onDetachedFromWindow()中取消valueAnimator,防止内存泄漏
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (valueAnimator != null) {
valueAnimator.cancel();
}
}