Android 自定義 View 進階 - Xfermode

在 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());
    }
}

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