当前位置:   article > 正文

Android高级UI进阶之路(四) —— Paint渲染滤镜xfermode使用_setlayertype(view.layer_type_software

setlayertype(view.layer_type_software

系列文章

Android高级UI进阶之路(一) —— View的基础知识

Android高级UI进阶之路(二) —— 深入理解Android8.0 View的触摸事件分发机制

Android高级UI进阶之路(三) —— 理解View的工作原理及自定义View入门

Android高级UI进阶之路(四) —— Paint渲染滤镜xfermode使用

Android高级UI进阶之路(五) —— Canvas详解

Android高级UI进阶之路(六) —— PathMeasure-制作路径动画

Android高级UI进阶之路(七) —— SVG基础使用(绘制中国地图)

前言

在前面三篇文章中我们学习了 “View 基础”、“事件分发机制”、“自定义 View 入门和 View 工作流程” 那么该篇文章将为大家带来自定义 View 中一个必不可少的类 Paint 画笔。有了它可以让我们绘制出来的图形更加绚丽。该篇主要讲解 Paint 高级使用,基础部分我就大概过一下 API。

Paint API

  1. // 重置Paint。
  2. void reset();
  3. // 是否抗锯齿
  4. void setAntiAlias(boolean aa);
  5. // 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
  6. void setDither(boolean dither);
  7. // 设置线性文本
  8. void setLinearText(boolean linearText);
  9. // 设置该项为true,将有助于文本在LCD屏幕上的显示效果
  10. void setSubpixelText(boolean subpixelText);
  11. // 设置下划线
  12. void setUnderlineText(boolean underlineText);
  13. // 设置带有删除线的效果
  14. void setStrikeThruText(boolean strikeThruText);
  15. // 设置伪粗体文本,设置在小字体上效果会非常差
  16. void setFakeBoldText(boolean fakeBoldText);
  17. // 如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作
  18. // 加快显示速度,本设置项依赖于dither和xfermode的设置
  19. void setFilterBitmap(boolean filter);
  20. // 设置画笔风格,空心或者实心 FILL,FILL_OR_STROKE,或STROKE
  21. // Paint.Style.STROKE 表示当前只绘制图形的轮廓,而Paint.Style.FILL表示填充图形。
  22. void setStyle(Style style);
  23. // 设置颜色值
  24. void setColor(int color);
  25. // 设置透明图0~255,要在setColor后面设置才生效
  26. void setAlpha(int a);
  27. // 设置RGB及透明度
  28. void setARGB(int a, int r, int g, int b);
  29. // 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
  30. void setStrokeWidth(float width);
  31. void setStrokeMiter(float miter);
  32. // 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷末端的图形样式
  33. // 如圆形样式Cap.ROUND,或方形样式Cap.SQUARE
  34. void setStrokeCap(Cap cap);
  35. // 设置绘制时各图形的结合方式,如平滑效果等
  36. void setStrokeJoin(Join join);
  37. // 设置图像效果,使用Shader可以绘制出各种渐变效果
  38. Shader setShader(Shader shader);
  39. // 设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果
  40. ColorFilter setColorFilter(ColorFilter filter);
  41. // 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
  42. Xfermode setXfermode(Xfermode xfermode);
  43. // 设置绘制路径的效果,如点画线等
  44. PathEffect setPathEffect(PathEffect effect);
  45. // 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等
  46. MaskFilter setMaskFilter(MaskFilter maskfilter);
  47. // 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
  48. Typeface setTypeface(Typeface typeface);
  49. // 设置光栅化
  50. Rasterizer setRasterizer(Rasterizer rasterizer);
  51. // 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
  52. // 注意:在Android4.0以上默认开启硬件加速,有些图形的阴影无法显示。关闭View的硬件加速 view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  53. void setShadowLayer(float radius, float dx, float dy, int color);
  54. // 设置文本对齐
  55. void setTextAlign(Align align);
  56. // 设置字体大小
  57. void setTextSize(float textSize);
  58. // 设置文本缩放倍数,1.0f为原始
  59. void setTextScaleX(float scaleX);
  60. // 设置斜体文字,skewX为倾斜弧度
  61. void setTextSkewX(float skewX);

Paint 高级使用

渲染

1. 绘制图像阴影效果 setShadowLayer

  1. val paint = Paint()
  2. paint.setStyle(Paint.Style.FILL)
  3. paint.setColor(Color.BLACK)
  4. // 设置透明度,要在setColor后面设置才生效
  5. paint.setAlpha(80)
  6. // 如果不关闭硬件加速,setShadowLayer无效
  7. setLayerType(View.LAYER_TYPE_SOFTWARE, null)
  8. // (阴影的半径,X轴方向上相对主体的位移,Y轴相对位移)
  9. paint.setShadowLayer(50f, 10f, 10f, Color.RED)
  10. paint.setTextSize(50f)
  11. // cx和cy为圆点的坐标
  12. val radius = 200
  13. val offest = 40
  14. val startX = width / 2 - radius
  15. val startY = height / 2
  16. canvas.drawText("画一个圆", width / 2 - 100f, height / 2f - 300, paint)
  17. canvas.drawCircle(startX.toFloat(), startY.toFloat(), radius.toFloat(), paint)
  18. paint.setStyle(Paint.Style.STROKE)
  19. paint.setStrokeWidth(5f)
  20. paint.setShadowLayer(50f, -20f, 10f, Color.RED)
  21. canvas.drawCircle(startX + radius * 2 + offest.toFloat(), startY.toFloat(), radius.toFloat(), paint)

2. 为 Bitmap 设置图形渲染 BitmapShader

  1. class MyGradientView : View {
  2. private var mPaint: Paint? = null
  3. private var mBitMap: Bitmap? = null
  4. private var mWidth: Int = 0
  5. private var mHeight: Int = 0
  6. private val mColors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW)
  7. constructor(context: Context?) : super(context) {
  8. init()
  9. }
  10. constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
  11. init()
  12. }
  13. constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  14. private fun init() {
  15. mBitMap = (resources.getDrawable(R.mipmap.girl_gaitubao) as BitmapDrawable).bitmap
  16. mPaint = Paint()
  17. mWidth = mBitMap!!.getWidth()
  18. mHeight = mBitMap!!.getHeight()
  19. }
  20. override fun onDraw(canvas: Canvas) {
  21. super.onDraw(canvas)
  22. /**
  23. * TileMode.CLAMP 拉伸最后一个像素去铺满剩下的地方
  24. * TileMode.MIRROR 通过镜像翻转铺满剩下的地方。
  25. * TileMode.REPEAT 重复图片平铺整个画面(电脑设置壁纸)
  26. * 在图片和显示区域大小不符的情况进行扩充渲染
  27. */
  28. /**
  29. * 位图渲染,BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY)
  30. * Bitmap:构造shader使用的bitmap
  31. * tileX:X轴方向的TileMode
  32. * tileY:Y轴方向的TileMode
  33. */
  34. val bitMapShader = BitmapShader(
  35. mBitMap!!, Shader.TileMode.MIRROR,
  36. Shader.TileMode.MIRROR
  37. )
  38. //设置图片效果
  39. mPaint!!.setShader(bitMapShader)
  40. //抗锯齿
  41. mPaint!!.setAntiAlias(true)
  42. //绘制圆
  43. canvas.drawCircle(width/2f,height/2f,mHeight.toFloat(),mPaint!!)
  44. }
  45. }

参数的意思我注释很详细,我就不在过多说明了。

3. 线性渲染 LinearGradient

  1. /**线性渲染
  2. * x0, y0, 起始点
  3. * x1, y1, 结束点
  4. * int[] mColors, 中间依次要出现的几个颜色
  5. * float[] positions 位置数组,position的取值范围[0,1],作用是指定几个颜色分别放置在那个位置上,
  6. * 如果传null,渐变就线性变化。
  7. * tile 用于指定控件区域大于指定的渐变区域时,空白区域的颜色填充方法
  8. */
  9. var linearGradient = LinearGradient(
  10. 0f, 0f, 800f, 800f,
  11. mColors, null, Shader.TileMode.CLAMP
  12. )
  13. // var linearGradient = LinearGradient(0f, 0f, 400f, 400f, mColors, null, Shader.TileMode.REPEAT)
  14. mPaint!!.setShader(linearGradient)
  15. canvas.drawRect(0f, 0f, 800f, 800f, mPaint!!)

4. 环形渲染 RadialGradient

  1. /**
  2. *
  3. * 环形渲染
  4. * centerX ,centerY:shader的中心坐标,开始渐变的坐标
  5. * radius:渐变的半径
  6. * centerColor,edgeColor:中心点渐变颜色,边界的渐变颜色
  7. * colors:渐变颜色数组
  8. * stops:渐变位置数组,类似扫描渐变的positions数组,取值[0,1],中心点为0,半径到达位置为1.0f
  9. * tileMode:shader未覆盖以外的填充模式
  10. */
  11. val mRadialGradient = RadialGradient(
  12. width/2f,height/2f,width/3.toFloat(),
  13. mColors, null, Shader.TileMode.REPEAT
  14. )
  15. mPaint!!.setShader(mRadialGradient)
  16. canvas.drawCircle(width/2f,height/2f,width/3.toFloat(), mPaint!!)

5. 扫描渲染 SweepGradient

  1. /**
  2. * 扫描渲染
  3. * cx,cy 渐变中心坐标
  4. * color0,color1:渐变开始结束颜色
  5. * colors,positions:类似LinearGradient,用于多颜色渐变,positions为null时,根据颜色线性渐变
  6. */
  7. val mSweepGradient = SweepGradient(width/2f,height/2f, mColors, null)
  8. mPaint!!.setShader(mSweepGradient)
  9. canvas.drawCircle(width/2f,height/2f,width/3.toFloat(), mPaint!!)

6. 组合渲染 ComposeShader

  1. /**
  2. * 组合渲染,
  3. * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, Xfermode mode)
  4. * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, PorterDuff.Mode mode)
  5. * shaderA,shaderB:要混合的两种shader
  6. * Xfermode mode: 组合两种shader颜色的模式
  7. * PorterDuff.Mode mode: 组合两种shader颜色的模式
  8. */
  9. val bitMapShader = BitmapShader(
  10. mBitMap!!, Shader.TileMode.REPEAT,
  11. Shader.TileMode.REPEAT
  12. )
  13. val linearGradient = LinearGradient(
  14. 0f, 0f, 800f, 800f,
  15. mColors, null, Shader.TileMode.CLAMP
  16. )
  17. val mComposeShader = ComposeShader(linearGradient, bitMapShader, PorterDuff.Mode.SRC_OVER)
  18. mPaint!!.setShader(mComposeShader)
  19. canvas.drawRect(0f, 0f, 800f, 1000f, mPaint!!)

7. 绘制心型 ComposeShader

  1. //创建BitmapShader,用以绘制心
  2. val mBitmap = (resources.getDrawable(R.mipmap.heart) as BitmapDrawable).bitmap
  3. val bitmapShader = BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
  4. //创建LinearGradient,用以产生从左上角到右下角的颜色渐变效果
  5. val linearGradient = LinearGradient(
  6. 0f, 0f, mWidth.toFloat(), mHeight.toFloat(),
  7. Color.BLUE, Color.RED, Shader.TileMode.CLAMP
  8. )
  9. //bitmapShader对应目标像素,linearGradient对应源像素,像素颜色混合采用MULTIPLY模式
  10. val composeShader = ComposeShader(linearGradient, bitmapShader, PorterDuff.Mode.MULTIPLY)
  11. // ComposeShader composeShader2 = new ComposeShader(composeShader, linearGradient, PorterDuff.Mode.MULTIPLY);
  12. //将组合的composeShader作为画笔paint绘图所使用的shader
  13. mPaint!!.setShader(composeShader)
  14. //用composeShader绘制矩形区域
  15. canvas.drawRect(0f, 0f, mBitmap.width.toFloat(), mBitmap.height.toFloat(), mPaint!!)
  16. //所谓渲染就是对于我们绘制区域进行按照上诉渲染规则进行色彩的填充

9. 线性渲染-字体渐变 LinearGradient

  1. class LinearGradientTextView : TextView {
  2. /**
  3. * 定义线性渐变
  4. */
  5. private var mLinearGradient: LinearGradient? = null
  6. /**
  7. * 定义一个矩阵
  8. */
  9. private var mGradientatrix: Matrix? = null
  10. /**
  11. * 定义一个画笔
  12. */
  13. private var mPaint: Paint? = null
  14. private var mViewWidth = 0
  15. private var mTranslate = 0
  16. private var delta = 15
  17. constructor(context: Context?) : super(context)
  18. constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
  19. constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  20. /**
  21. * 当字改变的时候回调
  22. */
  23. override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
  24. super.onSizeChanged(w, h, oldw, oldh)
  25. if (mViewWidth ==0){
  26. //拿到当前 text 宽度
  27. mViewWidth = measuredWidth
  28. if (mViewWidth > 0){
  29. //拿到当前画笔
  30. mPaint = paint
  31. //拿到 text
  32. var text = text.toString()
  33. //mViewWidth除字体总数就得到了每个字的像素 然后*3 表示3个文字的像素
  34. var size = 0;
  35. //如果当前 text 长度大于 0
  36. if (text.length > 0){
  37. //拿到当前 3 个文字的像素
  38. size = mViewWidth / text.length * 3
  39. }else{//说明没有文字
  40. size = mViewWidth
  41. }
  42. /**线性渲染
  43. * x0, y0, 起始点
  44. * x1, y1, 结束点
  45. * int[] mColors, 中间依次要出现的几个颜色
  46. * float[] positions 位置数组,position的取值范围[0,1],作用是指定几个颜色分别放置在那个位置上,
  47. * 如果传null,渐变就线性变化。
  48. * tile 用于指定控件区域大于指定的渐变区域时,空白区域的颜色填充方法
  49. */
  50. //从左边 size 开始,左边看不见的地方开始,以滚动扫描的形式过来
  51. mLinearGradient = LinearGradient(-size.toFloat(),0f,0f,0f, intArrayOf(0x33ffffff, -0x1, 0x33ffffff),
  52. floatArrayOf(0f, 0.2f, 1f), Shader.TileMode.CLAMP)
  53. //将线性渐变添加到 paint 中
  54. mPaint!!.setShader(mLinearGradient)
  55. //定义一个矩阵
  56. mGradientatrix = Matrix()
  57. }
  58. }
  59. }
  60. /**
  61. * 开始绘制
  62. */
  63. override fun draw(canvas: Canvas?) {
  64. super.draw(canvas)
  65. val measureWindth = paint.measureText(text.toString())
  66. mTranslate += delta
  67. /**
  68. * 如果位置已经移动到了边界,那么文字就开始往回滚动
  69. * 但是如果小于 1 那么又开始递增,执行另一个逻辑
  70. */
  71. if (mTranslate > measureWindth + 1 || mTranslate < 1){
  72. delta = -delta
  73. }
  74. //将矩阵平移
  75. mGradientatrix!!.setTranslate(mTranslate.toFloat(),0f)
  76. mLinearGradient!!.setLocalMatrix(mGradientatrix)
  77. //paint是textview的所以只需要不断色控制画笔的shader 然后利用矩阵控制位移即可
  78. postInvalidateDelayed(30)
  79. }
  80. }

10. 雷达扫描 SweepGradient

  1. class RadarGradientView : View {
  2. private var mWidth: Int = 0
  3. private var mHeight: Int = 0
  4. private val TAG = javaClass.simpleName
  5. //五个圆
  6. private val pots = floatArrayOf(0.05f, 0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.35f)
  7. private var scanShader: Shader? = null // 扫描渲染shader
  8. private val scanSpeed = 10 // 扫描速度
  9. private var scanAngle: Int = 0 // 扫描旋转的角度
  10. private lateinit var mMatrix: Matrix // 旋转需要的矩阵
  11. private var mPaintCircle = Paint() // 画圆用到的paint
  12. private var mPaintRadar = Paint() // 扫描用到的paint
  13. private val run = object : Runnable {
  14. override fun run() {
  15. scanAngle = (scanAngle + scanSpeed) % 125 //
  16. Log.d(TAG,"scanAngle:$scanAngle")
  17. mMatrix.postRotate(scanSpeed.toFloat(), (mWidth / 2).toFloat(), (mHeight / 2).toFloat()) // 旋转矩阵
  18. invalidate() // 通知view重绘
  19. postDelayed(this, 50) // 调用自身 重复绘制
  20. }
  21. }
  22. constructor(context: Context) : super(context) {
  23. init()
  24. }
  25. private fun init() {
  26. mMatrix = Matrix()
  27. // 画圆用到的paint
  28. mPaintCircle = Paint()
  29. mPaintCircle.style = Paint.Style.STROKE // 描边
  30. mPaintCircle.strokeWidth = 1f // 宽度
  31. mPaintCircle.alpha = 100 // 透明度
  32. mPaintCircle.isAntiAlias = true // 抗锯齿
  33. mPaintCircle.color = Color.parseColor("#B0C4DE") // 设置颜色 亮钢兰色
  34. // 扫描用到的paint
  35. mPaintRadar = Paint()
  36. mPaintRadar.style = Paint.Style.FILL_AND_STROKE // 填充
  37. mPaintRadar.isAntiAlias = true // 抗锯齿
  38. post(run)
  39. }
  40. constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
  41. init()
  42. }
  43. constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
  44. override fun onDraw(canvas: Canvas) {
  45. super.onDraw(canvas)
  46. Log.d(TAG,"onDraw()")
  47. for (i in pots.indices) {
  48. canvas.drawCircle((mWidth / 2).toFloat(), (mHeight / 2).toFloat(), mWidth * pots[i], mPaintCircle)
  49. }
  50. // 画布的旋转变换 需要调用save() 和 restore()
  51. canvas.save()
  52. scanShader = SweepGradient(
  53. (mWidth / 2).toFloat(), (mHeight / 2).toFloat(),
  54. intArrayOf(Color.TRANSPARENT, Color.parseColor("#84B5CA")), null
  55. )
  56. mPaintRadar.shader = scanShader // 设置着色器
  57. canvas.concat(mMatrix)
  58. canvas.drawCircle((mWidth / 2).toFloat(), (mHeight / 2).toFloat(), mWidth * pots[6], mPaintRadar)
  59. canvas.restore()
  60. }
  61. override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
  62. super.onMeasure(widthMeasureSpec, heightMeasureSpec)
  63. Log.d(TAG,"onMeasure()")
  64. // 取屏幕的宽高是为了把雷达放在屏幕的中间
  65. mWidth = measuredWidth
  66. mHeight = measuredHeight
  67. mHeight = Math.min(mWidth, mHeight)
  68. mWidth = mHeight
  69. }
  70. }

11. 放大镜 BitmapShader

  1. class ZoomImageView : View {
  2. // 原图
  3. private val mBitmap: Bitmap
  4. // 放大后的图
  5. private var mBitmapScale: Bitmap? = null
  6. // 制作的圆形的图片(放大的局部),盖在Canvas上面
  7. private val mShapeDrawable: ShapeDrawable
  8. private val mMatrix: Matrix
  9. constructor(context: Context?) : super(context)
  10. constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
  11. constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  12. init {
  13. mBitmap = BitmapFactory.decodeResource(resources, R.mipmap.gild_3)
  14. mBitmapScale = mBitmap
  15. //放大后的整个图片
  16. mBitmapScale = Bitmap.createScaledBitmap(
  17. mBitmapScale!!, mBitmapScale!!.width * FACTOR,
  18. mBitmapScale!!.height * FACTOR, true
  19. )
  20. val bitmapShader = BitmapShader(
  21. mBitmapScale!!, Shader.TileMode.CLAMP,
  22. Shader.TileMode.CLAMP
  23. )
  24. mShapeDrawable = ShapeDrawable(OvalShape())
  25. mShapeDrawable.paint.shader = bitmapShader
  26. // 切出矩形区域,用来画圆(内切圆)
  27. mShapeDrawable.setBounds(0, 0, RADIUS * 2, RADIUS * 2)
  28. mMatrix = Matrix()
  29. }
  30. override fun onDraw(canvas: Canvas) {
  31. super.onDraw(canvas)
  32. // 1、画原图
  33. canvas.drawBitmap(mBitmap, 0f, 0f, null)
  34. // 2、画放大镜的图
  35. mShapeDrawable.draw(canvas)
  36. }
  37. override fun onTouchEvent(event: MotionEvent): Boolean {
  38. val x = event.x.toInt()
  39. val y = event.y.toInt() - RADIUS
  40. Log.d("onTouchEvent", "x:" + x + "y:" + y)
  41. // 将放大的图片往相反的方向挪动
  42. mMatrix.setTranslate((RADIUS - x * FACTOR).toFloat(), (RADIUS - y * FACTOR).toFloat())
  43. mShapeDrawable.paint.shader.setLocalMatrix(mMatrix)
  44. // 切出手势区域点位置的圆
  45. mShapeDrawable.setBounds(x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS)
  46. // invalidate()
  47. postInvalidate()
  48. return true
  49. }
  50. companion object {
  51. //放大倍数
  52. private val FACTOR = 3
  53. //放大镜的半径
  54. private val RADIUS = 300
  55. }
  56. }

滤镜

  1. //平移运算---加法
  2. ColorMatrix colorMartrix = new ColorMatrix(new float[]{
  3. 1, 0,0,0,0,
  4. 0,1,0,0,100,
  5. 0,0,1,0,0,
  6. 0,0,0,1,0,
  7. });
  8. //反相效果 -- 底片效果
  9. ColorMatrix colorMartrix = new ColorMatrix(new float[]{
  10. -1, 0,0,0,255,
  11. 0,-1,0,0,255,
  12. 0,0,-1,0,255,
  13. 0,0,0,1,0,
  14. });
  15. //缩放运算---乘法 -- 颜色增强
  16. ColorMatrix colorMartrix = new ColorMatrix(new float[]{
  17. 1.2f, 0,0,0,0,
  18. 0,1.2f,0,0,0,
  19. 0,0,1.2f,0,0,
  20. 0,0,0,1.2f,0,
  21. });
  22. /** 黑白照片
  23. * 是将我们的三通道变为单通道的灰度模式
  24. * 去色原理:只要把R G B 三通道的色彩信息设置成一样,那么图像就会变成灰色,
  25. * 同时为了保证图像亮度不变,同一个通道里的R+G+B =1
  26. */
  27. ColorMatrix colorMartrix = new ColorMatrix(new float[]{
  28. 0.213f, 0.715f,0.072f,0,0,
  29. 0.213f, 0.715f,0.072f,0,0,
  30. 0.213f, 0.715f,0.072f,0,0,
  31. 0,0,0,1,0,
  32. });
  33. //发色效果---(比如红色和绿色交换)
  34. ColorMatrix colorMartrix = new ColorMatrix(new float[]{
  35. 1,0,0,0,0,
  36. 0, 0,1,0,0,
  37. 0,1,0,0,0,
  38. 0,0,0,0.5F,0,
  39. });
  40. //复古效果
  41. ColorMatrix colorMartrix = new ColorMatrix(new float[]{
  42. 1/2f,1/2f,1/2f,0,0,
  43. 1/3f, 1/3f,1/3f,0,0,
  44. 1/4f,1/4f,1/4f,0,0,
  45. 0,0,0,1,0,
  46. });
  1. class FilterView : View {
  2. private var paint = Paint()
  3. lateinit var bitmap: Bitmap
  4. constructor(context: Context?) : super(context)
  5. constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
  6. constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  7. /**
  8. * 显示的高
  9. */
  10. var showHeight = 0
  11. init {
  12. init()
  13. }
  14. private fun init() {
  15. paint = Paint(Paint.ANTI_ALIAS_FLAG)
  16. paint.color = Color.RED
  17. bitmap = BitmapFactory.decodeResource(resources, R.mipmap.gild_3)
  18. }
  19. override fun onDraw(canvas: Canvas) {
  20. super.onDraw(canvas)
  21. // //关闭单个View的硬件加速功
  22. // // setLayerType(View.LAYER_TYPE_SOFTWARE,null);
  23. //1\. 缩放运算---乘法 -- 颜色增强
  24. val colorMartrix = ColorMatrix(
  25. floatArrayOf(
  26. 1.2f, 0f, 0f, 0f, 0f,
  27. 0f, 1.2f, 0f, 0f, 0f,
  28. 0f, 0f, 1.2f, 0f, 0f,
  29. 0f, 0f, 0f, 1.2f, 0f
  30. )
  31. )
  32. val rectF = RectF(
  33. 0f,
  34. showHeight.toFloat() ,
  35. (bitmap.width / 2).toFloat(),
  36. (bitmap.height / 4).toFloat()
  37. )
  38. drawFilterBitmap(colorMartrix, canvas,rectF)
  39. showHeight += bitmap.height / 4
  40. //2 平移运算---加法
  41. var colorMartrix2 = ColorMatrix(floatArrayOf(
  42. 1f, 0f,0f,0f,0f,
  43. 0f,1f,0f,0f,100f,
  44. 0f,0f,1f,0f,0f,
  45. 0f,0f,0f,1f,0f
  46. ))
  47. val rectF2 = RectF(
  48. 0f,
  49. showHeight.toFloat(),
  50. (bitmap.width / 2).toFloat(),
  51. (bitmap.height /4) * 2.toFloat()
  52. )
  53. drawFilterBitmap(colorMartrix2, canvas,rectF2)
  54. showHeight += bitmap.height / 4
  55. //3\. 反相效果 -- 底片效果
  56. var colorMartrix3 = ColorMatrix(floatArrayOf(
  57. -1f, 0f,0f,0f,255f,
  58. 0f,-1f,0f,0f,255f,
  59. 0f,0f,-1f,0f,255f,
  60. 0f,0f,0f,1f,0f
  61. ));
  62. val rectF3 = RectF(
  63. 0f,
  64. showHeight.toFloat(),
  65. (bitmap.width / 2).toFloat(),
  66. (bitmap.height /4) * 3.toFloat()
  67. )
  68. drawFilterBitmap(colorMartrix3, canvas,rectF3)
  69. /**
  70. * 4.黑白照片
  71. * 是将我们的三通道变为单通道的灰度模式
  72. * 去色原理:只要把R G B 三通道的色彩信息设置成一样,那么图像就会变成灰色,
  73. * 同时为了保证图像亮度不变,同一个通道里的R+G+B =1
  74. */
  75. var colorMartrix4 = ColorMatrix(floatArrayOf(
  76. 0.213f, 0.715f,0.072f,0f,0f,
  77. 0.213f, 0.715f,0.072f,0f,0f,
  78. 0.213f, 0.715f,0.072f,0f,0f,
  79. 0f,0f,0f,1f,0f
  80. ));
  81. showHeight += bitmap.height / 4
  82. val rectF4 = RectF(
  83. bitmap.width/2f,
  84. bitmap.height /2f,
  85. (bitmap.width).toFloat(),
  86. (bitmap.height /4) * 3.toFloat()
  87. )
  88. drawFilterBitmap(colorMartrix4, canvas,rectF4)
  89. //5.发色效果---(比如红色和绿色交换)
  90. var colorMartrix5 = ColorMatrix(floatArrayOf(
  91. 1f,0f,0f,0f,0f,
  92. 0f, 0f,1f,0f,0f,
  93. 0f,1f,0f,0f,0f,
  94. 0f,0f,0f,0.5F,0f
  95. ));
  96. val rectF5 = RectF(
  97. bitmap.width / 2f,
  98. 0f,
  99. (bitmap.width / 2 * 2).toFloat(),
  100. (bitmap.height /4) .toFloat()
  101. )
  102. drawFilterBitmap(colorMartrix5, canvas,rectF5)
  103. //6.复古效果
  104. var colorMartrix6= ColorMatrix(floatArrayOf(
  105. 1/2f,1/2f,1/2f,0f,0f,
  106. 1/3f, 1/3f,1/3f,0f,0f,
  107. 1/4f,1/4f,1/4f,0f,0f,
  108. 0f,0f,0f,1f,0f
  109. ));
  110. val rectF6 = RectF(
  111. bitmap.width / 2f,
  112. bitmap.height /4f,
  113. (bitmap.width / 2 * 2).toFloat(),
  114. (bitmap.height /4 * 2) .toFloat()
  115. )
  116. drawFilterBitmap(colorMartrix6, canvas,rectF6)
  117. }
  118. private fun drawFilterBitmap(colorMartrix: ColorMatrix, canvas: Canvas,rectF: RectF) {
  119. paint.colorFilter = ColorMatrixColorFilter(colorMartrix)
  120. canvas.drawBitmap(bitmap, null, rectF, paint)
  121. }
  122. }

xfermode

  1. private static final Xfermode[] sModes = {
  2. new PorterDuffXfermode(PorterDuff.Mode.CLEAR), // 清空所有,要闭硬件加速,否则无效
  3. new PorterDuffXfermode(PorterDuff.Mode.SRC), // 显示前都图像,不显示后者
  4. new PorterDuffXfermode(PorterDuff.Mode.DST), // 显示后者图像,不显示前者
  5. new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER), // 后者叠于前者
  6. new PorterDuffXfermode(PorterDuff.Mode.DST_OVER), // 前者叠于后者
  7. new PorterDuffXfermode(PorterDuff.Mode.SRC_IN), // 显示相交的区域,但图像为后者
  8. new PorterDuffXfermode(PorterDuff.Mode.DST_IN), // 显示相交的区域,但图像为前者
  9. new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT), // 显示后者不重叠的图像
  10. new PorterDuffXfermode(PorterDuff.Mode.DST_OUT), // 显示前者不重叠的图像
  11. new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP), // 显示前者图像,与后者重合的图像
  12. new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP), // 显示后者图像,与前者重合的图像
  13. new PorterDuffXfermode(PorterDuff.Mode.XOR), // 显示持有不重合的图像
  14. new PorterDuffXfermode(PorterDuff.Mode.DARKEN), // 后者叠于前者上,后者与前者重叠的部份透明。要闭硬件加速,否则无效
  15. new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN), // 前者叠于前者,前者与后者重叠部份透明。要闭硬件加速,否则无效
  16. new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY), // 显示重合的图像,且颜色会合拼
  17. new PorterDuffXfermode(PorterDuff.Mode.SCREEN) // 显示持有图像,重合的会变白
  18. };
  1. class XfermodeView : View {
  2. lateinit var mPaint: Paint
  3. internal var mItemSize = 0f
  4. internal var mItemHorizontalOffset = 0f
  5. internal var mItemVerticalOffset = 0f
  6. internal var mCircleRadius = 0f
  7. internal var mRectSize = 0f
  8. internal var mCircleColor = -0x33bc//黄色
  9. internal var mRectColor = -0x995501//蓝色
  10. internal var mTextSize = 25f
  11. constructor(context: Context) : super(context) {
  12. init(null, 0)
  13. }
  14. constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
  15. init(attrs, 0)
  16. }
  17. constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
  18. init(attrs, defStyle)
  19. }
  20. private fun init(attrs: AttributeSet?, defStyle: Int) {
  21. if (Build.VERSION.SDK_INT >= 11) {
  22. setLayerType(View.LAYER_TYPE_SOFTWARE, null)
  23. }
  24. mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
  25. mPaint.textSize = mTextSize
  26. mPaint.textAlign = Paint.Align.CENTER
  27. mPaint.strokeWidth = 2f
  28. }
  29. override fun onDraw(canvas: Canvas) {
  30. super.onDraw(canvas)
  31. //设置背景色
  32. // canvas.drawARGB(255, 139, 197, 186);
  33. val canvasWidth = canvas.width
  34. val canvasHeight = canvas.height
  35. for (row in 0..3) {
  36. for (column in 0..3) {
  37. canvas.save()
  38. val layer =
  39. canvas.saveLayer(0f, 0f, canvasWidth.toFloat(), canvasHeight.toFloat(), null, Canvas.ALL_SAVE_FLAG)
  40. mPaint.xfermode = null
  41. val index = row * 4 + column
  42. val translateX = (mItemSize + mItemHorizontalOffset) * column
  43. val translateY = (mItemSize + mItemVerticalOffset) * row
  44. canvas.translate(translateX, translateY)
  45. //画文字
  46. val text = sLabels[index]
  47. mPaint.color = Color.BLACK
  48. val textXOffset = mItemSize / 2
  49. val textYOffset = mTextSize + (mItemVerticalOffset - mTextSize) / 2
  50. canvas.drawText(text, textXOffset, textYOffset, mPaint)
  51. canvas.translate(0f, mItemVerticalOffset)
  52. //画边框
  53. mPaint.style = Paint.Style.STROKE
  54. mPaint.color = -0x1000000
  55. canvas.drawRect(2f, 2f, mItemSize - 2, mItemSize - 2, mPaint)
  56. mPaint.style = Paint.Style.FILL
  57. //画圆
  58. mPaint.color = mCircleColor
  59. val left = mCircleRadius + 3
  60. val top = mCircleRadius + 3
  61. canvas.drawCircle(left, top, mCircleRadius, mPaint)
  62. mPaint.xfermode = sModes[index]
  63. //画矩形
  64. mPaint.color = mRectColor
  65. val rectRight = mCircleRadius + mRectSize
  66. val rectBottom = mCircleRadius + mRectSize
  67. canvas.drawRect(left, top, rectRight, rectBottom, mPaint)
  68. mPaint.xfermode = null
  69. //canvas.restore();
  70. canvas.restoreToCount(layer)
  71. }
  72. }
  73. }
  74. override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
  75. super.onSizeChanged(w, h, oldw, oldh)
  76. mItemSize = w / 4.5f
  77. mItemHorizontalOffset = mItemSize / 6
  78. mItemVerticalOffset = mItemSize * 0.426f
  79. mCircleRadius = mItemSize / 3
  80. mRectSize = mItemSize * 0.6f
  81. }
  82. companion object {
  83. private val sModes = arrayOf<Xfermode>(
  84. PorterDuffXfermode(PorterDuff.Mode.CLEAR),
  85. PorterDuffXfermode(PorterDuff.Mode.SRC),
  86. PorterDuffXfermode(
  87. PorterDuff.Mode.DST
  88. ),
  89. PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
  90. PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
  91. PorterDuffXfermode(
  92. PorterDuff.Mode.SRC_IN
  93. ),
  94. PorterDuffXfermode(PorterDuff.Mode.DST_IN),
  95. PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
  96. PorterDuffXfermode(
  97. PorterDuff.Mode.DST_OUT
  98. ),
  99. PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
  100. PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
  101. PorterDuffXfermode(
  102. PorterDuff.Mode.XOR
  103. ),
  104. PorterDuffXfermode(PorterDuff.Mode.DARKEN),
  105. PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
  106. PorterDuffXfermode(
  107. PorterDuff.Mode.MULTIPLY
  108. ),
  109. PorterDuffXfermode(PorterDuff.Mode.SCREEN)
  110. )
  111. private val sLabels = arrayOf(
  112. "Clear",
  113. "Src",
  114. "Dst",
  115. "SrcOver",
  116. "DstOver",
  117. "SrcIn",
  118. "DstIn",
  119. "SrcOut",
  120. "DstOut",
  121. "SrcATop",
  122. "DstATop",
  123. "Xor",
  124. "Darken",
  125. "Lighten",
  126. "Multiply",
  127. "Screen"
  128. )
  129. }
  130. }

1. 画圆角 PorterDuff.Mode.DST_IN

  1. public class RoudImageView : View {
  2. constructor(context: Context?) : super(context)
  3. constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
  4. init()
  5. }
  6. var mBitPaint = Paint()
  7. private lateinit var mBmpDST: Bitmap
  8. private lateinit var mBmpSRC: Bitmap
  9. private fun init() {
  10. setLayerType(View.LAYER_TYPE_SOFTWARE, null)
  11. mBmpDST = changeBitmapSize(R.mipmap.gild_3)
  12. mBmpSRC = changeBitmapSize(R.drawable.shade)
  13. }
  14. constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
  15. override fun draw(canvas: Canvas) {
  16. super.draw(canvas)
  17. val saveLayer = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
  18. canvas.drawBitmap(mBmpDST,0f,0f,mBitPaint)
  19. mBitPaint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.DST_IN))
  20. canvas.drawBitmap(mBmpSRC,0f,0f,mBitPaint)
  21. mBitPaint.setXfermode(null)
  22. canvas.restoreToCount(saveLayer)
  23. }
  24. private fun changeBitmapSize(res: Int) : Bitmap {
  25. var bitmap = BitmapFactory.decodeResource(getResources(), res);
  26. var width = bitmap.getWidth();
  27. var height = bitmap.getHeight();
  28. Log.e("width","width:"+width);
  29. Log.e("height","height:"+height);
  30. //设置想要的大小
  31. var newWidth=500;
  32. var newHeight=500;
  33. //计算压缩的比率
  34. var scaleWidth=(newWidth)/width .toFloat()
  35. var scaleHeight=(newHeight)/height.toFloat();
  36. //获取想要缩放的matrix
  37. var matrix = Matrix();
  38. matrix.postScale(scaleWidth,scaleHeight.toFloat());
  39. //获取新的bitmap
  40. bitmap=Bitmap.createBitmap(bitmap,0,0,width,height,matrix,true);
  41. bitmap.getWidth();
  42. bitmap.getHeight();
  43. Log.e("newWidth","newWidth"+bitmap.getWidth());
  44. Log.e("newHeight","newHeight"+bitmap.getHeight());
  45. return bitmap;
  46. }
  47. }

2. 倒影 PorterDuff.Mode.DST_IN

  1. class InvertedImageView(context: Context, attrs: AttributeSet) : View(context, attrs) {
  2. private val mBitPaint: Paint
  3. private val BmpDST: Bitmap
  4. private val BmpSRC: Bitmap
  5. private val BmpRevert: Bitmap
  6. init {
  7. setLayerType(View.LAYER_TYPE_SOFTWARE, null)
  8. mBitPaint = Paint()
  9. BmpDST = BitmapUtis.changeBitmapSize(context,R.mipmap.gild_3)
  10. BmpSRC = BitmapUtis.changeBitmapSize(context,R.drawable.invert_shade)
  11. val matrix = Matrix()
  12. matrix.setScale(1f, -1f)
  13. // 生成倒影图
  14. BmpRevert = Bitmap.createBitmap(BmpDST, 0, 0, BmpDST.width, BmpDST.height, matrix, true)
  15. }
  16. override fun onDraw(canvas: Canvas) {
  17. super.onDraw(canvas)
  18. canvas.drawColor(Color.BLACK)
  19. //先画出原始图片
  20. canvas.drawBitmap(BmpDST, 0f, 0f, mBitPaint)
  21. //再画出倒影
  22. val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
  23. canvas.translate(0f, BmpSRC.height.toFloat())
  24. canvas.drawBitmap(BmpRevert, 0f, 0f, mBitPaint)
  25. mBitPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
  26. canvas.drawBitmap(BmpSRC, 0f, 0f, mBitPaint)
  27. mBitPaint.xfermode = null
  28. canvas.restoreToCount(layerId)
  29. }
  30. }

4. 不规则波浪 PorterDuff.Mode.DST_IN

  1. class IrregularWaveView(context: Context, attrs: AttributeSet) : View(context, attrs) {
  2. private val mPaint: Paint
  3. private var mItemWaveLength = 0
  4. private var dx = 0
  5. private val BmpSRC: Bitmap
  6. private val BmpDST: Bitmap
  7. init {
  8. mPaint = Paint()
  9. BmpDST = BitmapFactory.decodeResource(resources, R.drawable.wav, null)
  10. BmpSRC = BitmapFactory.decodeResource(resources, R.drawable.circle_shape, null)
  11. //不要让它超出边界
  12. mItemWaveLength = BmpDST.width - BmpSRC.width
  13. startAnim()
  14. }
  15. override fun onDraw(canvas: Canvas) {
  16. super.onDraw(canvas)
  17. //先画上圆形
  18. canvas.drawBitmap(BmpSRC, 0f, 0f, mPaint)
  19. //再画上结果
  20. val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
  21. canvas.drawBitmap(
  22. BmpDST,
  23. Rect(dx, 0, dx + BmpSRC.width, BmpSRC.height),
  24. Rect(0, 0, BmpSRC.width, BmpSRC.height),
  25. mPaint
  26. )
  27. mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
  28. canvas.drawBitmap(BmpSRC, 0f, 0f, mPaint)
  29. mPaint.xfermode = null
  30. canvas.restoreToCount(layerId)
  31. }
  32. fun startAnim() {
  33. val animator = ValueAnimator.ofInt(0, mItemWaveLength)
  34. animator.duration = 2000
  35. animator.repeatCount = ValueAnimator.INFINITE
  36. animator.interpolator = LinearInterpolator()
  37. animator.addUpdateListener { animation ->
  38. dx = animation.animatedValue as Int
  39. postInvalidate()
  40. }
  41. animator.start()
  42. }
  43. }

5.心电图 PorterDuff.Mode.DST_IN

  1. class HeartView(context: Context, attrs: AttributeSet) : View(context, attrs) {
  2. private val mPaint: Paint
  3. private var mItemWaveLength = 0
  4. private var dx = 0
  5. private val BmpSRC: Bitmap
  6. private val BmpDST: Bitmap
  7. init {
  8. mPaint = Paint()
  9. mPaint.color = Color.RED
  10. BmpDST = BitmapFactory.decodeResource(resources, R.drawable.heartmap, null)
  11. BmpSRC = Bitmap.createBitmap(BmpDST.width, BmpDST.height, Bitmap.Config.ARGB_8888)
  12. mItemWaveLength = BmpDST.width
  13. startAnim()
  14. }
  15. override fun onDraw(canvas: Canvas) {
  16. super.onDraw(canvas)
  17. val c = Canvas(BmpSRC)
  18. //清空bitmap
  19. c.drawColor(Color.RED, PorterDuff.Mode.CLEAR)
  20. Log.d("onDraw","左移动:${BmpDST.width - dx}");
  21. //画上矩形
  22. c.drawRect((BmpDST.width - dx).toFloat(), 0f, BmpDST.width.toFloat(), BmpDST.height.toFloat(), mPaint)
  23. //模式合成
  24. val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
  25. canvas.drawBitmap(BmpDST, 0f, 0f, mPaint)
  26. mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
  27. canvas.drawBitmap(BmpSRC, 0f, 0f, mPaint)
  28. mPaint.xfermode = null
  29. canvas.restoreToCount(layerId)
  30. }
  31. fun startAnim() {
  32. val animator = ValueAnimator.ofInt(0, mItemWaveLength)
  33. animator.duration = 6000
  34. animator.repeatCount = ValueAnimator.INFINITE
  35. animator.interpolator = LinearInterpolator()
  36. animator.addUpdateListener { animation ->
  37. dx = animation.animatedValue as Int
  38. postInvalidate()
  39. }
  40. animator.start()
  41. }
  42. }

6. PorterDuff.Mode.MULTIPLY

  1. class TwitterView(context: Context, attrs: AttributeSet) : View(context, attrs) {
  2. private val mBitPaint: Paint
  3. private val BmpDST: Bitmap
  4. private val BmpSRC: Bitmap
  5. init {
  6. setLayerType(View.LAYER_TYPE_SOFTWARE, null)
  7. mBitPaint = Paint()
  8. //目标图像
  9. BmpDST = BitmapFactory.decodeResource(resources, R.drawable.twiter_bg, null)
  10. //原图像
  11. BmpSRC = BitmapFactory.decodeResource(resources, R.drawable.twiter_light, null)
  12. }
  13. override fun onDraw(canvas: Canvas) {
  14. super.onDraw(canvas)
  15. val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
  16. canvas.drawBitmap(BmpDST, 0f, 0f, mBitPaint)
  17. mBitPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)
  18. canvas.drawBitmap(BmpSRC, 0f, 0f, mBitPaint)
  19. mBitPaint.xfermode = null
  20. canvas.restoreToCount(layerId)
  21. }
  22. }

7. PorterDuff.Mode.SRC_OUT

  1. class GuaGuaCardView(context: Context, attrs: AttributeSet) : View(context, attrs) {
  2. private val mBitPaint: Paint
  3. private val BmpDST: Bitmap
  4. private val BmpSRC: Bitmap
  5. private val BmpText: Bitmap
  6. private val mPath: Path
  7. private var mPreX: Float = 0.toFloat()
  8. private var mPreY: Float = 0.toFloat()
  9. init {
  10. setLayerType(View.LAYER_TYPE_SOFTWARE, null)
  11. mBitPaint = Paint()
  12. mBitPaint.color = Color.RED
  13. mBitPaint.style = Paint.Style.STROKE
  14. mBitPaint.strokeWidth = 45f
  15. BmpText = BitmapFactory.decodeResource(resources, R.drawable.guaguaka_text1, null)
  16. BmpSRC = BitmapFactory.decodeResource(resources, R.drawable.guaguaka, null)
  17. BmpDST = Bitmap.createBitmap(BmpSRC.width, BmpSRC.height, Bitmap.Config.ARGB_8888)
  18. mPath = Path()
  19. }
  20. override fun onDraw(canvas: Canvas) {
  21. super.onDraw(canvas)
  22. canvas.drawBitmap(BmpText, 0f, 0f, mBitPaint)
  23. val layerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
  24. //先把手指轨迹画到目标Bitmap上
  25. val c = Canvas(BmpDST)
  26. c.drawPath(mPath, mBitPaint)
  27. //然后把目标图像画到画布上
  28. canvas.drawBitmap(BmpDST, 0f, 0f, mBitPaint)
  29. //计算源图像区域
  30. mBitPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
  31. canvas.drawBitmap(BmpSRC, 0f, 0f, mBitPaint)
  32. mBitPaint.xfermode = null
  33. canvas.restoreToCount(layerId)
  34. }
  35. override fun onTouchEvent(event: MotionEvent): Boolean {
  36. when (event.action) {
  37. MotionEvent.ACTION_DOWN -> {
  38. mPath.moveTo(event.x, event.y)
  39. mPreX = event.x
  40. mPreY = event.y
  41. return true
  42. }
  43. MotionEvent.ACTION_MOVE -> {
  44. val endX = (mPreX + event.x) / 2
  45. val endY = (mPreY + event.y) / 2
  46. mPath.quadTo(mPreX, mPreY, endX, endY)
  47. mPreX = event.x
  48. mPreY = event.y
  49. }
  50. MotionEvent.ACTION_UP -> {
  51. }
  52. }
  53. postInvalidate()
  54. return super.onTouchEvent(event)
  55. }
  56. }

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号