文章目录
1、渲染
Shader—着色器
BitmapShader–位图渲染
/**
* 位图渲染,BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY)
* Bitmap:构造shader使用的bitmap
* tileX:X轴方向的TileMode
* tileY:Y轴方向的TileMode
*/
/**
* TileMode.CLAMP 拉伸最后一个像素去铺满剩下的地方
* TileMode.MIRROR 通过镜像翻转铺满剩下的地方。
* TileMode.REPEAT 重复图片平铺整个画面(电脑设置壁纸)
* 在图片和显示区域大小不符的情况进行扩充渲染
*/
// BitmapShader bitMapShader = new BitmapShader(mBitMap, Shader.TileMode.CLAMP,
// Shader.TileMode.CLAMP);
// BitmapShader bitMapShader = new BitmapShader(mBitMap, Shader.TileMode.MIRROR,
// Shader.TileMode.MIRROR);
BitmapShader bitMapShader = new BitmapShader(mBitMap, Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
mPaint.setShader(bitMapShader);
mPaint.setAntiAlias(true);
canvas.drawRect(0, 0, 800, 1800, mPaint);
3种效果如下显示:
LinearGradient–线性渲染
/**线性渲染
* x0, y0, 起始点
* x1, y1, 结束点
* int[] mColors, 中间依次要出现的几个颜色
* float[] positions 位置数组,position的取值范围[0,1],作用是指定几个颜色分别放置在那个位置上,
* 如果传null,渐变就线性变化。
* tile 用于指定控件区域大于指定的渐变区域时,空白区域的颜色填充方法
*/
LinearGradient linearGradient = new LinearGradient( 0, 0,800, 800,
mColors, null, Shader.TileMode.CLAMP);
// linearGradient = new LinearGradient(0, 0, 400, 400, mColors, null, Shader.TileMode.REPEAT);
mPaint.setShader(linearGradient);
canvas.drawRect(0, 0, 800, 800, mPaint);
RadialGradient—环形渲染
/**
* 环形渲染
* centerX ,centerY:shader的中心坐标,开始渐变的坐标
* radius:渐变的半径
* centerColor,edgeColor:中心点渐变颜色,边界的渐变颜色
* colors:渐变颜色数组
* stops:渐变位置数组,类似扫描渐变的positions数组,取值[0,1],中心点为0,半径到达位置为1.0f
* tileMode:shader未覆盖以外的填充模式
*/
RadialGradient mRadialGradient = new RadialGradient(300, 300, 100,
mColors, null, Shader.TileMode.REPEAT);
mPaint.setShader(mRadialGradient);
canvas.drawCircle(300, 300, 300, mPaint);
SweepGradient—扫描渲染
/**
* 扫描渲染
* cx,cy 渐变中心坐标
* color0,color1:渐变开始结束颜色
* colors,positions:类似LinearGradient,用于多颜色渐变,positions为null时,根据颜色线性渐变
*/
SweepGradient mSweepGradient = new SweepGradient(300, 300, mColors, null);
mPaint.setShader(mSweepGradient);
canvas.drawCircle(300, 300, 300, mPaint);
ComposeShader—组合渲染
/**
* 组合渲染,
* ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, Xfermode mode)
* ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, PorterDuff.Mode mode)
* shaderA,shaderB:要混合的两种shader
* Xfermode mode: 组合两种shader颜色的模式
* PorterDuff.Mode mode: 组合两种shader颜色的模式
*/
/***************用ComposeShader即可实现心形图渐变效果*********************************/
//创建BitmapShader,用以绘制心
Bitmap mBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.heart)).getBitmap();
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//创建LinearGradient,用以产生从左上角到右下角的颜色渐变效果
LinearGradient linearGradient = new LinearGradient(0, 0, mWidth, mHeight,
Color.GREEN, Color.BLUE, Shader.TileMode.CLAMP);
//bitmapShader对应目标像素,linearGradient对应源像素,像素颜色混合采用MULTIPLY模式
ComposeShader composeShader = new ComposeShader(linearGradient, bitmapShader, PorterDuff.Mode.MULTIPLY);
// ComposeShader composeShader2 = new ComposeShader(composeShader, linearGradient, PorterDuff.Mode.MULTIPLY);
//将组合的composeShader作为画笔paint绘图所使用的shader
mPaint.setShader(composeShader);
//用composeShader绘制矩形区域
canvas.drawRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), mPaint);
放大镜效果
/**
* 放大镜效果
*/
public class ZoomImageView extends View {
//放大倍数
private static final int FACTOR = 2;
//放大镜的半径
private static final int RADIUS = 100;
// 原图
private Bitmap mBitmap;
// 放大后的图
private Bitmap mBitmapScale;
// 制作的圆形的图片(放大的局部),盖在Canvas上面
private ShapeDrawable mShapeDrawable;
private Matrix mMatrix;
public ZoomImageView(Context context) {
super(context);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.xyjy3);
mBitmapScale = mBitmap;
//放大后的整个图片
mBitmapScale = Bitmap.createScaledBitmap(mBitmapScale,mBitmapScale.getWidth() * FACTOR,
mBitmapScale.getHeight() * FACTOR,true);
BitmapShader bitmapShader = new BitmapShader(mBitmapScale, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
mShapeDrawable = new ShapeDrawable(new OvalShape());
mShapeDrawable.getPaint().setShader(bitmapShader);
// 切出矩形区域,用来画圆(内切圆)
mShapeDrawable.setBounds(0,0,RADIUS * 2,RADIUS * 2);
mMatrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 1、画原图
canvas.drawBitmap(mBitmap, 0 , 0 , null);
// 2、画放大镜的图
mShapeDrawable.draw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
// 将放大的图片往相反的方向挪动
mMatrix.setTranslate(RADIUS - x * FACTOR, RADIUS - y *FACTOR);
mShapeDrawable.getPaint().getShader().setLocalMatrix(mMatrix);
// 切出手势区域点位置的圆
mShapeDrawable.setBounds(x-RADIUS,y - RADIUS, x + RADIUS, y + RADIUS);
invalidate();
return true;
}
}
2、滤镜
BlurMaskFilter模糊遮罩滤镜
参考:
https://blog.csdn.net/lyz_zyx/article/details/78783956
//关闭单个View的硬件加速功
setLayerType(View.LAYER_TYPE_SOFTWARE,null);
RectF rectF = new RectF(200,100,bitmap.getWidth()+200,bitmap.getHeight());
/**
* @param radius 阴影的半径
* @param style NORMAL -- 整个图像都被模糊掉
* SOLID -- 图像边界外产生一层与Paint颜色一致阴影效果,不影响图像的本身
* OUTER -- 图像边界外产生一层阴影,并且将图像变成透明效果
* INNER -- 在图像内部边沿产生模糊效果
*/
paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.SOLID));
canvas.drawRect(rectF,paint);
4种效果如下:
EmbossMaskFilter浮雕遮罩滤镜
/**
* @param direction 指定光源的位置,长度为xxx的数组标量[x,y,z]
* @param ambient 环境光的因子 (0~1),越接近0,环境光越暗
* @param specular 镜面反射系数 越接近0,镜面反射越强
* @param blurRadius 模糊半径 值越大,模糊效果越明显
*/
paint.setMaskFilter(new EmbossMaskFilter(new float[]{1,1,1},0.5f,1,5 ));
paint.setTextSize(200);
canvas.drawText("Hello World", 50, 200, paint);
颜色矩阵
平移运算—加法
// 平移运算---加法
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1, 0,0,0,0,
0,1,0,0,100,
0,0,1,0,0,
0,0,0,1,0,
});
RectF rectF2 = new RectF(200,100 + bitmap.getHeight(),bitmap.getWidth()+200,bitmap.getHeight() * 2);
paint.setColorFilter(new ColorMatrixColorFilter(colorMartrix));
canvas.drawBitmap(bitmap,null, rectF2,paint);
反相效果 – 底片效果
// 反相效果 -- 底片效果
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
-1, 0,0,0,255,
0,-1,0,0,255,
0,0,-1,0,255,
0,0,0,1,0,
});
缩放运算—乘法 – 颜色增强
// 缩放运算---乘法 -- 颜色增强
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1.5f, 0,0,0,0,
0,1.5f,0,0,0,
0,0,1.5f,0,0,
0,0,0,1.5f,0,
});
黑白照片
/** 黑白照片
*是将我们的三通道变为单通道的灰度模式
*去色原理:只要把R G B 三通道的色彩信息设置成一样,那么图像就会变成灰色,
*同时为了保证图像亮度不变,同一个通道里的R+G+B =1
*/
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
0.213f, 0.715f,0.072f,0,0,
0.213f, 0.715f,0.072f,0,0,
0.213f, 0.715f,0.072f,0,0,
0,0,0,1,0,
});
发色效果
// 发色效果---(比如红色和绿色交换)
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1,0,0,0,0,
0, 0,1,0,0,
0,1,0,0,0,
0,0,0,0.5F,0,
});
复古效果
// 复古效果
ColorMatrix colorMartrix = new ColorMatrix(new float[]{
1/2f,1/2f,1/2f,0,0,
1/3f, 1/3f,1/3f,0,0,
1/4f,1/4f,1/4f,0,0,
0,0,0,1,0,
});
3、xfermode图形组合
参考:
https://developer.android.google.cn/reference/android/graphics/PorterDuff.Mode?hl=en#ADD
public class MyView extends View {
Paint mPaint;
float mItemSize = 0;
float mItemHorizontalOffset = 0;
float mItemVerticalOffset = 0;
float mCircleRadius = 0;
float mRectSize = 0;
int mCircleColor = 0xffffcc44;//黄色
int mRectColor = 0xff66aaff;//蓝色
float mTextSize = 25;
private static final Xfermode[] sModes = {
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
new PorterDuffXfermode(PorterDuff.Mode.SRC),
new PorterDuffXfermode(PorterDuff.Mode.DST),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.XOR),
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
};
private static final String[] sLabels = {
"Clear", "Src", "Dst", "SrcOver",
"DstOver", "SrcIn", "DstIn", "SrcOut",
"DstOut", "SrcATop", "DstATop", "Xor",
"Darken", "Lighten", "Multiply", "Screen"
};
public MyView(Context context) {
super(context);
init(null, 0);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
if(Build.VERSION.SDK_INT >= 11){
setLayerType(LAYER_TYPE_SOFTWARE, null);
}
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(mTextSize);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setStrokeWidth(2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置背景色
// canvas.drawARGB(255, 139, 197, 186);
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
for(int row = 0; row < 4; row++){
for(int column = 0; column < 4; column++){
canvas.save();
int layer = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);
mPaint.setXfermode(null);
int index = row * 4 + column;
float translateX = (mItemSize + mItemHorizontalOffset) * column;
float translateY = (mItemSize + mItemVerticalOffset) * row;
canvas.translate(translateX, translateY);
//画文字
String text = sLabels[index];
mPaint.setColor(Color.BLACK);
float textXOffset = mItemSize / 2;
float textYOffset = mTextSize + (mItemVerticalOffset - mTextSize) / 2;
canvas.drawText(text, textXOffset, textYOffset, mPaint);
canvas.translate(0, mItemVerticalOffset);
//画边框
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(0xff000000);
canvas.drawRect(2, 2, mItemSize - 2, mItemSize - 2, mPaint);
mPaint.setStyle(Paint.Style.FILL);
//画圆
mPaint.setColor(mCircleColor);
float left = mCircleRadius + 3;
float top = mCircleRadius + 3;
canvas.drawCircle(left, top, mCircleRadius, mPaint);
mPaint.setXfermode(sModes[index]);
//画矩形
mPaint.setColor(mRectColor);
float rectRight = mCircleRadius + mRectSize;
float rectBottom = mCircleRadius + mRectSize;
canvas.drawRect(left, top, rectRight, rectBottom, mPaint);
mPaint.setXfermode(null);
//canvas.restore();
canvas.restoreToCount(layer);
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mItemSize = w / 4.5f;
mItemHorizontalOffset = mItemSize / 6;
mItemVerticalOffset = mItemSize * 0.426f;
mCircleRadius = mItemSize / 3;
mRectSize = mItemSize * 0.6f;
}
}
刮刮卡
public class GuaGuaCardView_SRCOUT extends View {
private Paint mBitPaint;
private Bitmap BmpDST,BmpSRC,BmpText;
private Path mPath;
private float mPreX,mPreY;
public GuaGuaCardView_SRCOUT(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mBitPaint = new Paint();
mBitPaint.setColor(Color.RED);
mBitPaint.setStyle(Paint.Style.STROKE);
mBitPaint.setStrokeWidth(45);
BmpText = BitmapFactory.decodeResource(getResources(), R.drawable.guaguaka_text1,null);
BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.guaguaka,null);
BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(BmpText,0,0,mBitPaint);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//先把手指轨迹画到目标Bitmap上
Canvas c = new Canvas(BmpDST);
c.drawPath(mPath,mBitPaint);
//然后把目标图像画到画布上
canvas.drawBitmap(BmpDST,0,0,mBitPaint);
//计算源图像区域
mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
mBitPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.moveTo(event.getX(),event.getY());
mPreX = event.getX();
mPreY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float endX = (mPreX+event.getX())/2;
float endY = (mPreY+event.getY())/2;
mPath.quadTo(mPreX,mPreY,endX,endY);
mPreX = event.getX();
mPreY =event.getY();
break;
case MotionEvent.ACTION_UP:
break;
}
postInvalidate();
return super.onTouchEvent(event);
}
}