Android 自定义 View进阶 - Shader

不知曾几何时,渐变色变得流行起来了,各大手机厂商都发布了各自的渐变色手机,同时越来越多的 App ,也开始应用了渐变色的设计。为了满足工作需要,我们也要学习下 Android 中的渐变着色器 Shader。

效果图

Android 自定义 View进阶 - Shader

Shader 概念

/**
 * Shader is the based class for objects that return horizontal spans of colors
 * during drawing. A subclass of Shader is installed in a Paint calling
 * paint.setShader(shader). After that any object (other than a bitmap) that is
 * drawn with that paint will get its color(s) from the shader.
 */
 
着色器是在绘制过程中返回水平颜色范围的对象的基类。 Shader的子类安装在Paint中,
调用paint.setShader(着色器)。 之后,使用该绘制绘制的任何对象(位图除外)
都将从着色器中获取其颜色

Shader 类 有五个子类,也是我们平时主要使用的类:
Android 自定义 View进阶 - Shader

ComposeShader

/**
 * 组合着色器,它通过 Xfermode 将两个着色器组合起来。
 */
public class ComposeShaderView extends View {

    private static final String TAG = "ComposeShaderView";
    private Paint paint;
    private int mViewWidth;
    private int mViewHeight;
    private ComposeShader composeShader;
    private Bitmap bitmap;
    private float centerX;
    private float centerY;
    private int radius;
    private int imgResId;
    private RectF rectF;
    private float value;
    private BitmapShader bitmapShader;
    private LinearGradient linearGradient;
    private Matrix gradientMatrix;
    private ValueAnimator valueAnimator;

    public ComposeShaderView(Context context) {
        super(context);
        init();
    }

    public ComposeShaderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        readAttrs(context, attrs);
        init();
    }

    private void readAttrs(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ComposeShaderView);
        imgResId = typedArray.getResourceId(R.styleable.ComposeShaderView_imgSrc, 0);
        if (imgResId == 0) {
            throw new IllegalArgumentException("the image resource can't be null");
        }
        typedArray.recycle();
    }

    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        bitmap = BitmapFactory.decodeResource(getResources(), imgResId);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMeasuredSize(widthMeasureSpec, bitmap.getWidth());
        int height = getMeasuredSize(heightMeasureSpec, bitmap.getHeight());
        setMeasuredDimension(width, height);
    }

    private int getMeasuredSize(int measureSpec, int defSize) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        int value;
        if (mode == MeasureSpec.EXACTLY) {
            value = size;
        } else {
            value = defSize;
        }
        return value;
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mViewWidth = w;
        mViewHeight = h;

        centerX = mViewWidth / 2f;
        centerY = mViewHeight / 2f;
        radius = Math.min(mViewWidth, mViewHeight) / 2;
        value = -mViewWidth;

        rectF = new RectF(0, 0, mViewWidth, mViewHeight);

        // 缩放 bitmap 对象,宽高和 控件宽高一致
        Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, mViewWidth, mViewHeight, false);
        // 创建 BitmapShader
        bitmapShader = new BitmapShader(scaledBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        // 创建 LinearGradient 线性渐变
        linearGradient = new LinearGradient(0, 0, mViewWidth, mViewHeight,
                new int[]{0x22000000, 0xee333333, 0x22000000},
                new float[]{0.3f, 0.5f, 0.7f},
                Shader.TileMode.CLAMP);
        // 混合渲染 将两个效果叠加,使用PorterDuff叠加模式
//        composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);

        gradientMatrix = new Matrix();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int saveCount = canvas.saveLayer(0, 0, mViewWidth, mViewHeight, paint, Canvas.ALL_SAVE_FLAG);
        if (linearGradient != null) {
            gradientMatrix.setTranslate(value, 0);
            // 为着色器设置矩阵
            linearGradient.setLocalMatrix(gradientMatrix);
            // 混合渲染 将两个效果叠加,使用PorterDuff叠加模式
            composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
            paint.setShader(composeShader);
            canvas.drawRect(rectF, paint);
            paint.setXfermode(null);
        }
        canvas.restoreToCount(saveCount);
    }

    public void startAnimator() {
        valueAnimator = ValueAnimator.ofFloat(-mViewWidth, mViewWidth);
        valueAnimator.setDuration(1300);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                value = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.start();
    }


    /**
     * 停止动画
     */
    public void stopAnimator() {
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator = null;
        }
    }
}