1.Shader 著色器
著色器就是用來上色的,可以用來實(shí)現(xiàn)一系列的漸變诊胞、渲染效果撵孤,有5個子類
BitmapShader
位圖ShaderLinerGradient
線性ShaderRadialGradient
光束ShaderSweepGradient
梯度ShaderComposeShader
混合Shader
BitmapShader
是唯一個可以用來給一個圖片著色邪码,其他四個就是漸變闭专、渲染效果
2.BitmapShader 位圖著色器
構(gòu)造方法:
BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)
構(gòu)造方法中除了一個Bitmap
外影钉,還需要兩個TileMode
枚舉類型的參數(shù),一個代表在x
軸的模式廉赔,一個在y
軸的模式
2.1 TileMode 瓷磚模式
TileMode是Shader中的一個枚舉蜡塌,有三個值
-
CLAMP
拉伸馏艾,圖片的最后的一個像素攒至,不斷重復(fù) -
REPEAT
重復(fù),橫向账忘、縱向不斷重復(fù) -
MIRROR
鏡像溉浙,橫向不斷翻轉(zhuǎn)重復(fù)戳稽,縱向不斷翻轉(zhuǎn)重復(fù)
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
final Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.w);
final BitmapShader shader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
}
/**
* 利用 clmp得到圓形圖片
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float x = getWidth() / 2;
float y = getHeight() / 2;
float radius = Math.min(getWidth(), getHeight()) / 2;
canvas.drawCircle(x, y, radius,mPaint);
}
利用CLMP得到一個圓形圖片
這時圖片的下面部分已經(jīng)出現(xiàn)了問題播赁,明顯是最后一個元素被拉伸過多容为,使用這種方式得到圓形圖片替劈,拉伸后的圖片要大于控件的大小陨献,這樣最后一個元素既然被拉伸變形湿故,也看不到
2.2 BitmapShader下三種模式的效果
-
REPEAT
重復(fù)
簡單修改代碼坛猪,將CLAMP
變?yōu)?code>REPEAT,圖片資源變?yōu)?code>R.mipmap.ic_launcher就斤,畫的圖形由圓形變?yōu)榫匦窝蠡季种械膶挾雀臑?code>match_parent
REPEAT.png
-
MIRROR
鏡像
上下對稱洋魂,出現(xiàn)鏡像
image.png
上面的情況兩個TileMode都是同一個值
BitmapShader總是先應(yīng)用Y軸上的模式后绷旗,再應(yīng)用X軸上的模式
3 LinearGradient 線性漸變
構(gòu)造方法喜鼓,有兩個:
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)
LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)
3.1第一種構(gòu)造方法簡單使用:
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
LinearGradient shader = new LinearGradient(0, 0, 600, 600, Color.CYAN, Color.BLUE, Shader.TileMode.REPEAT);
mPaint.setShader(shader);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,600,600,mPaint);
}
-
x0
繪制x軸起始點(diǎn) -
y0
繪制y軸起始點(diǎn) -
x1
繪制x軸結(jié)束點(diǎn) -
y1
繪制y軸結(jié)束點(diǎn) -
color0
起始顏色 -
color1
結(jié)束顏色 -
tile
瓷磚模式
LinearGradient第1種構(gòu)造方法.png
效果很容易理解,在控件中從0,0左上角點(diǎn)到600,600右下角兩種顏色漸變
3.2第二種構(gòu)造方法簡單使用:
兩個構(gòu)造方法的區(qū)別在于衔肢,第4參數(shù)庄岖,是一個int[]
,第5個參數(shù)為一個float[]
角骤,使用這個構(gòu)造方法可以設(shè)置多種顏色的漸變
-
colors
顏色int值數(shù)組 -
postions
數(shù)組中的值有效范圍是0f~1f隅忿,漸變結(jié)束所在區(qū)域的比例胳赌,1f的結(jié)束位置,與x1,y1有關(guān)
LinearGradient第2種構(gòu)造方法.png
三種顏色挺勿,在起始點(diǎn)(0,0)蚊丐,二分之一點(diǎn)(300,300),結(jié)束點(diǎn)(600,600)的漸變效果
3.3 圖片倒影效果:
思路:
- 繪制原圖呛梆,考慮繪制坐標(biāo)融痛,圖片縮放
- 繪制倒影,利用Matrix
- 繪制漸變層目派,利用PortDuffXfermode和LinearGradient
代碼如下:
public class ReflectView extends View {
private Paint mPaint;
private Bitmap dstBitmap, srcBitmap;
private PorterDuffXfermode xfermode;
private int x, y;
public ReflectView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
//原圖Bitmap
dstBitmap = decodeBitmapFormRes(getResources(), R.drawable.wa,540, 960);
//垂直翻轉(zhuǎn)
Matrix matrix = new Matrix();
matrix.setScale(1f, -1f);
//倒影Bitmap
srcBitmap = Bitmap.createBitmap(dstBitmap, 0, 0, dstBitmap.getWidth(), dstBitmap.getHeight(), matrix, true);
//初始化畫筆
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
//屏幕寬度
int screenW = getResources().getDisplayMetrics().widthPixels;
//起始點(diǎn)
x = screenW / 2 - dstBitmap.getWidth() / 2;
y = 0;
//設(shè)置漸變矩形
mPaint.setShader(new LinearGradient(x, dstBitmap.getHeight(), x, dstBitmap.getHeight() + dstBitmap.getHeight() / 2, 0xDD000000, Color.TRANSPARENT, Shader.TileMode.CLAMP));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制背景
canvas.drawColor(Color.BLACK);
//繪制原圖
canvas.drawBitmap(dstBitmap, x, y, null);
//繪制倒影圖片
canvas.drawBitmap(srcBitmap, x, dstBitmap.getHeight(), null);
mPaint.setXfermode(xfermode);
//繪制漸變層
canvas.drawRect(x, dstBitmap.getHeight(), x + dstBitmap.getWidth(), dstBitmap.getHeight() * 2, mPaint);
mPaint.setXfermode(null);
}
/**
* 測量
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, 300);
} else if (wSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, hSpecSize);
} else if (hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(wSpecSize, 300);
}
}
/**
* 圖片的縮放
*/
private Bitmap decodeBitmapFormRes(Resources resources, int resId, int targetWidth, int targetHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = false;
BitmapFactory.decodeResource(resources, resId, options);
int inSample = calculateInSample(options, targetWidth, targetHeight);
options.inSampleSize = inSample;
return BitmapFactory.decodeResource(resources, resId, options);
}
private int calculateInSample(BitmapFactory.Options options, int targetWidth, int targetHeight) {
if (targetWidth <= 0 || targetHeight <= 0) {
return 1;
}
int inSample = 1;
final int rawWidth = options.outWidth;
final int rawHeight = options.outHeight;
if (rawWidth > targetWidth || rawHeight > targetHeight) {
final int halfWidth = rawWidth / 2;
final int halfHeight = rawHeight / 2;
while ((halfWidth / inSample >= targetWidth) && (halfHeight / inSample >= targetHeight)) {
inSample *= 2;
}
}
return inSample;
}
}
4.RadialGradient 光束漸變
兩個構(gòu)造方法:
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, @NonNull TileMode tileMode)
RadialGradient(float centerX, float centerY, float radius, @NonNull int colors[], @Nullable float stops[], @NonNull TileMode tileMode)
private void init() {
final RadialGradient shader = new RadialGradient(300f,300f,300,Color.RED,Color.YELLOW, Shader.TileMode.CLAMP);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//LinearGradient shader = new LinearGradient(0, 0, 600, 600,colors,positions, Shader.TileMode.REPEAT);
mPaint.setShader(shader);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,600,600,mPaint);
}
-
centerX
漸變中心點(diǎn)的X軸坐標(biāo) -
centerY
漸變中心點(diǎn)的Y軸坐標(biāo) -
radius
漸變區(qū)域的半徑
控件大小為600 * 600
,(300f,300f)
為控件的中心土辩,半徑為300
第二種構(gòu)造方法簡單使用:
private void init() {
final int[] colors = new int[]{Color.YELLOW,Color.BLUE ,Color.RED};
final float[] positions = new float[]{0f, 0.5f ,1f};
final RadialGradient shader = new RadialGradient(300f,300f,300,colors,positions, Shader.TileMode.CLAMP);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(shader);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,600,600,mPaint);
}
colors
中的元素個數(shù)要和positions
中的元素個數(shù)相等
5. SweepGradient 梯度漸變
兩個構(gòu)造方法:
SweepGradient(float cx, float cy, int color0, int color1)
SweepGradient(float cx, float cy, int colors[], float positions[])
cx,cy
是旋轉(zhuǎn)點(diǎn)的x,y
軸坐標(biāo),漸變過程總是順時針方向旋轉(zhuǎn)
第一種構(gòu)造方法簡單使用:
private void init() {
final SweepGradient shader = new SweepGradient(300f,300f,Color.RED,Color.YELLOW);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(shader);
mPaint.setShader(shader);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,600,600,mPaint);
}
控件的大小為
600 * 600,300f * 300f
就是整個控件的中心,可以試試其他的值
第二種構(gòu)造方法簡單使用:
private void init() {
final int[] colors = new int[]{Color.MAGENTA, Color.CYAN,Color.YELLOW,Color.BLUE ,Color.RED};
final float[] positions = new float[]{0f, 0.25f,0.5f ,0.75f,1f};
final SweepGradient shader = new SweepGradient(300f, 300f, colors, positions);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(shader);
mPaint.setShader(shader);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,600,600,mPaint);
}
6. ComposeShader 混合漸變
兩個構(gòu)造方法:
···
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
···
構(gòu)造方法中,前兩個參數(shù)相同补鼻,都是需要一個著色器沪么,差別在于第三個參數(shù)州胳。第一個構(gòu)造方法需要一個PorterDuff.Mode
瓤湘,而第二個構(gòu)造構(gòu)造方法需要PorterDuffXfermode
前面的三種的漸變,都是一種單一的漸變,ComposeShader
可以把前面兩種漸變混合進(jìn)一種漸變效果
簡單使用:
private void init() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);//關(guān)閉硬件加速
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
final LinearGradient linearGradient = new LinearGradient(0, 0, 600, 600,Color.GREEN ,Color.BLUE ,Shader.TileMode.CLAMP);
final RadialGradient radialGradient = new RadialGradient(300f,300f,300,Color.RED,Color.YELLOW, Shader.TileMode.CLAMP);
final ComposeShader shader = new ComposeShader(linearGradient, radialGradient, PorterDuff.Mode.SCREEN);
mPaint.setShader(shader);
}
必須要把硬件加速關(guān)閉抵赢,ComposeShader
混合漸變效果才會出現(xiàn)淡诗,否則屏幕一片空白,手邊暫時沒有了別的手機(jī),不知道會不會也是這樣
第3個參數(shù)杉编,new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
和PorterDuff.Mode.SCREEN
這兩種寫法有啥區(qū)別救军,暫時也沒看出來