1.Shader 着色器
着色器就是用来上色的,可以用来实现一系列的渐变、渲染效果,有5个子类
BitmapShader
位图ShaderLinerGradient
线性ShaderRadialGradient
光束ShaderSweepGradient
梯度ShaderComposeShader
混合Shader
BitmapShader
是唯一个可以用来给一个图片着色,其他四个就是渐变、渲染效果
2.BitmapShader 位图着色器
构造方法:
BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)
构造方法中除了一个Bitmap
外,还需要两个TileMode
枚举类型的参数,一个代表在x
轴的模式,一个在y
轴的模式
2.1 TileMode 瓷砖模式
TileMode是Shader中的一个枚举,有三个值
-
CLAMP
拉伸,图片的最后的一个像素,不断重复 -
REPEAT
重复,横向、纵向不断重复 -
MIRROR
镜像,横向不断翻转重复,纵向不断翻转重复
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得到一个圆形图片
image.png
image.png
这时图片的下面部分已经出现了问题,明显是最后一个元素被拉伸过多,使用这种方式得到圆形图片,拉伸后的图片要大于控件的大小,这样最后一个元素既然被拉伸变形,也看不到
2.2 BitmapShader下三种模式的效果
-
REPEAT
重复
简单修改代码,将CLAMP
变为REPEAT
,图片资源变为R.mipmap.ic_launcher
,画的图形由圆形变为矩形,布局中的宽度改为match_parent
REPEAT.png
-
MIRROR
镜像
上下对称,出现镜像
image.png
上面的情况两个TileMode都是同一个值
BitmapShader总是先应用Y轴上的模式后,再应用X轴上的模式
3 LinearGradient 线性渐变
构造方法,有两个:
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第一种构造方法简单使用:
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轴起始点 -
y0
绘制y轴起始点 -
x1
绘制x轴结束点 -
y1
绘制y轴结束点 -
color0
起始颜色 -
color1
结束颜色 -
tile
瓷砖模式
LinearGradient第1种构造方法.png
效果很容易理解,在控件中从0,0左上角点到600,600右下角两种颜色渐变
3.2第二种构造方法简单使用:
两个构造方法的区别在于,第4参数,是一个int[]
,第5个参数为一个float[]
,使用这个构造方法可以设置多种颜色的渐变
-
colors
颜色int值数组 -
postions
数组中的值有效范围是0f~1f,渐变结束所在区域的比例,1f的结束位置,与x1,y1有关
LinearGradient第2种构造方法.png
三种颜色,在起始点(0,0),二分之一点(300,300),结束点(600,600)的渐变效果
3.3 图片倒影效果:
思路:
- 绘制原图,考虑绘制坐标,图片缩放
- 绘制倒影,利用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);
//垂直翻转
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;
//起始点
x = screenW / 2 - dstBitmap.getWidth() / 2;
y = 0;
//设置渐变矩形
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 光束渐变
两个构造方法:
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);
}
RadialGradient第1种构造方法.png
-
centerX
渐变中心点的X轴坐标 -
centerY
渐变中心点的Y轴坐标 -
radius
渐变区域的半径
控件大小为600 * 600
,(300f,300f)
为控件的中心,半径为300
第二种构造方法简单使用:
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);
}
RadialGradient第2种构造方法.png
colors
中的元素个数要和positions
中的元素个数相等
5. SweepGradient 梯度渐变
两个构造方法:
SweepGradient(float cx, float cy, int color0, int color1)
SweepGradient(float cx, float cy, int colors[], float positions[])
cx,cy
是旋转点的x,y
轴坐标,渐变过程总是顺时针方向旋转
第一种构造方法简单使用:
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
就是整个控件的中心,可以试试其他的值
第二种构造方法简单使用:
image.png
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 混合渐变
两个构造方法:
···
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
···
构造方法中,前两个参数相同,都是需要一个着色器,差别在于第三个参数。第一个构造方法需要一个PorterDuff.Mode
,而第二个构造构造方法需要PorterDuffXfermode
前面的三种的渐变,都是一种单一的渐变,ComposeShader
可以把前面两种渐变混合进一种渐变效果
简单使用:
private void init() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);//关闭硬件加速
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);
}
必须要把硬件加速关闭,ComposeShader
混合渐变效果才会出现,否则屏幕一片空白,手边暂时没有了别的手机,不知道会不会也是这样
第3个参数,new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
和PorterDuff.Mode.SCREEN
这两种写法有啥区别,暂时也没看出来
网友评论