不知曾幾何時,漸變色變得流行起來了,各大手機廠商都發布了各自的漸變色手機,同時越來越多的 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;
}
}
}