Xamarin.Android实现手写板的功能

目录

  • 1、背景说明
  • 2、实现效果
  • 3、代码实现
    • 3.1 整体思路
    • 3.2 核心绘画类-PaintView.cs
    • 3.3 对话框类-WritePadDialog.cs
    • 3.4 前端实现类-MainActivity
    • 3.5 布局文件
      • 3.5.1 write_pad.xml
      • 3.5.2 activity_main布局文件
  • 4、知识总结
  • 5、代码下载
  • 6、参考资料

1、背景说明

在实际使用过程中,可能会需要在APP中实现手写板的功能,网上比较多的是Android的实现,因此找了下资料,改了改,实现了Xamarin.Android手写板的功能

2、实现效果

实现的效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、代码实现

3.1 整体思路

Xamarin.Android中实现绘图主要是两种方式Drawable ResourcesCanvas,前者可主要进行类似HtmlCSS之类的功能,后者则实现比较负责的功能,本次主要用到了后者-Canvas

整个思路是这样的:绘画核心部分通过继承View,重写相关方法,从而实现笔迹的追踪及记录。对话框主要是实现文件的保存等操作功能;前端的界面(即MainActivity)实现图像的展示,具体代码如下:

3.2 核心绘画类-PaintView.cs

绘画的核心方法

public class PaintView : View
{private Bitmap mBitmap; //用于存放展示的内容private Path mPath; //路径private Paint mPaint;//关键类private Canvas mCanvas; //画布private int screenWidth, screenHeight;private float currentX, currentY;public PaintView(Context context,int screenWidth,int screenHeight):base(context){this.screenWidth = screenWidth;this.screenHeight = screenHeight;Initialize();}public PaintView(Context context, IAttributeSet attrs) :base(context, attrs){Initialize();}public PaintView(Context context, IAttributeSet attrs, int defStyle) :base(context, attrs, defStyle){Initialize();}//完成初始化的设置private void Initialize(){mPaint = new Paint();mPaint.AntiAlias = true;mPaint.Color = Color.Black;mPaint.StrokeWidth = 5;mPaint.SetStyle(Paint.Style.Stroke);mPath = new Path();mBitmap=Bitmap.CreateBitmap(screenWidth, screenHeight, Bitmap.Config.Argb8888);mCanvas = new Canvas(mBitmap);}//重写绘画方法protected override void OnDraw(Canvas canvas){base.OnDraw(canvas);canvas.DrawBitmap(mBitmap, 0, 0, null);canvas.DrawPath(mPath, mPaint);}//重写监听的事件public override bool OnTouchEvent(MotionEvent e){float x=e.GetX();float y=e.GetY();switch(e.Action){case MotionEventActions.Down:currentX = x;currentY = y;mPath.MoveTo(currentX, currentY);break;case MotionEventActions.Move: currentX = x;currentY = y;mPath.QuadTo(currentX, currentY,x,y);break;case MotionEventActions.Up:mCanvas.DrawPath(mPath, mPaint);break;}Invalidate();return true;}// 缩放public static Bitmap resizeImage(Bitmap bitmap, int width, int height){int originWidth = bitmap.Width;int originHeight = bitmap.Height;float scaleWidth = ((float)width) / originWidth;float scaleHeight = ((float)height) / originHeight;Matrix matrix = new Matrix();matrix.PostScale(scaleWidth, scaleHeight);Bitmap resizedBitmap = Bitmap.CreateBitmap(bitmap, 0, 0, originWidth,originHeight, matrix, true);return resizedBitmap;}//清空public void clear(){if (mCanvas != null){mPath.Reset();mCanvas.DrawColor(Color.Transparent, PorterDuff.Mode.Clear);Invalidate();}}public Bitmap getPaintBitmap(){return resizeImage(mBitmap, 320, 480);}public Path getPath(){return mPath;}}

3.3 对话框类-WritePadDialog.cs

public delegate void Handler(object sender);public class WritePadDialog : Dialog
{private Android.Content.Context mContext;private FrameLayout mFrameLayout;private PaintView mPaintView;private Button mBtnOK, mBtnClear, mBtnCancel;public event Handler WriteDialogListener;public WritePadDialog(Android.Content.Context context) : base(context){mContext=context;}protected override void OnCreate(Bundle savedInstanceState){base.OnCreate(savedInstanceState);RequestWindowFeature(1);//Window.SetFeatureInt(WindowFeatures.NoTitle,5);SetContentView(Resource.Layout.write_pad);mFrameLayout = FindViewById<FrameLayout>(Resource.Id.tablet_view);// 获取屏幕尺寸DisplayMetrics mDisplayMetrics = new DisplayMetrics();Window.WindowManager.DefaultDisplay.GetMetrics(mDisplayMetrics);int screenWidth = mDisplayMetrics.WidthPixels;int screenHeight = mDisplayMetrics.HeightPixels;mPaintView = new PaintView(mContext, screenWidth, screenHeight);mFrameLayout.AddView(mPaintView);mPaintView.RequestFocus();//保存按钮mBtnOK =FindViewById<Button>(Resource.Id.write_pad_ok);mBtnOK.Click += MBtnOK_Click;//清空按钮mBtnClear = FindViewById<Button>(Resource.Id.write_pad_clear);mBtnClear.Click += (o, e) => { mPaintView.clear(); };//取消按钮mBtnCancel = FindViewById<Button>(Resource.Id.write_pad_cancel);mBtnCancel.Click += (o, e) => { Cancel(); };}private void MBtnOK_Click(object sender, EventArgs e){if (mPaintView.getPath().IsEmpty){Toast.MakeText(mContext, "请写下你的大名", ToastLength.Short).Show();return;}WriteDialogListener(mPaintView.getPaintBitmap());Dismiss();}
}

这儿声明了一个委托delegate,主要是想实现通过这个委托,将生成的图像传递出去。就是对话框中的eventWriteDialogListener

3.4 前端实现类-MainActivity

protected override void OnCreate(Bundle savedInstanceState){base.OnCreate(savedInstanceState);Xamarin.Essentials.Platform.Init(this, savedInstanceState);SetContentView(Resource.Layout.activity_main);AndroidX.AppCompat.Widget.Toolbar toolbar = FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.toolbar);SetSupportActionBar(toolbar);FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);fab.Click += FabOnClick;mIVSign = FindViewById<ImageView>(Resource.Id.signImageView);mTVSign = FindViewById<TextView>(Resource.Id.signBtn);mTVSign.Click += MTVSign_Click;}private void MTVSign_Click(object sender, EventArgs e){WritePadDialog mWritePadDialog = new WritePadDialog(this);mWritePadDialog.WriteDialogListener += MWritePadDialog_WriteDialogListener;mWritePadDialog.Show();}private void MWritePadDialog_WriteDialogListener(object sender){mSignBitmap = (Bitmap)sender;createSignFile();mIVSign.SetImageBitmap(mSignBitmap);mTVSign.Visibility = ViewStates.Gone; }//创建文件
private void createSignFile()
{//ByteArrayOutputStream baos = null;MemoryStream baos = null;FileOutputStream fos = null;String path = null;Java.IO.File file = null;try{path = System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal),DateTime.Now.ToString("yyyyMMddHHmmss")+ ".jpg");file = new Java.IO.File(path);fos = new FileOutputStream(file);baos = new MemoryStream();//如果设置成Bitmap.compress(CompressFormat.JPEG, 100, fos) 图片的背景都是黑色的mSignBitmap.Compress(Bitmap.CompressFormat.Png, 100, baos);byte[] b = StreamToBytes(baos);if (b != null){fos.Write(b);}}catch (Java.IO.IOException e){e.PrintStackTrace();}finally{try{if (fos != null){fos.Close();}if (baos != null){baos.Close();}}catch (Java.IO.IOException e){e.PrintStackTrace();}}
}private  byte[] StreamToBytes(Stream stream)
{byte[] bytes = new byte[stream.Length];stream.Read(bytes, 0, bytes.Length);// 设置当前流的位置为流的开始stream.Seek(0, SeekOrigin.Begin);return bytes;
}

这儿有个点,在Java中会存在ByteArrayOutputStream 类,但是在Xamarin中不存在,因此需要进行一个转换。

3.5 布局文件

3.5.1 write_pad.xml

write_pad.xml布局文件如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><FrameLayoutandroid:id="@+id/tablet_view"android:layout_width="fill_parent"android:layout_height="300dp" ></FrameLayout><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:background="@android:drawable/bottom_bar"android:paddingTop="4dp" ><Buttonandroid:id="@+id/write_pad_ok"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"android:text="确定" /><Buttonandroid:id="@+id/write_pad_clear"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"android:text="清除" /><Buttonandroid:id="@+id/write_pad_cancel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"android:text="取消" /></LinearLayout></LinearLayout>

3.5.2 activity_main布局文件

<?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"app:layout_behavior="@string/appbar_scrolling_view_behavior"tools:showIn="@layout/activity_main"><ImageViewandroid:layout_width="match_parent"android:layout_height="400dp"android:id="@+id/signImageView" /><TextViewandroid:id="@+id/signBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="请点击我,进行签名~" /></RelativeLayout>

4、知识总结

里面大量的涉及了Canvas的方法,可以参考官网的这篇文章Android Graphics and Animation

程序中主要使用了Path类和Canvas,具体的知识可以参考资料的第二篇文章,非常好

5、代码下载

代码下载

6、参考资料

1、Android实现手写板和涂鸦功能
2、Android知识总结——Path常用方法解析

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

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

相关文章

【SpringCloud技术专题】「Gateway网关系列」(2)微服务网关服务的Gateway功能配置指南分析

Spring Cloud Gateway简介 Spring Cloud Gateway是Spring Cloud体系的第二代网关组件&#xff0c;基于Spring 5.0的新特性WebFlux进行开发&#xff0c;底层网络通信框架使用的是Netty&#xff0c;所以其吞吐量高、性能强劲&#xff0c;未来将会取代第一代的网关组件Zuul。Spri…

【论文阅读】自动驾驶安全的研究现状与挑战

文章目录 摘要1.引言1.1.自动驾驶安全1.2.攻击面1.3.内容和路线图 2.自动驾驶技术2.1.组成2.2.技术 3.传感器安全3.1.照相机3.2.GNSS&#xff08;全球导航系统&#xff09;/IMU&#xff08;惯性测量单元&#xff09;3.3.超声波传感器3.4.毫米波雷达3.5.激光雷达3.6.多传感器交叉…

【校招VIP】前端基础之post和get

考点介绍&#xff1a; get和post 是网络基础&#xff0c;也是每个前端同学绕不过去的小问题&#xff0c;但是在校招面试中很多同学在基础回答中不到位&#xff0c;或者倒在引申问题里&#xff0c;就丢分了。 『前端基础之post和get』相关题目及解析内容可点击文章末尾链接查看…

Azure Bastion的简单使用

什么是Azure Bastion Azure Bastion 是一个提供安全远程连接到 Azure 虚拟机&#xff08;VM&#xff09;的服务。传统上&#xff0c;访问 VM 需要使用公共 IP 或者设立 VPN 连接&#xff0c;这可能存在一些安全风险。Azure Bastion 提供了一种更安全的方式&#xff0c;它是一个…

JZ36 二叉搜索树与双向链表

问题描述&#xff1a; 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。 问题要求&#xff1a; 1.要求不能创建任何新的结点&#xff0c;只能调整树中结点指针的指向。当转化完成以后&#xff0c;树中节点的左指 针需要指向前驱&#xff0c;树中节点…

selenium +Jmeter 的性能测试

通过Jmeter快速将已有的Selenium 代码以性能测试的方式组织起来&#xff0c;并使用JMeter 丰富的报表展示测试结果 from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By driver …

08.利用Redis实现签到功能

学习目标&#xff1a; 来源&#xff1a;黑马教程 使用Redis中BitMap数据结构使用签到功能和连续签到功能 学习产出&#xff1a; 解决方案&#xff1a; 1. 准备pom环境 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-b…

push github

一、生成密钥 打开git bash执行下面指令&#xff0c;Enter下一步Enter下一步..生成ssh key 密钥&#xff1b; ssh-keygen -t rsa 二、 复制公共密钥到git hub 登录github&#xff0c;在选项setting >> SSH and GPG key >> add new ssh添加刚才的公钥地址即可 验证…

Hadoop入门机安装hadoop

0目录 1.Hadoop入门 2.linux安装hadoop 1.Hadoop入门 定义 Hadoop是一个由Apache基金会所开发的分布式系统基础架构。用户可以在不了解分布式底层细节的情况下&#xff0c;开发分布式程序。充分利用集群的威力进行高速运算和存储。 优势 高可靠性&#xff1a;Hadoop底层维护多…

机器学习中XGBoost算法调参技巧

本文将详细解释XGBoost中十个最常用超参数的介绍&#xff0c;功能和值范围&#xff0c;及如何使用Optuna进行超参数调优。 对于XGBoost来说&#xff0c;默认的超参数是可以正常运行的&#xff0c;但是如果你想获得最佳的效果&#xff0c;那么就需要自行调整一些超参数来匹配你…

docker实践作业

1.安装docker服务&#xff0c;配置镜像加速器 2.下载系统镜像&#xff08;Ubuntu、 centos&#xff09; 3.基于下载的镜像创建两个容器 &#xff08;容器名一个为自己名字全拼&#xff0c;一个为首名字字母&#xff09; 4.容器的启动、 停止及重启操作 5.怎么查看正在运行的容器…

c++都补了c语言哪些坑?

目录 1.命名空间 1.1 定义 1.2 使用 2.缺省参数 2.1 概念 2.2 分类 3.函数重载 4.引用 4.1 概念 4.2 特性 4.3 常引用 4.4 引用和指针的区别 5.内联函数 1.命名空间 在 C/C 中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将…

AR地图微信小程序:数字化时代下地图应用的新突破

随着数字化时代的到来&#xff0c;地图应用成为人们日常生活中不可或缺的工具。而随着增强现实&#xff08;AR&#xff09;技术的快速发展&#xff0c;AR地图微信小程序应运而生&#xff0c;为用户提供了一种全新的地图导航体验。本文将深入探讨AR地图微信小程序的专业性和思考…

安卓系列机型-禁止卸载某个APP 防止误卸载软件 无需root权限

安卓系列机型-禁止安装某软件 防止“沉迷游戏的小孩”操作解析_安卓机器的博客-CSDN博客 上一期讲了如何禁止安装某个app。今天讲下如何禁止卸载某app。正好相反的操作。任何操作有利有弊。主要看使用者如何对待使用。 &#x1f494;&#x1f494;&#x1f494;以腾讯的一款游…

Android Lottie加载gson文件动画

一&#xff1a;Lottie的使用 在你工程的build.gradle文件里添加如下配置 implementation com.airbnb.android:lottie:3.4.0二&#xff1a;布局文件直接引入LottieAnimationView <com.airbnb.lottie.LottieAnimationViewandroid:id"id/lottie_view"android:layout…

2023年国赛 高教社杯数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…

基于Jenkins自动打包并部署docker环境

目录 1、安装docker-ce 2、阿里云镜像加速器 3、构建tomcat 基础镜像 4、构建一个Maven项目 实验环境 操作系统 IP地址 主机名 角色 CentOS7.5 192.168.200.111 git git服务器 CentOS7.5 192.168.200.112 Jenkins git客户端 jenkins服务器 CentOS7.5 192.168…

【实训项目】易行APP设计(c2c二手交易平台)

1.设计摘要 1.1市场背景 随着经济的迅速发展与科技日新月异的进步&#xff0c;家庭内的各项物品更新换代频率越来越快&#xff0c;人们购买新商品后越来越多旧的商品积压需要处理&#xff1b;在互联网电商的各种营销刺激下&#xff0c;消费者非常容易形成“冲动”消费&#x…

回归预测 | MATLAB实现DBN-ELM深度置信网络结合极限学习机多输入单输出回归预测

回归预测 | MATLAB实现DBN-ELM深度置信网络结合极限学习机多输入单输出回归预测 目录 回归预测 | MATLAB实现DBN-ELM深度置信网络结合极限学习机多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现DBN-ELM深度置信网络结合极限学习…

基于paddleocr的文档识别

1、版面分析 使用轻量模型PP-PicoDet检测模型实现版面各种类别的检测。 数据集&#xff1a; 英文&#xff1a;publaynet数据集的训练集合中包含35万张图像&#xff0c;验证集合中包含1.1万张图像。总共包含5个类别。 中文&#xff1a;CDLA据集的训练集合中包含5000张图像&a…