目录
- 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 Resources
和Canvas
,前者可主要进行类似Html
的CSS
之类的功能,后者则实现比较负责的功能,本次主要用到了后者-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
,主要是想实现通过这个委托,将生成的图像传递出去。就是对话框中的event
(WriteDialogListener
)
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
5、代码下载
代码下载
6、参考资料
主要参考Android实现手写板和涂鸦功能