自定义View(二-番外2-Shader)
From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige
跟着爱哥打天下
自定义控件其实很简单4:
setShader(Shader shader)
Shader有五个子类:
BitmapShader、ComposeShader、LinearGradient、RadialGradient、SweepGradient
BitmapShader:
这五个Shader里最异类的是BitmapShader,因为只有它是允许我们载入一张图片来给图像着色
BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
只有一个含参的构造方法,而其他的四个兄弟姐妹呢都有两个
Shader.TileMode里有三种模式:CLAMP、MIRROR和REPETA
边缘拉伸(先作用Y轴,再作用与X轴)、镜像、重复
public class BrickView extends View {
/**
* 画笔
*/
private Paint mPaint;
/**
* 描边画笔
*/
private Paint mStrokePaint;
/**
* Bitmap着色器
*/
private BitmapShader bitmapShader;
/**
* 触摸点坐标
*/
private float posX, posY;
/**
* 构造器
*/
public BrickView(Context context) {
this(context, null);
}
public BrickView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BrickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
mPaint = new Paint();
/*
* 实例化描边画笔并设置参数
*/
mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mStrokePaint.setColor(0xFF000000);
mStrokePaint.setStyle(Paint.Style.STROKE);
mStrokePaint.setStrokeWidth(5);
//生成BitmapShader
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_round);
bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
mPaint.setShader(bitmapShader);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.DKGRAY);
/*
* 绘制圆和描边
*/
canvas.drawCircle(posX, posY, 300, mPaint);
canvas.drawCircle(posX, posY, 300, mStrokePaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
/*
* 手指移动时获取触摸点坐标并刷新视图
*/
if (event.getAction() == MotionEvent.ACTION_DOWN) {
posX = event.getX();
posY = event.getY();
invalidate();
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
posX = event.getX();
posY = event.getY();
invalidate();
}
return true;
}
}
BitmapShader是从画布的左上方开始着色,设置着色器前为我们的着色器设置了一个变换矩阵,可以更换位置
只是加载了一张图片,然后在XY轴无限重复,圆形的区域为绘制显示的区域(不是在整个屏幕中重复,而是只是在绘制的区域中重复,只不过因为坐标的问题才如此)
LinearGradient:线性渐变
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)
LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile)
前面四个参数也是定义坐标的不扯了colors是一个int型数组,我们用来定义所有渐变的颜色,positions表示的是渐变的相对区域,其取值只有0到1,上面的代码中我们定义了一个[0, 0.1F, 0.5F, 0.7F, 0.8F],意思就是红色到黄色的渐变起点坐标在整个渐变区域(left, top, right, bottom定义了渐变的区域)的起点,而终点则在渐变区域长度 * 10%的地方,而黄色到绿色呢则从渐变区域10%开始到50%的地方以此类推,positions可以为空
public class ShaderView2 extends View {
/**
* 画笔
*/
private Paint mPaint;
/**
* 构造器
*/
public ShaderView2(Context context) {
this(context, null);
}
public ShaderView2(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ShaderView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
mPaint = new Paint();
// mPaint.setShader(new LinearGradient(0, 0, 300, 300, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT));
// mPaint.setShader(new LinearGradient(0, 0, 600, 600, new int[] { Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE }, new float[] { 0, 0.1F, 0.5F, 0.7F, 0.8F }, Shader.TileMode.MIRROR));
mPaint.setShader(new LinearGradient(0, 0, 600, 600, new int[] { Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE }, null, Shader.TileMode.MIRROR));
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, 600, 600, mPaint);
}
}
感觉开始有点跟不上了,得一遍又一遍的梳理代码逻辑:
public class ReflectView extends View {
/**
* 画笔
*/
private Paint mPaint;
/**
* 位图
*/
private Bitmap mSrcBitmap, mRefBitmap;
/**
* 混合模式
*/
private PorterDuffXfermode mXfermode;
/**
* 构造器
*/
public ReflectView(Context context) {
this(context, null);
}
public ReflectView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ReflectView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
//获取源图
mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap4);
//实例化一个矩阵对象
Matrix matrix = new Matrix();
//图像倒过来
matrix.setScale(1F, -1F);
//生成倒影图
mRefBitmap = Bitmap.createBitmap(mSrcBitmap, 0, 0, mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), matrix, true);
mPaint = new Paint();
//设置渐变
mPaint.setShader(new LinearGradient(0, mSrcBitmap.getHeight(), 0, mSrcBitmap.getHeight() + mSrcBitmap.getHeight() / 4, 0xAA000000, Color.TRANSPARENT, Shader.TileMode.CLAMP));
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(mSrcBitmap, 0, 0, null);
//新建一个图层,并在上面进行DST_IN操作,不然有影响
int sc = canvas.saveLayer(0, mSrcBitmap.getHeight(), mRefBitmap.getWidth(), mSrcBitmap.getHeight() * 2, null, Canvas.ALL_SAVE_FLAG);
//紧接下面绘制倒影Bitmap
canvas.drawBitmap(mRefBitmap, 0, mSrcBitmap.getHeight(), null);
//取DST的交集部分
mPaint.setXfermode(mXfermode);
//绘制矩形,渐变开始位置为倒影的开始位置,渐变方向是垂直从上到下,高度为倒影的1/4,渐变模式为边界像素拉伸,也就是透明拉伸,
//取出交集为整个倒影,然后渐变到1/4开始透明
canvas.drawRect(0, mSrcBitmap.getHeight(), mRefBitmap.getWidth(), mSrcBitmap.getHeight() * 2, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
}
SweepGradient:
梯度渐变,也称之为扫描式渐变,因为其效果有点类似雷达的扫描效果,他也有两个构造方法:
SweepGradient(float cx, float cy, int color0, int color1)
public class ShaderView2 extends View {
/**
* 画笔
*/
private Paint mPaint;
/**
* 构造器
*/
public ShaderView2(Context context) {
this(context, null);
}
public ShaderView2(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ShaderView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayerType(LAYER_TYPE_SOFTWARE, null);
initPaint();
}
/**
* 初始化画笔
*/
private void initPaint() {
mPaint = new Paint();
// mPaint.setShader(new LinearGradient(0, 0, 0, 300, Color.RED, Color.TRANSPARENT, Shader.TileMode.CLAMP));
// mPaint.setShader(new LinearGradient(0, 0, 600, 600, new int[] { Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE }, new float[] { 0, 0.1F, 0.5F, 0.7F, 0.8F }, Shader.TileMode.MIRROR));
// mPaint.setShader(new LinearGradient(0, 0, 600, 600, new int[] { Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE }, null, Shader.TileMode.MIRROR));
mPaint.setShader(new SweepGradient(300, 300, Color.RED, Color.YELLOW));
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, 600, 600, mPaint);
}
}
SweepGradient(float cx, float cy, int[] colors, float[] positions)
RadialGradient:
径向渐变,径向渐变说的简单点就是个圆形中心向四周渐变的效果,他也一样有两个构造方法
RadialGradient (float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)
RadialGradient(float centerX, float centerY, float radius, int colors[], float stops[], TileMode tileMode)
在之前的代码上加上shader:
mPaint.setShader(new RadialGradient(293, 440, mBitmap.getHeight() * 7 / 8, Color.TRANSPARENT, Color.BLACK, Shader.TileMode.CLAMP));
但是效果没出来,好像setShader对drawBitmap没什么作用,然后在最后画一个同等大小的矩形,进行渲染。
canvas.drawRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), mPaint);
ComposeShader
就是组合Shader的意思,顾名思义就是两个Shader组合在一起作为一个新Shader
两个构造函数:
ComposeShader (Shader shaderA, Shader shaderB, Xfermode mode)
ComposeShader (Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
PorterDuff.Mode在Xfermode之下,是Xfermode的子类,其余的两个子类已经在API16之后过时了