Android 自定義 View進階 - Shader

不知曾幾何時,漸變色變得流行起來了,各大手機廠商都發布了各自的漸變色手機,同時越來越多的 App ,也開始應用了漸變色的設計。爲了滿足工作需要,我們也要學習下 Android 中的漸變着色器 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 類 有五個子類,也是我們平時主要使用的類:
在這裏插入圖片描述

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;
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章