Android 画板(简单的自定义控件)

Android Studio 实现的画板App 基于Android Studio 实现的画板App ** 1.实现目标** 随着科技的不断发展和社会的不断进步,手机越来越流行,成为了人们日常生活和学习的必要工具,人们越来越离不开手机,还在几年前手机仅仅是一个打电话和发短信的工具,但在地球村的今天,手机无论除了可以满足基木的通讯作用以外,还成为了人们办公和娱乐的必不可少的工具。当然手机画板也成为了手机的一个基木的app软件,以前人们想画画必须在纸上或者自己携带的画板上,对于现在人们快节奏的生活不是非常方便。而手机画板的出现打破了这个固有的僵局,它可以满足人 阅读详情

这段时间做了几个自定义控件,整理到博客上供大家参考,先看下画板效果图:

这个画板很简单,但基本的功能都具有了,橡皮擦,清除,撤销,反撤销,保存。想要实现更多的功能,比如说更改画笔颜色,大小等等功能,继续往上加代码就可以了。
实现这个画板的逻辑很简单,就是自定义一个画板控件,下面看下主要的代码:

public class DrawingBoardView extends View {

    // 画图的模式(默认是画笔)
    private DrawMode mDrawMode = DrawMode.PaintMode;
    // 画笔
    private Paint mPaint;
    // 画笔颜色
    private int mPaintColor = Color.RED;
    // 画笔宽度
    private int mPaintSize = diptopx(5);
    // 橡皮擦的宽度
    private int mEraserSize = diptopx(36);
    // 缓冲的位图
    private Bitmap mBufferBitmap;
    // 缓冲的画布
    private Canvas mBufferCanvas;
    // 当前控件的宽
    private int mWidth;
    // 当前控件的高
    private int mHight;
    // 画布的颜色
    private int mCanvasColor = Color.WHITE;
    // 上次的位置
    private float mLastX;
    private float mLastY;
    // 路径
    private Path mPath;
    // 设置图形混合模式为清除
    private PorterDuffXfermode mEraserMode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
    // 保存的路径
    private List<DrawPathInfo> savePaths;
    // 当前的路径
    private List<DrawPathInfo> currPaths;
    // 最多保存20条路径
    private int MAX_PATH = 20;


    public DrawingBoardView(Context context) {
        this(context, null);
    }

    public DrawingBoardView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DrawingBoardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPath();
        initPaint();
    }

    /**
     * 初始化路径
     */
    private void initPath() {
        mPath = new Path();
        savePaths = new ArrayList<DrawPathInfo>();
        currPaths = new ArrayList<DrawPathInfo>();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(mWidth, mHight);
        initCanvas();
    }

    /**
     * 初始化缓冲画布
     */
    private void initCanvas() {
        // 创建一个BITMAP
        mBufferBitmap = Bitmap.createBitmap(mWidth, mHight, Bitmap.Config.ARGB_8888);
        // 创建一个画布,所有mBufferCanvas画的东西都被保存在了mBufferBitmap中
        mBufferCanvas = new Canvas(mBufferBitmap);
        // 设置画布颜色
        mBufferCanvas.drawColor(mCanvasColor);
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        // 设置画笔抗锯齿和抖动
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        // 画笔只描边
        mPaint.setStyle(Paint.Style.STROKE);
        // 设置画笔颜色
        mPaint.setColor(mPaintColor);
        // 设置画笔宽度
        mPaint.setStrokeWidth(mPaintSize);
        // 设置圆形线帽
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        // 设置线段连接处圆角
        mPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mBufferBitmap, 0, 0, null);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                mPath.moveTo(mLastX, mLastY);
                break;
            case MotionEvent.ACTION_MOVE:
                // 画出路径
                mPath.quadTo(mLastX, mLastY, (mLastX + x) / 2, (mLastY + y) / 2);
                mBufferCanvas.drawPath(mPath, mPaint);
                invalidate();
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
                // 保存路径
                saveDrawPaths();
                mPath.reset();
                break;
        }
        return true;
    }

    /**
     * 保存画的路径
     */
    private void saveDrawPaths() {
        if (savePaths.size() == MAX_PATH) {
            savePaths.remove(0);
        }
        savePaths.clear();
        savePaths.addAll(currPaths);
        Path cachePath = new Path(mPath);
        Paint cachePaint = new Paint(mPaint);
        savePaths.add(new DrawPathInfo(cachePaint, cachePath));
        currPaths.add(new DrawPathInfo(cachePaint, cachePath));
    }

    public int diptopx(float dipValue) {
        final float scale = this.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    /**
     * 擦除画布
     */
    public void clean() {
        savePaths.clear();
        currPaths.clear();
        // 把位图擦成透明的
        mBufferBitmap.eraseColor(Color.TRANSPARENT);
        invalidate();
    }

    /**
     * 设置画笔模式
     *
     * @param mode
     */
    public void setMode(DrawMode mode) {
        if (mode != mDrawMode) {
            if (mode == DrawMode.EraserMode) {
                mPaint.setStrokeWidth(mEraserSize);
                mPaint.setXfermode(mEraserMode);
                mPaint.setColor(mCanvasColor);
            } else {
                mPaint.setXfermode(null);
                mPaint.setColor(mPaintColor);
                mPaint.setStrokeWidth(mPaintSize);
            }
            mDrawMode = mode;
        }
    }

    /**
     * 获取当前模式
     *
     * @return
     */
    public DrawMode getMode() {
        return mDrawMode;
    }

    /**
     * 上一步  撤销
     */
    public void lastStep() {
        if (currPaths.size() > 0) {
            currPaths.remove(currPaths.size() - 1);
            redrawBitmap();
        }
    }

    /**
     * 下一步  凡撤销
     */
    public void nextStep() {
        if (currPaths != savePaths) {
            if (savePaths.size() > currPaths.size()) {
                currPaths.add(savePaths.get(currPaths.size()));
                redrawBitmap();
            }
        }
    }

    /**
     * 重绘位图
     */
    private void redrawBitmap() {
        mBufferBitmap.eraseColor(Color.TRANSPARENT);
        for (int i = 0; i < currPaths.size(); i++) {
            DrawPathInfo path = currPaths.get(i);
            mBufferCanvas.drawPath(path.getPath(), path.getPaint());
        }
        invalidate();
    }

    /**
     *  保存图片
     */
    public void save() {
        mBufferCanvas.save(Canvas.ALL_SAVE_FLAG);
        mBufferCanvas.restore();
        String path = Environment.getExternalStorageDirectory().getPath();
        //存储路径
        File appDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        if (appDir == null) {
            return;
        }
        String fileName = System.currentTimeMillis() + ".jpg";
        File file = new File(appDir, fileName);
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            mBufferBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
            fileOutputStream.close();
            Toast.makeText(getContext(), "保存成功", Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

源码已上传到GitHub,欢迎fark,star点击查看GitHub源码

android简单调色画板 这次是基于调色板上改的画板,上面的---------1.---------------2.------------------3.-------------------4是相互对应的,方便看主要代码: (MainActivity.java) package com.example.color; import java.io.File; import java.io.FileOutputSt 阅读详情

相关推荐

一个涂鸦画板控件

这次带来一个涂鸦画板控件。之前一个项目里需要做一个功能:用户在屏幕上签名,然后将签名保存成图片。于是就顺手写了这个控件,起个名字叫DrawView,这里拿出来给大家分享一下。首先说一下原理。原理其实很简单,我们在屏幕上画的一笔,本质上就是一堆坐标点的集合,把这个集合保存在Path中,然后调用canvas.drawPath就可以把这条线画出来了。而触控产生的坐标点可以在onTouchEvent里采集

Sure_Min的博客 1028

【安卓】自定义View实现画板涂鸦等功能

绘制主要用到Paint和Path,大体思路都是监听OnTouchEvent事件,绘制路径或者其他图案,开发这个项目用时一周。

小李不背锅的博客 2030

自定义控件-画板,橡皮擦,刮刮乐

画板效果图页面代码public class ActionerView extends View { private Paint mPaint = new Paint(); private Path mPath = new Path();//手指滑动路径 private Canvas mCanvas;//缓存画布 private Bitmap mBitmap;//缓存图

pengkv的专栏 2391

【Android入门03】从0开发一个绘画板

前言 前2章我们已经学习了如何制作一个计算器,如何制作一个手势解锁页面。不过这些都只是开胃菜,并不能算一个比较完整的程序。今天我们就来学习一下如何开发一个较为完整的应用——绘画板。本文主要分为界面搭建和实现逻辑两个部分。 需求分析 在进行程序开发之前,我们首先需要明确这个应用应该具备什么功能。一个合格的绘画板,首先应该具有如下功能: 1.调节画笔粗细,画笔颜色功能 2.橡皮擦功能 3.撤销功能 在实现了基本功能后,你还可以选择对功能进行扩充,如: 1.将作品保存到本地,并进行分享 2.从本地相册选择图片,

n0ahmayberry的博客 1660

android自定义View画板

android 自定义view 画板

安路科技FPSoC开发实战:从芯片特性到边缘计算应用

本文深入解析安路科技FPSoC芯片在边缘计算领域的开发实战,重点介绍SALDRAGON系列芯片的异构计算架构优势,包括RISC-V/ARM处理器与FPGA的协同工作。通过工业控制和智能视觉应用案例,展示硬件加速实现与低功耗设计技巧,帮助开发者高效利用FPSoC进行边缘计算应用开发。

weixin_29248611的博客 375

【亲测免费】 推荐开源项目:DrawingView - 在Android上绘制的艺术之旅

推荐开源项目:DrawingView - 在Android上绘制的艺术之旅 该项目——,由开发者hcz017创建并维护,提供了一个自定义的Android视图组件,允许用户在应用中自由绘画,实现丰富的绘图功能。如果你正在寻找一个轻松集成到你的Android应用中的画板解决方案,那么DrawingView可能是你的理想选择。 技术分析 DrawingView的核心是通过重写Android视图的一些关键...

gitblog_00066的博客 1043

信贷审批中的那些坑:从零开始搭建风控策略体系的5个关键步骤

本文深入探讨了信贷审批中的风控策略体系搭建,从数据陷阱识别、规则冲突诊断到反欺诈攻防,提供了5个关键步骤的实战指南。重点解析了如何通过特征工程、决策树优化和模型可解释性技术,构建动态平衡的风控体系,有效降低逾期风险并提升审批效率。

weixin_29253711的博客 411

Android画板(三):WhiteBoard画板开源库

SketchBoard项目简介 SketchBoard是一个可涂鸦、绘图、添加文字、图像(可旋转缩放)、背景的Fragment,其中主要由SketchView利用matrix完成所有图形绘制操作。 tips:图像旋转缩放高仿美图APP的操作方式 tips: 感谢

qq_32138419的博客 3467

实战指南:用Python实现ONVIF WS-Discovery设备发现(附完整代码)

本文提供了一份详细的实战指南,介绍如何使用Python实现基于ONVIF标准的WS-Discovery设备发现协议。文章深入解析了协议原理、网络通信模型,并提供了完整的代码实现,帮助开发者快速构建智能安防和物联网应用中的设备自动发现工具,有效提升网络设备集成与管理的效率。

chair的专栏 1113

Qt Location实战:5分钟搞定地图插件切换与卫星图源加载(附完整代码)

本文是Qt Location开发系列教程的实战指南,详细讲解如何在Qt应用中快速实现地图插件(如Esri、OSM、MapboxGL)的动态切换与卫星图源的加载。通过清晰的代码示例,帮助开发者理解插件机制,解决常见配置问题,并在5分钟内构建出具备交互式地图切换功能的应用程序。

kafka6courier的博客 523

android画板控件,GitHub - imaiya/PainterView: Android画板控件,可以写字画画并生成图片...

PainterViewAndroid画板控件,可以写字画画并生成图片引用最新版本号GradleProject.gradleallprojects {repositories {jcenter()maven { url "https://jitpack.io" }}}app.gradlecompile 'com.github.chenzhenboy:PainterView:0.9'Mavenjitp...

weixin_42360762的博客 411

YOLO格式数据集划分

YOLO格式的数据集划分 标题标签格式txt import os import cv2 import random from PIL import Image # os.makedirs('labels_train') # os.makedirs('labels_val') img_train_path ='./img_train/' #图片训练集保存路径 img_val_path = './img_val/' #图片验证集保存路径 labels_train_path = './labels_t

weixin_51154380的博客 3306

Android 绘制手势路线制作图画板并把View保存为图片

先看看效果图 一个简单的图画板 当然核心就是自定义的画板控件 package com.example.test.myapplication; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics

liaoyi的博客 1214

Vue3 - 实现用户上传本地视频文件到服务器+上传进度条+暂停续传+中断取消上传等,视频上传后可在线播放预览已上传的视频,视频文件上传视频回显示例代码(支持Element、Antd等upload组件

实现视频上传和视频预览,vue3实现视频上传功能,上传视频回显,视频流上传播放功能,vue与axios上传视频并显示上传进度,upload组件实现视频文件上传视频回显,upload组件实现视频上传及进度条展示,视频上传及实时进度条显示,视频上传过程中如何中断上传,VUE3+element 上传视频,大文件分片上传+断点续传,axios大文件切片上传,Vue3上传视频后如何播放预览,上传本地视频到后端服务器及在线预览播放视频+移除视频+进度条上传,将已选视频上传服务端并可预览视频,详细实现选择视频并上传到服务

🏆 前端领域优质创作者 3112

【华为OD机试真题 新系统】1029、字符串处理 | 机试真题+思路参考+代码解析(C++、Java、Py、C语言、JS)

【华为OD机试真题 新系统】1029、字符串处理 | 机试真题+思路参考+代码解析(C++、Java、Py、C语言、JS) 华为od机试,华为od机试真题,华为od机试E卷,华为odE卷,华为od机试真题E卷,华为OD,华为OD机试,华为OD机试真题,华为od机考,华为od机试D卷,华为od机试真题D卷,华为od手撕代码,华为od,华为od机试C卷,华为od e卷,华为2026e卷,od机试,2026E卷,华为OD机试E卷,什么是华为od,华为od介绍,全面了解华为od,2026华为od,如何备战华为od,

KJ.JK 218

Android自定义控件2-简单的写字板控件

概述上一篇文章我们对自定义控件进行了一个大体的知识介绍。今天就来学习自定义一个简单的写字板控件。先来看看效果图就是简单的根据手指写下的轨迹去画出内容实现在上一篇文章里提到了android官方给出的自定义控件需要考虑以下几点: 创建View 处理View的布局 绘制View 与用户进行交互 优化已定义的View 就按照这个步骤来完成今天的自定义控件1、创建View上篇提到创建View这一步的时候要考虑

程序员yqy的博客 4203

2025届程序技术类笔试题

希尔排序在直接插入排序的基础上进行了改进,它通过比较相距一定间隔的元素来进行,各轮比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一轮排序为止。某人逆着水流的方向向岸边划船,每次行进前进x米后需要休息一下,休息期间船会自行倒退y米,每次休息后再次行进,前进距离缩短1/5,若起始距离岸边为m米,问:能否到达岸上,需要行进多少次才能上岸。直接插入排序的原理是将第i个元素插入前面i-1个已排好序的记录中,将比第i个元素大的记录依次向后移动一个位置,将第i个元素插入空位。第二轮相邻比较,后移2次。

博客 591

Android自定义控件(View)的基本方法

绘图时所需工具: 画板 ----对应Canvas类 画笔----对应Paint类 简单自定义view: public class BasisView extends View { public BasisView(Context context) { super(context); } public BasisView(Context co...

世俗的博客 1280

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

画板 android自定义控件

画板|自定义控件实现轨迹划线 第一次写博客,因为自己在阅读一些博客有一些难懂的会比较痛苦,所以这篇会尽量介绍清楚。 先上完整代码和效果 public class DrawView extends View { private Path mPath;//路径 private Paint mLinePaint=new Paint();//路径画笔 private fl...

qq_43595398的博客 507

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

从零开始实现Android自定义手写画板

从零开始实现Android自定义手写画板 最近需要实现一个手写签名控件,在搜索多轮后,终于实现了效果,但是一个手写签名,涉及到了自定义View,画板,path等知识发现自己还是有挺多地方不清楚的,下面从零开始实现一个自定义画板,也一个个解释用到的知识点,算是对手写签名用到所需知识的一个总结。 手写顾名思义是在屏幕中获取若干不连续的点,然后把这些点连成一条线。 1.构造方法里面初始化画布背景、画笔、...

hard work 1230

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Android自定义View--简易画板

自定义VIew实现简易画板效果,功能包括清空、选择颜色,选择大小,效果如下 画板布局: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"

xqppw111的博客 2000

Android自定义UI的简单实现

在app开发中经常会用到自定义UI以及效果,那么在安卓中自定义控件的流程是怎么样的呢,本文简单介绍了自定义控件的实现方式以及关键方法。 1. 自定义控件的类型 自定义控件可以简单的分为两类,一类是以继承自基本视图View与视图控件ViewGroup的自定义控件,此类控件默认无可视化UI,包括功能逻辑等都需要从头开始自定义,一切的视图都是继承自View,而ViewGroup则是继...

weixin_30951231的博客 504

自定义控件画板

自定义控件分为三类 自绘式自定义控件 组合式自定义控件 继承式自定义控件 每一个自定义控件都要重写的方法有 public Base(Context context) { this(context,null); } public Base(Context context, AttributeSet attrs) { super(context, ...

weixin_41722852的博客 243

Android 实现简单画板二

一、首先自定义一个画板控件二、创建Activity实现

lovoo的博客 781

Android studio实现画板功能

简单概述 在日常生活中,我们经常会突发一些奇思妙想,或是一个画面,或是几个符号。这时候无法使用拍照或者打字功能实现,想拿笔记下又身边找不到笔。于是我琢磨能不能做一个手机端的画板。 效果图 实现过程 项目布局很简单 让我们来看代码:首先声明画笔,画板,和坐标 public class MainActivity extends AppCompatActivity{ Paint paint; Canvas canvas; ImageView imageview; Bitmap

fjnu_se的博客 1827
上一篇: Android Canvas方法总结
下一篇: Android AsyncTask 源码分析详解
谦行
博客等级 码龄11年 182粉丝 35原创
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值