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

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