Xamarin.Android实现手写板的功能

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

    <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常用方法解析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值