Xamarin.Android实现手写板的功能

这篇具有很好参考价值的文章主要介绍了Xamarin.Android实现手写板的功能。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、背景说明

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

2、实现效果

实现的效果如下:
Xamarin.Android实现手写板的功能,xamarin,android
Xamarin.Android实现手写板的功能,xamarin,android
Xamarin.Android实现手写板的功能,xamarin,android

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">

    <FrameLayout
        android:id="@+id/tablet_view"
        android:layout_width="fill_parent"
        android:layout_height="300dp" >
    </FrameLayout>
 
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="@android:drawable/bottom_bar"
        android:paddingTop="4dp" >
 
        <Button
            android:id="@+id/write_pad_ok"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="确定" />
 
        <Button
            android:id="@+id/write_pad_clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="清除" />
 
        <Button
            android: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">

    <ImageView
        
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:id="@+id/signImageView" />

    <TextView
        android: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常用方法解析文章来源地址https://www.toymoban.com/news/detail-637279.html

到了这里,关于Xamarin.Android实现手写板的功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Xamarin.Android中的Fragment

    一般而言,会在 activity 中添加一个加载 fragment 的方法。通过点击菜单的按钮,加载不同的 fragment 。其样子一般是这样的: 在 fragment 中往往通过工厂模式,提供及返回实例 非常简单,因为fragment一定是要寄存到某个activity中。因此直接使用如下代码: 通过 FragmentManager 查找

    2024年02月10日
    浏览(59)
  • Xamarin体验:使用C#开发iOS/Android应用

    http://www.cnblogs.com/lwme/p/use-xamarin-develop-Android-iOS-app.html Xamarin是Mono创始人Miguel de Icaza创建的公司,旨在让开发者可以用C#编写iOS, Android, Mac应用程序,也就是跨平台移动开发。 简介 Xamarin是基于Mono的平台,目前主要有以下产品(更具体请见:http://xamarin.com/products): Xamarin Studio:

    2024年02月07日
    浏览(46)
  • Xamarin.Android | 界面跳转到手机自带的自启动管理界面,引导用户将APP加入自启动

    很多 Android 应用需要在后台运行,以便提供实时通知、定时任务等服务,但是部分 Android 系统通过限制应用程序后台运行,以降低电池消耗和提高系统性能。这就可能导致应用程序运行不稳定,通知延迟,定时任务无法正常执行等问题。因此,在某些情况下,将应用程序添加

    2024年02月16日
    浏览(51)
  • C#使用xamarin进行跨平台开发

    使用 Xamarin 进行跨平台开发可以使用 C# 和 .NET 平台来开发移动应用程序,同时将代码在多个主要移动操作系统上运行,包括 Android 和 iOS。以下是在 C# 中使用 Xamarin 进行跨平台开发的一般步骤: 安装 Xamarin : 在开始之前,你需要安装 Xamarin 开发环境。你可以选择安装 Visual

    2024年02月11日
    浏览(65)
  • PLC-IoT 网关开发札记(6): Xamarin.Forms 的 CollectionView 绑定了什么?

    项目开发中不可避免地会遇到在一个页面中呈现列表的情况,使用 CollectionView 作为容器是很方便的。CollectionView 中显示的数据对应于后台的一个 IEnumerable 派生的列表,常用的是 ListT 和 VectorT,我习惯于使用 ListT 作为后台的数据表。 CollectionView 的每一项对应后台的 ListT 的一

    2024年01月23日
    浏览(55)
  • 原生微信小程序实现手写签名功能

    项目中有遇到在小程序上实现手动签名功能,今天给大家分享下代码 wxml 文件代码如下,catchtouchmove属性一定要加上,否则移动起来连笔非常不流畅 wxss代码 js代码 效果如下

    2024年02月16日
    浏览(46)
  • 如何通过原型修改第三方类库,来实现自己的功能而不破坏类库?手写一下

    使用原型链对第三方库进行扩展或修改是JavaScript中的常见做法。这种做法的优点是可以在不修改原始库源码的情军况下增加或修改功能。但也需要小心,因为过度的修改可能会导致与原始库的不兼容,或者出现不可预料的副作用。 下面给出一个简单的例子说明如何通过原型

    2024年02月10日
    浏览(45)
  • Android实现拨打电话功能

    直接拨号 demo下载

    2024年02月09日
    浏览(40)
  • Android 实现录音功能

    通过媒体录制器MediaRecorder实现:MediaRecorder是Android自带的音频和视频录制工具,它通过操纵摄像头和麦克风完成媒体录制,既可录制视频,又可单独录制音频。 reset:重置录制资源。 prepare:准备录制。 start:开始录制。 stop:结束录制。 release:释放录制资源。 setOnErrorList

    2024年02月12日
    浏览(42)
  • Android 实现无预览拍照功能

    1.权限 需要相机、读写文件、悬浮窗权限 申请相机、读写文件 manifest.xml 相机、读写文件权限需要动态申请 悬浮窗权限 需要申请 2.布局与使用 布局 使用 主要参数 然后调用 拍照结束需要把wm给remove掉,要不还是会挡着下边的东西。 3.完整代码

    2024年02月13日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包