在 Android 自定義控件中,Xfermode 是一個難點也是一個重點,我們有必要掌握它。之前學習過,沒有充分理解,今天重新學習了下,完成了一個高亮進度條的 ImageView 控件(逐步優化中)。
效果圖:
實現代碼
/**
* 高亮進度的 imageview
*/
public class HightlightProgressImageView extends AppCompatImageView {
private Paint backgroundPaint;
private Paint circlePaint;
private int radius;
private int width;
private int height;
private int roundCorner;
private Path clipPath;
private ValueAnimator valueAnimator;
public HightlightProgressImageView(Context context) {
super(context);
init();
}
public HightlightProgressImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
backgroundPaint.setColor(getResources().getColor(R.color.translucentGray));
backgroundPaint.setStyle(Paint.Style.FILL);
circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setColor(getResources().getColor(android.R.color.white));
circlePaint.setStyle(Paint.Style.FILL);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
radius = dp2Px(30);
roundCorner = dp2Px(10);
clipPath = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
clipPath.addRoundRect(new RectF(0, 0, width, height), roundCorner, roundCorner, Path.Direction.CCW);
}
/**
* 繪製步驟: 先繪製 圓, 再在圓上繪製灰色背景,繪製灰色背景時,將 Xfermode 設置爲 PorterDuff.Mode.SRC_OUT, 這樣重疊的部分就會變爲透明,顯示出正常的圖片
*
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
// 通過 path, 裁剪 canvas 畫布
// canvas.clipPath(clipPath);
// 繪製圖片
super.onDraw(canvas);
//將繪製操作保存到新的圖層,因爲圖像合成是很昂貴的操作,將用到硬件加速,這裏將圖像合成的處理放到離屏緩存中進行
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), backgroundPaint, Canvas.ALL_SAVE_FLAG);
canvas.save();
// 平移畫布到中心點位置
canvas.translate(getWidth() / 2f, getHeight() / 2f);
canvas.drawCircle(0, 0, value, circlePaint);
canvas.restore();
backgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
// 在圖片上層繪製背景
canvas.drawPaint(backgroundPaint);
backgroundPaint.setXfermode(null);
canvas.restoreToCount(saveCount);
}
/**
* 開啓動畫
*/
public void start() {
startAnimator();
}
/**
* 停止動畫
*/
public void stop() {
if (valueAnimator != null) {
valueAnimator.cancel();
valueAnimator = null;
}
}
private float value;
private void startAnimator() {
if (valueAnimator == null) {
valueAnimator = ValueAnimator.ofFloat(radius, width > height ? width : height);
valueAnimator.setDuration(3000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
value = (float) animation.getAnimatedValue();
invalidate();
}
});
}
valueAnimator.start();
}
private int dp2Px(int value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
}
}