android仿支付寶螞蟻森林加載動畫效果的兩種方法

一圖勝千言

偷過別人能量的小夥伴都熟悉這個加載效果,下面就講解一下實現過程。

1,自定義view

2,這裏要用到螞蟻森林的圖標,如圖

通過canvas.drawBitmap()畫出圖片。

3,通過PorterDuff.Mode.SRC_IN,給圖片填充想要的顏色。

4,通過ValueAnimator實現往復動畫。

下面從第二步開始講解。有兩種方式可以實現上圖效果,如下:

一,方式一

       該方法中,利用兩張圖片:一張logo圖和一張上半部分有顏色、下半部分白色的圖片,把第二張圖疊加在第一張圖上實現着色效果。

1,繪製圖標

通過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的寬高的一半。

這樣就在屏幕中心繪製出了圖片。

2,繪製填充色

中間的填充色其實也是一張圖片,如圖

2.1,同樣我們需要先獲取到該圖片

srcBmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ant_forest_wave);

2.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);

2.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的各種效果:

3,讓疊加效果動起來

如圖,我們只需要不斷改變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();
    }
}

       

二,方法二

       這種疊加效果也可以利用矩形去裁剪有色的logo圖片來實現,具體如下:

       1,生成目標Bitmap

       根據logo的bitmap-dstBmp,生成一個同樣的bitmap,記爲:srcBmp。

dstBmp = BitmapFactory.decodeResource(getResources(), R.mipmap.heart);

Bitmap.Config config = Bitmap.Config.ARGB_4444;
srcBmp = Bitmap.createBitmap(dstBmp.getWidth(), dstBmp.getHeight(), config);

       2,設置目標Bitmap的顏色

       通過ColorFilter來設置srcBmp的顏色:

Canvas canvas = new Canvas(srcBmp);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColorFilter(new PorterDuffColorFilter(mColor, PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(dstBmp, 0, 0, paint);

       3,生成裁剪矩形

       裁剪矩形是根據當前進度以及圖片高度來算的,如下:

public void setProgress(int progress) {
    float rate = progress/100f;
    // 由於canvas做了居中平移,通過(0.5-rate)*dstHeight來計算高度
    mRect = new RectF(-dstWidth/2, (0.5f-rate)*dstHeight, dstWidth/2, dstHeight/2);
    invalidate();
}

       4,繪製

protected void onDraw(Canvas canvas) {
    canvas.translate(mWidth / 2, mHeight / 2);
    // 畫原圖
    canvas.drawBitmap(dstBmp, -dstWidth, -dstHeight, null);
    // 保存圖層
    int saveCount = canvas.saveLayer(dstRect, mPaint, Canvas.ALL_SAVE_FLAG);
    // 畫裁剪矩形
    canvas.drawRect(mRect, mPaint);
    // 設置疊加模式
    mPaint.setXfermode(mXfermode);
    // 畫目標圖
    canvas.drawBitmap(srcBmp,-dstWidth, -dstHeight, mPaint);
    // 還原畫筆
    mPaint.setXfermode(null);
    // 還原圖層
    canvas.restoreToCount(saveCount);
}

       結果和第一種方法一樣,但這種方式更節省內存,因爲第一張方式使用了原圖3倍的Bitmap,而第二方式使用了原圖2倍的Bitmap。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章