安卓自定义画板

包含功能:

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

 自定义视图组件

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();
    }
    @Override
    public 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;
    }

    @Override
    public 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);
    }

    @Override
    protected 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);
    }

    @Override
    protected 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);
    }

    /**
     * 触摸事件处理方法,用于绘制路径
     */
    @Override
    public 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.DrawingBoard
        android:id="@+id/fp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

效果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VScode中配置 C/C++ 环境 | IT拯救者

文章目录 0 引言1. 下载编辑器VScode2. 下载编译器MinGW并解压3. 将MinGW添加至环境变量4. 配置VScode插件5. 运行代码6. 调整和优化7. 提示8. 例行格式条款9. 例行格式条款 0 引言 由于VScode毛毛张使用不习惯&#xff0c;因此配置教程记不住&#xff0c;不过毛毛张看到一篇不…

论文阅读-PIM-tree:一种面向内存处理的抗偏移索引

论文名称&#xff1a;PIM-tree: A Skew-resistant Index for Processing-in-Memory 摘要 当今的内存索引性能受到内存延迟/带宽瓶颈的限制。Processing-in-memory (PIM) 是一种新兴的方法&#xff0c;可能通过实现低延迟内存访问&#xff0c;其聚合内存带宽随 PIM 节点数量扩…

力扣1732. 找到最高海拔(前缀和)

Problem: 1732. 找到最高海拔 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.求取数组gain的大小 n n n; 2.定义一个大小为 n 1 n 1 n1的数组preSum; 3.先求取前 n n n个元素的前缀和&#xff0c;再最后单独处理preSum[n];其中preSum[n] preSum[n - 1] gai…

Leetcode-54. 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#xff1a;matrix [[1,2,3,…

LeetCode 每日一题 Day 62 - 75

1686. 石子游戏 VI Alice 和 Bob 轮流玩一个游戏&#xff0c;Alice 先手。 一堆石子里总共有 n 个石子&#xff0c;轮到某个玩家时&#xff0c;他可以 移出 一个石子并得到这个石子的价值。Alice 和 Bob 对石子价值有 不一样的的评判标准 。双方都知道对方的评判标准。 给你…

【论文精读】CLIP

摘要 以往基于自然语言监督的图像表示学习方法如ConVIRT&#xff0c;只在一二十万张图像的小规模数据集上训练&#xff0c;限制了该方法能发挥的性能。故本文研究了在大规模自然语言监督下训练的图像分类器的性能&#xff0c;具体有&#xff1a; 借助互联网上大量公开可用数据…

【Android】使用Apktool反编译Apk文件

文章目录 1. 下载Apktool1.1 Apktool官网下载1.2 百度网盘下载 2. 安装Apktool3. 使用Apktool3.1 配置Java环境3.2 准备Apk文件3.3 反编译Apk文件3.3.1 解包Apk文件3.3.2 修改Apk文件3.3.3 打包Apk文件3.3.4 签名Apk文件 1. 下载Apktool 要使用Apktool&#xff0c;需要准备好 …

如何手机搜学法减分答案? #媒体#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式&#xff0c;可以快速查找问题解析&#xff0c;加深对题目答案的理解。 1.证件照全能管家&#xff08;APP&#xff09; 一个非常好用的证件照APP 常用的证件照尺寸和底色都有、日常的证件照编辑完全够用&#…

【PyQt6】QScreen 屏幕截屏

文章目录 0 环境1 简介2 QScreen 类2.1 获取 QScreen 的对象2.2 QScreen 的常见信息 3. 示例代码 0 环境 - Python 3.12.1 - PyQt6 6.6.1 pip install PyQt6 PyQt6-Qt6 6.6.1 默认安装PyQt6-sip 13.6.…

C语言学习day13:for循环练习(生成随机数)

题目&#xff1a; 通过程序随机一个1-100的数&#xff0c;用户通过键盘输入数字 看是否匹配&#xff0c;匹配成功则跳出循环&#xff0c;失败则继续循环。 思路&#xff1a; 然后生成一个随机数需要写成死循环&#xff0c;同时需要有其他的出口可以写一个提示&#xff0c;比…

书生浦语大模型实战营-课程作业(3)

下载sentence_transformer的代码运行情况。sentence_transformer用于embedding&#xff08;转向量&#xff09; 本地构建持久化向量数据库。就是把txt和md文件抽取出纯文本&#xff0c;分割成定长&#xff08;500&#xff09;后转换成向量&#xff0c;保存到本地&#xff0c;称…

压缩PDF的大小-Adobe Acrobat Pro

经常遇到上传的pdf太大&#xff0c;无法成功上传。 今天找到一个方法&#xff1a; 打开Adobe Acrobat Pro软件 → 文件 → 另存为其他&#xff08;H&#xff09;... →缩小大小的PDF 版本选择 4.0 最低的版本。 文件由9M变为1.5M。

Vi 和 Vim 编辑器

Vi 和 Vim 编辑器 vi 和 vim 的基本介绍 Linux 系统会内置 vi 文本编辑器 Vim 具有程序编辑的能力&#xff0c;可以看做是 Vi 的增强版本&#xff0c;可以主动的以字体颜色辨别语法的正确性&#xff0c;方便程序设计。 代码补完、编译及错误跳转等方便编程的功能特别丰富&…

C++ bfs反向搜索(五十七)【第四篇】

今天我们来学习bfs的反向搜索。 1.反向搜索 反向搜索&#xff1a;是从目标状态出发进行的搜索&#xff0c;一般用于终点状态唯一&#xff0c;起点状态有多种&#xff0c;且状态转移是可逆的&#xff08;无向边&#xff09;情况。 例题&#xff1a;在一个长度为 n 的坐标轴上&a…

备战蓝桥杯---图论之最短路dijkstra算法

目录 先分个类吧&#xff1a; 1.对于有向无环图&#xff0c;我们直接拓扑排序&#xff0c;和AOE网类似&#xff0c;把取max改成min即可。 2.边权全部相等&#xff0c;直接BFS即可 3.单源点最短路 从一个点出发&#xff0c;到达其他顶点的最短路长度。 Dijkstra算法&#x…

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

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

AJAX——接口文档

1 接口文档 接口文档&#xff1a;描述接口的文章 接口&#xff1a;使用AJAX和服务器通讯时&#xff0c;使用的URL&#xff0c;请求方法&#xff0c;以及参数 传送门&#xff1a;AJAX阶段接口文档 <!DOCTYPE html> <html lang"en"><head><meta c…

《数电》理论笔记-第3章-常用组合逻辑电路及MSI组合电路模块的应用

一&#xff0c;编码器和译码器 1&#xff0c;编码器 编码:用由0和1组成的代码表示不同的事物。 编码器:实现编码功能的电路&#xff0c; 常见编码器:普通编码器、优先编码器、二进制编码器二-十进制编码器等等 1.1 三位二进制普通编码器和三位二进制优先编码器 1分58秒开始 …

Cocos2dx-lua ScrollView[一]基础篇

一.ScrollView概述 cocos游戏中ScrollView控件大量使用,95%以上的项目都会使用ScrollView,个别游戏可能全部使用翻页的滑动效果。如果想要精通Cocos的UI开发,精通ScrollView控件非常关键,因此对ScrollView的使用进行总结很有必要。 下文缩写说明:sv = ScrollView, item代…

具有集中目录服务器的 P2P 工作方式

P2P 工作方式概述 在 P2P 工作方式下&#xff0c;所有的音频/视频文件都是在普通的互联网用户之间传输。 具有集中目录服务器的 P2P 工作方式 Napster 最早使用 P2P 技术&#xff0c;提供免费下载 MP3 音乐。 Napster 将所有音乐文件的索引信息都集中存放在 Napster 目录服务…
最新文章