实现一个简单的画图APP,通过自定义View 实现

相关官网文档参考:https://blog.csdn.net/whjk20/article/details/115666165

目录

1. Canvas基本概念 (Canvas/Bitmap/Paint/Clip)

onDraw(Canvas canvas)

onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int)

Path()

onTouchEvent()

3. 日志分析

4. 结论:


1. Canvas基本概念 (Canvas/Bitmap/Paint/Clip)

  • Canvas(画布,帆布)  Paint(涂料)
  • 官方文档介绍:

https://developer.android.com/reference/android/graphics/Canvas.html
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components:
 A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap),
a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).

Bitmap 保存像素点Canvas 定义在屏幕画的形状Paint 定义画的颜色、风格、fund?,  clip定义哪部分是可见

主要方法

onDraw(Canvas canvas)

在画布上绘制,需要缓存所画的内容,以提升性能 (缓存canvas , bitmap);  调用invalidate() 则会回调方法以更新界面。

onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int)

屏幕分辩率变化时回调,在这里创建基于新尺寸的Bitmap 以及 Canvas 已经一些初始化

Path()

画的轨迹 : https://developer.android.com/reference/kotlin/android/graphics/Path.html

onTouchEvent()

记录触摸的x, y坐标,响应按下(ACTION_DOWN) / 移动(ACTION_MOVE)/抬起 (ACTION_UP) 操作

2. 问题
(1) 无法全屏(仍有状态栏statusbar) myCanvasView.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

package com.example.minipaintimport android.content.Context
import android.graphics.*
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import androidx.core.content.res.ResourcesCompat
import kotlin.math.absprivate const val STROKE_WIDTH = 12f // has to be floatclass MyCanvasView(context: Context) : View(context) {private lateinit var extraCanvas: Canvasprivate lateinit var extraBitmap: Bitmapprivate val backgroundColor = ResourcesCompat.getColor(resources, R.color.colorBackground, null)private val drawColor = ResourcesCompat.getColor(resources, R.color.colorPaint, null)private var motionTouchEventX = 0fprivate var motionTouchEventY = 0fprivate var currentX = 0fprivate var currentY = 0f//阈值:大于这个值才认为是滑动,进而画出轨迹private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop//显示一个边框, 实际上超出这个边界也是可以画,仅仅是显示边框而已private lateinit var frame: Rect// Set up the paint with which to draw. 画笔的样式private val paint = Paint().apply {color = drawColor// Smooths out edges of what is drawn without affecting shape.isAntiAlias = true// Dithering affects how colors with higher-precision than the device are down-sampled.isDither = truestyle = Paint.Style.STROKE // default: FILLstrokeJoin = Paint.Join.ROUND // default: MITERstrokeCap = Paint.Cap.ROUND // default: BUTTstrokeWidth = STROKE_WIDTH // default: Hairline-width (really thin)}private var path = Path() //轨迹override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)// 因为每次大小变化,都需要创建新的bitmap, 但是旧的也要回收,否则内存泄露if (::extraBitmap.isInitialized) extraBitmap.recycle()extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)extraCanvas = Canvas(extraBitmap)extraCanvas.drawColor(backgroundColor)// Calculate a rectangular frame around the picture.val inset = 40frame = Rect(inset, inset, width - inset, height - inset) // todo 为何左边没有设置,都会有框??}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)canvas.drawBitmap(extraBitmap, 0f, 0f, null)// Draw a frame around the canvas.canvas.drawRect(frame, paint)}override fun onTouchEvent(event: MotionEvent): Boolean {motionTouchEventX = event.xmotionTouchEventY = event.ywhen (event.action) {MotionEvent.ACTION_DOWN -> touchStart()MotionEvent.ACTION_MOVE -> touchMove()MotionEvent.ACTION_UP -> touchUp()}return true}private fun touchStart() {path.reset()path.moveTo(motionTouchEventX, motionTouchEventY)currentX = motionTouchEventXcurrentY = motionTouchEventY}private fun touchMove() {val dx = abs(motionTouchEventX - currentX)val dy = abs(motionTouchEventY - currentY)if (dx >= touchTolerance || dy >= touchTolerance) {// QuadTo() adds a quadratic bezier from the last point,// approaching control point (x1,y1), and ending at (x2,y2).path.quadTo(currentX,currentY,(motionTouchEventX + currentX) / 2,(motionTouchEventY + currentY) / 2) //低阶法计算数值积分 todocurrentX = motionTouchEventXcurrentY = motionTouchEventYextraCanvas.drawPath(path, paint)}invalidate()}private fun touchUp() {// Reset the path so it doesn't get drawn again.path.reset()}
}

其中,

    <color name="colorBackground">#FFFF5500</color><color name="colorPaint">#FFFFEB3B</color>
    <string name="canvasContentDescription">Mini Paint is a simple line drawing app.Drag your fingers to draw. Rotate the phone to clear.</string>

extraCanvas 缓存画布, 如何绘制(draw)到当前视图(View)上??

----> 通过Bitmap,  即不断的往Bitmap中添加内容(使用一个缓存的extraCanvas),然后在View.onDraw 时,使用Canvas 绘制出来

仅仅是在onSizeChanged 时调用 extraCanvas.drawColor,  响应触摸时,调用extraCanvas.drawPath(path, paint)

那么在  onDraw() 时调用  canvas.drawBitmap(extraBitmap, 0f,0f, null) 这个有什么用???  ---> 如果没有,则无背景颜色

3. 日志分析

//第一次启动进入,此时的缓存extraCanvas
04-13 09:48:55.759 I MyCanvasView:          onSizeChanged extraCanvas=android.graphics.Canvas@981f35b 
//此时的onDraw回调的canvas
04-13 09:48:55.782 I MyCanvasView:          onDraw Call canvas=android.graphics.RecordingCanvas@7dc87f8

//之后触摸屏幕后,extraCanvas.drawPath()完成后,刷新界面
//此时回调onDraw 传进来的canvas,  既不是缓存canvas, 也不是第一次回调onDraw的canvas (但是后面触摸后回调onDraw,传入的是同一个Canvas对象)
04-13 09:49:05.992 I MyCanvasView:          onDraw Call canvas=android.graphics.RecordingCanvas@6ec4b0e 
04-13 09:49:06.026 I MyCanvasView:          onDraw Call canvas=android.graphics.RecordingCanvas@6ec4b0e

4. 结论:

1. 需要一个保存像素的位图 (缓存的Bitmap)
2. 在回调onDraw(canvas:Canvas) 时,通过这个canvas 画出这个位图 (Canvas.drawBitmap), 同时可以其它 drawXXX 画形状操作
3. 画笔(Paint) 可以装饰画的属性(颜色、风格等)
4. 创建的缓存Canvas(通过一个缓存Bitmap创建), 只要有绘制内容,在调用 View 的invalidate()时,就回执行步骤2

Android 自定义View 实例_ 画图相关推荐

  1. Android 自定义View 实例2_Clipping Canvas

    上一篇 Android 自定义View 实例_ 画图  参考: https://blog.csdn.net/whjk20/article/details/115639448 这里是Canvas 的裁剪 ...

  2. Android 自定义 圆环,Android自定义view实现圆环效果实例代码

    先上效果图,如果大家感觉不错,请参考实现代码. 重要的是如何实现自定义的view效果 (1)创建类,继承view,重写onDraw和onMesure方法 public class CirclePerc ...

  3. Android自定义view详解,使用实例,自定义属性,贝塞尔曲线

    //只会触发执行onDraw方法,只会改变绘制里面的内容,条目的绘制 invalidate(); //只会触发执行onDraw方法,但是可以在子线程中刷新 postInvalidate(); //vi ...

  4. android 自定义view滚动条,Android自定义View实现等级滑动条的实例

    Android自定义View实现等级滑动条的实例 实现效果图: 思路: 首先绘制直线,然后等分直线绘制点: 绘制点的时候把X值存到集合中. 然后绘制背景图片,以及图片上的数字. 点击事件down的时候 ...

  5. android自定义view案例,Android自定义View的实现方法实例详解

    一.自绘控件 下面我们准备来自定义一个计数器View,这个View可以响应用户的点击事件,并自动记录一共点击了多少次.新建一个CounterView继承自View,代码如下所示: 可以看到,首先我们在 ...

  6. android自定义抽奖,Android自定义view制作抽奖转盘

    本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplicati ...

  7. android代码实现手机加速功能,Android自定义View实现内存清理加速球效果

    Android自定义View实现内存清理加速球效果 发布时间:2020-09-21 22:21:57 来源:脚本之家 阅读:105 作者:程序员的自我反思 前言 用过猎豹清理大师或者相类似的安全软件, ...

  8. android view 渐变动画,Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  9. 自定义圆形倒计时Android,Android自定义View倒计时圆

    本文实例为大家分享了Android自定义View倒计时圆的具体代码,供大家参考,具体内容如下 创建attr 创建DisplayUtil 类 import android.content.Context ...

最新文章

  1. html xhtml and css,HTML与XHTML的重要区别
  2. Android文档-开发者指南-第一部分:入门-中英文对照版
  3. AngularJS学习笔记(1)——MVC模式的清单列表效果
  4. 2018.5.28 PSOC第一枪:基于cypress的蓝牙开发
  5. 从零开始开发JVM语言(七)语义分析的起步
  6. 课节5:图神经网络算法(二):GraphSage实践
  7. php无需鉴权的接口,thinkphp5-restfulapi 博客 接口鉴权应用
  8. H3C交换机创建用户
  9. 魏俊妮《人力资源体系搭建专家》
  10. c语言二级选择题APP,C语言二级题库
  11. MySQL读写分离原理
  12. Java实现 蓝桥杯 算法训练 相邻数对(暴力)
  13. Java方法篇——String方法
  14. 新一代图片编解码技术在淘宝的应用及落地
  15. 正则表达式re之模块函数和编译标志
  16. 如何使用TEQC 分析rinex3 格式的数据
  17. 小程序实现3D轮播图效果
  18. python输入百分制成绩输出成绩等级_给出一百分制成绩 要求输出成绩等级
  19. selenium~初识自动化测试之元素的定位、对象的操作、浏览器的操作、键盘事件、鼠标事件
  20. 等待事件enq TX row lock contention分析

热门文章

  1. 90后小伙利用支付宝漏洞被抓, 我为什么拍手叫好?
  2. 欧拉定理相关及扩展欧几里得
  3. 今天我们来聊聊 UI 组件库推广一年有余遇到的困境吧
  4. 影视编导必备三项知识
  5. 中国医科大学2021年12月《医学心理学》作业考核试题
  6. 续 Matlab创建有价值历史纪录(完整版)
  7. 使用vsc写node遇到缺少 对象
  8. 谱聚类原理(深入浅出)
  9. gis计算机技术发展,关于计算机技术的GIS技术发展探索
  10. 微信第三方登录 -- (PC端+移动端)