安卓自定义画板

包含功能:

包含  获取当前画板的截图、设置画笔样式、获取画笔样式、设置画笔宽度、获取画笔宽度、设置画笔颜色、获取画笔颜色、加载图片、获取图片位图对象、设置图片位图对象,并在画布上绘制图片、撤销上一步操作、重做上一步撤销的操作、清空所有绘图路径,重新绘制位图

 自定义视图组件

package com.zx.drawing_board;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;public class DrawingBoard extends View {// 上下文对象,用于获取资源和应用程序信息public Context context;// 画布对象,用于绘制图形public Canvas canvas;// 画笔对象,用于设置绘制样式和颜色public Paint paint;// 位图对象,用于在其中进行绘制操作public Bitmap bitmap;// 绘制路径对象,记录用户绘制的路径public Path path;// 图片的URI地址public Uri uri;// 图片位图对象,用于加载图片private Bitmap mImageBitmap;// 保存用户绘制路径的栈结构private Stack<Path> paths = new Stack<>();/*** 构造函数,创建一个新的FingerPainterView对象* @param context 上下文对象,用于获取资源和应用程序信息*/public DrawingBoard(Context context) {super(context);// 执行初始化方法init(context);}/*** 构造函数,创建一个新的FingerPainterView对象* @param context 上下文对象,用于获取资源和应用程序信息* @param attrs 属性集合对象,用于获取视图的自定义属性*/public DrawingBoard(Context context, AttributeSet attrs) {super(context, attrs);// 执行初始化方法init(context);}/*** 构造函数,创建一个新的FingerPainterView对象* @param context 上下文对象,用于获取资源和应用程序信息* @param attrs 属性集合对象,用于获取视图的自定义属性* @param defStyle 样式属性,用于设置默认样式*/public DrawingBoard(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// 执行初始化方法init(context);}/*** 获取当前画板的截图* @return 画板的截图*/public Bitmap getScreenshot() {return Bitmap.createBitmap(bitmap);}/*** 初始化方法,设置默认的画笔样式和颜色* @param context 上下文对象,用于获取资源和应用程序信息*/private void init(Context context) {this.context = context;// 创建路径对象和画笔对象path = new Path();paint = new Paint();// 默认的画笔样式和颜色paint.setAntiAlias(true);paint.setStyle(Paint.Style.STROKE);paint.setStrokeJoin(Paint.Join.ROUND);paint.setStrokeWidth(20);paint.setStrokeCap(Paint.Cap.ROUND);paint.setARGB(255,0,0,0);}/*** 设置画笔样式* @param brush 画笔样式*/public void setBrush(Paint.Cap brush) {paint.setStrokeCap(brush);}/*** 获取画笔样式* @return 画笔样式*/public Paint.Cap getBrush() {return paint.getStrokeCap();}/*** 设置画笔宽度* @param width 画笔宽度*/public void setBrushWidth(int width) {paint.setStrokeWidth(width);}/*** 获取画笔宽度* @return 画笔宽度*/public int getBrushWidth() {return (int) paint.getStrokeWidth();}/*** 设置画笔颜色* @param colour 画笔颜色*/public void setColour(int colour) {paint.setColor(colour);}/*** 获取画笔颜色* @return 画笔颜色*/public int getColour() {return paint.getColor();}/*** 加载图片* @param uri 图片的URI地址*/public void load(Uri uri) {this.uri = uri;}/*** 获取图片位图对象* @return 图片位图对象*/public Bitmap getmImageBitmap() {return mImageBitmap;}/*** 设置图片位图对象,并在画布上绘制图片* @param mImageBitmap 图片位图对象*/public void setmImageBitmap(Bitmap mImageBitmap) {this.mImageBitmap = mImageBitmap;canvas.drawColor(Color.WHITE);canvas.drawBitmap(mImageBitmap, 0, 0, paint);}/*** 撤销上一步操作*/public void undo() {if (!paths.isEmpty()) {// 移除最近的路径,并重新绘制位图paths.pop();redrawBitmap();}}/*** 重做上一步撤销的操作*/public void redo() {if (!paths.isEmpty()) {// 将最近撤销的路径重新添加到绘图路径中,并重新绘制位图Path lastPath = paths.peek();paths.push(new Path(lastPath));redrawBitmap();}}/*** 清空所有绘图路径,重新绘制位图*/public void clear() {paths.clear();redrawBitmap();}@Overridepublic Parcelable onSaveInstanceState() {Bundle bundle = new Bundle();// 保存父类视图状态bundle.putParcelable("superState", super.onSaveInstanceState());try {// 将位图保存到临时缓存文件中,以克服Binder事务大小限制File f = File.createTempFile("fingerpaint", ".png", context.getCacheDir());bitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(f));// 将临时文件名保存到bundle中bundle.putString("tempfile", f.getAbsolutePath());} catch(IOException e) {Log.e("FingerPainterView", e.toString());}return bundle;}@Overridepublic void onRestoreInstanceState(Parcelable state) {if (state instanceof Bundle) {Bundle bundle = (Bundle) state;try {// 从bundle中获取缓存文件File f = new File(bundle.getString("tempfile"));Bitmap b = BitmapFactory.decodeStream(new FileInputStream(f));// 需要复制位图以创建可变版本bitmap = b.copy(b.getConfig(), true);b.recycle();f.delete();} catch(IOException e) {Log.e("FingerPainterView", e.toString());}state = bundle.getParcelable("superState");}super.onRestoreInstanceState(state);}@Overrideprotected void onDraw(Canvas canvas) {// 画布是白色的,并在顶部绘制带有alpha通道的位图canvas.drawColor(Color.WHITE);canvas.drawBitmap(bitmap, 0, 0, paint);// 显示当前的绘图路径for (Path p : paths) {canvas.drawPath(p, paint);}canvas.drawPath(path, paint);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {// 在Activity创建后,当视图被膨胀时调用if(bitmap==null) {if(uri!=null) {try {// 尝试加载提供的uri,并进行缩放以适应我们的画布InputStream stream = context.getContentResolver().openInputStream(uri);Bitmap bm = BitmapFactory.decodeStream(stream);bitmap  = Bitmap.createScaledBitmap(bm, Math.max(w, h), Math.max(w, h), false);stream.close();bm.recycle();} catch(IOException e) {Log.e("FingerPainterView", e.toString());}}else {// 创建一个正方形位图,以便即使在旋转到横向时也可绘制bitmap = Bitmap.createBitmap(Math.max(w,h), Math.max(w,h), Bitmap.Config.ARGB_8888);}}canvas = new Canvas(bitmap);}/*** 触摸事件处理方法,用于绘制路径*/@Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 按下手指时,重置路径并移动到指定位置path.reset();path.moveTo(x, y);path.lineTo(x, y);invalidate();break;case MotionEvent.ACTION_MOVE:// 手指移动时,连线到当前位置path.lineTo(x, y);invalidate();break;case MotionEvent.ACTION_UP:// 手指抬起时,将路径保存,并重置路径paths.push(new Path(path));path.reset();invalidate();break;}return true;}/*** 重新绘制位图,根据当前的绘图路径*/private void redrawBitmap() {bitmap.eraseColor(Color.WHITE);for (Path p : paths) {canvas.drawPath(p, paint);}invalidate();}
}

用法

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.zx.drawing_board.DrawingBoardandroid:id="@+id/fp"android:layout_width="match_parent"android:layout_height="match_parent"/></RelativeLayout>

效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/683968.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Dirty PageTable

前言 Dirty PageTable 是一种针对堆相关漏洞的利用手法&#xff0c;主要就是针对 PTE 进行攻击。 参考文章&#xff1a; Dirty Pagetable: A Novel Exploitation Technique To Rule Linux Kernel – 该利用方式提出原文 上述文章已经讲的非常清楚了&#xff0c;就是实操写 e…

挑战杯 python区块链实现 - proof of work工作量证明共识算法

文章目录 0 前言1 区块链基础1.1 比特币内部结构1.2 实现的区块链数据结构1.3 注意点1.4 区块链的核心-工作量证明算法1.4.1 拜占庭将军问题1.4.2 解决办法1.4.3 代码实现 2 快速实现一个区块链2.1 什么是区块链2.2 一个完整的快包含什么2.3 什么是挖矿2.4 工作量证明算法&…

CentOS7.9+Kubernetes1.29.2+Docker25.0.3高可用集群二进制部署

CentOS7.9Kubernetes1.29.2Docker25.0.3高可用集群二进制部署 Kubernetes高可用集群&#xff08;Kubernetes1.29.2Docker25.0.3&#xff09;二进制部署二进制软件部署flannel v0.22.3网络&#xff0c;使用的etcd是版本3&#xff0c;与之前使用版本2不同。查看官方文档进行了解…

红队打靶练习:HACK ME PLEASE: 1

信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:69:c7:bf, IPv4: 192.168.61.128 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.61.2 00:50:56:f0:df:20 …

大学建筑专业的搜题软件?大学搜题工具中的高级搜索功能有哪些? #学习方法#微信#经验分享

学习和考试是大学生生活中不可避免的一部分&#xff0c;而在这个信息爆炸的时代&#xff0c;如何快速有效地获取学习资源和解答问题成为了大学生们共同面临的难题。为了解决这个问题&#xff0c;搜题和学习软件应运而生。今天&#xff0c;我将为大家介绍几款备受大学生青睐的搜…

Python魔法方法 单例模式

前言 本文介绍一下python中常用的魔法方法以及面向对象中非常重要的单例模式。 魔法方法 python中一切皆对象&#xff0c;因为python是面向对象的编程语言。python给类和对象提供了大量的内置方法&#xff0c;这些内置方法也称魔法方法。这些魔法方法总是在某种条件下自动触…

探索设计模式的魅力:创建型设计模式的比较与决策

设计模式专栏&#xff1a;http://t.csdnimg.cn/U54zu 目录 一、设计模式概览 1.1 创建型模式 二、比较创建型设计模式 1.1 适用场景典型用例 1.2 关键要素与差异对比 1.3 结构图 三、模式选择指南 3.1 场景分析 3.2 决策流程图 四、结语 4.1 优势 4.2 考量因素 一、…

node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查

文章目录 ⭐前言⭐ 功能设计与实现💖 node后端操作数据库实现增删改查💖 vue3前端实现增删改查⭐ 效果⭐ 总结⭐ 结束⭐结束⭐前言 大家好,我是yma16,本文分享关于 node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查。 技术选型 前端:vite+vue3+antd 后端:…

使用radial-gradient完成弧形凹陷的绘制

1、效果如下图 我在微信小程序中制作的 2、代码如下 <style>.header {position: relative;width: 200px;height: 200px;overflow: hidden;}.header .circle {--circleValue: 500px;position: absolute;bottom: 0;left: 50%;width: 100%;height: var(--circleValue);trans…

[OPEN SQL] 修改数据

MODIFY语句用于修改数据库表中的数据 MODIFY拥有INSERT和UPDATE的操作&#xff0c;如果数据库表中不存在符合条件的数据则会添加该条新数据&#xff0c;反之数据库表中存在符合条件的数据则会更新该条数据 本次操作使用的数据库表为SCUSTOM&#xff0c;其字段内容如下所示 航…

【git】.gitignore 的匹配规则

每行一个规则&#xff1a;每行只能包含一个规则&#xff0c;多个规则需要分别写在不同的行上。 示例&#xff1a; # 忽略日志文件 logs/ # 忽略临时文件 temp.txt种类匹配&#xff1a; 文件&#xff1a;在规则的开头指定文件名或路径&#xff0c;如 file.txt。 示例&#xff1a…

HGAME2024 WEEK2 wp webmisc

web What the cow say? 进入容器有个输入框&#xff0c;尝试ssti、命令执行、代码执行等&#xff0c;最后发现可使用反引号执行命令&#xff1b; 输入 nl app.py 可查看源代码&#xff0c;有功能具体实现、过滤之类的&#xff1b; flag在 /flag_is_here home/flag_c0w54y 中…

每日OJ题_递归②_力扣21. 合并两个有序链表

目录 力扣21. 合并两个有序链表 解析代码 力扣21. 合并两个有序链表 21. 合并两个有序链表 难度 简单 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4]…

CSS设置盒子阴影

语法 box-shadow: *h-shadow v-shadow blur spread color* inset; 注释: box-shadow向框添加一个或多个阴影. 该属性是由逗号分隔的阴影列表,每个阴影由2-4个长度值、可选的颜色值及可选的inset关键词来规定。省略长度的值是0。 外阴影 a、给元素右边框和下边框加外阴影——把…

LabVIEW虚拟测试与分析仪

LabVIEW虚拟测试与分析仪 在现代工程技术领域&#xff0c;虚拟仪器的开发和应用已成为一种趋势。利用LabVIEW软件平台开发的虚拟测试与分析仪器进行展开&#xff0c;实现工程测试和分析中的实际需求。通过结合LabVIEW的强大功能和灵活性&#xff0c;成功实现了一套高效、精确的…

Gemini 1.5 Pro揭秘:Google DeepMind新一代AI模型如何突破千万级别词汇限制?

Gemini 1.5 Pro 发布&#xff01; 这款模型凭借其超长的上下文处理能力脱颖而出&#xff0c;支持10M tokens。 它的多模态特性意味着&#xff0c;无论面对多么庞大复杂的内容&#xff0c;Gemini 1.5 Pro都能游刃有余地应对。 在AI的世界里&#xff0c;上下文的理解如同记忆的…

嵌入式中UART通信的方法

UART是一种异步全双工串行通信协议&#xff0c;由 Tx 和 Rx 两根数据线组成&#xff0c;因为没有参考时钟信号&#xff0c;所以通信的双方必须约定串口波特率、数据位宽、奇偶校验位、停止位等配置参数&#xff0c;从而按照相同的速率进行通信。 异步通信以一个字符为传输单位…

插值(一)——多项式插值(C++)

插值 插值的作用是可以将原本比较难计算的函数转换为误差在一定范围内的多项式&#xff0c;比如在单片机中直接计算 x 、 log ⁡ 2 x \sqrt{x}、\log_2x x ​、log2​x之类的函数是比较麻烦的&#xff0c;但是使用插值的方法就可以将其转换为误差可控的只有乘法和加减法的多项…

MySQL学习记录——팔 函数

文章目录 1、日期函数2、字符串函数3、数学函数4、其它函数 1、日期函数 //获取日期 select current_date(); //获取时间 select current_time(); //获取时间戳, 格式为日期时间 select current_timestamp(); //获取当前时间, 格式为日期时间 select now(); //获取参数的日期部…

Leetcode-1572. 矩阵对角线元素的和

题目&#xff1a; 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;25 解释&#xff1a;对角线…