Android 自定义View 实例_ 画图
实现一个简单的画图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 实例_ 画图相关推荐
- Android 自定义View 实例2_Clipping Canvas
上一篇 Android 自定义View 实例_ 画图 参考: https://blog.csdn.net/whjk20/article/details/115639448 这里是Canvas 的裁剪 ...
- Android 自定义 圆环,Android自定义view实现圆环效果实例代码
先上效果图,如果大家感觉不错,请参考实现代码. 重要的是如何实现自定义的view效果 (1)创建类,继承view,重写onDraw和onMesure方法 public class CirclePerc ...
- Android自定义view详解,使用实例,自定义属性,贝塞尔曲线
//只会触发执行onDraw方法,只会改变绘制里面的内容,条目的绘制 invalidate(); //只会触发执行onDraw方法,但是可以在子线程中刷新 postInvalidate(); //vi ...
- android 自定义view滚动条,Android自定义View实现等级滑动条的实例
Android自定义View实现等级滑动条的实例 实现效果图: 思路: 首先绘制直线,然后等分直线绘制点: 绘制点的时候把X值存到集合中. 然后绘制背景图片,以及图片上的数字. 点击事件down的时候 ...
- android自定义view案例,Android自定义View的实现方法实例详解
一.自绘控件 下面我们准备来自定义一个计数器View,这个View可以响应用户的点击事件,并自动记录一共点击了多少次.新建一个CounterView继承自View,代码如下所示: 可以看到,首先我们在 ...
- android自定义抽奖,Android自定义view制作抽奖转盘
本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplicati ...
- android代码实现手机加速功能,Android自定义View实现内存清理加速球效果
Android自定义View实现内存清理加速球效果 发布时间:2020-09-21 22:21:57 来源:脚本之家 阅读:105 作者:程序员的自我反思 前言 用过猎豹清理大师或者相类似的安全软件, ...
- android view 渐变动画,Android自定义view渐变圆形动画
本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...
- 自定义圆形倒计时Android,Android自定义View倒计时圆
本文实例为大家分享了Android自定义View倒计时圆的具体代码,供大家参考,具体内容如下 创建attr 创建DisplayUtil 类 import android.content.Context ...
最新文章
- html xhtml and css,HTML与XHTML的重要区别
- Android文档-开发者指南-第一部分:入门-中英文对照版
- AngularJS学习笔记(1)——MVC模式的清单列表效果
- 2018.5.28 PSOC第一枪:基于cypress的蓝牙开发
- 从零开始开发JVM语言(七)语义分析的起步
- 课节5:图神经网络算法(二):GraphSage实践
- php无需鉴权的接口,thinkphp5-restfulapi 博客 接口鉴权应用
- H3C交换机创建用户
- 魏俊妮《人力资源体系搭建专家》
- c语言二级选择题APP,C语言二级题库
- MySQL读写分离原理
- Java实现 蓝桥杯 算法训练 相邻数对(暴力)
- Java方法篇——String方法
- 新一代图片编解码技术在淘宝的应用及落地
- 正则表达式re之模块函数和编译标志
- 如何使用TEQC 分析rinex3 格式的数据
- 小程序实现3D轮播图效果
- python输入百分制成绩输出成绩等级_给出一百分制成绩 要求输出成绩等级
- selenium~初识自动化测试之元素的定位、对象的操作、浏览器的操作、键盘事件、鼠标事件
- 等待事件enq TX row lock contention分析