想必大家都用过QQ的白板功能,里面主要有两项,一个是涂鸦功能,事实上类似于上节的画板功能,而还有一个就是手写,那记事本怎么能没有这个功能呢,今天就来为我们的记事本加入手写功能。

先上图,看看效果:

看了效果图,是不是心动了呢?那就赶紧着手做吧,事实上,手写功能并不难实现,大体就是全屏书写,定时发送handle消息,更新activity。

实现手写功能的主要步骤:

1. 自己定义两个View,一个是TouchView,用于在上面绘图,还有一个是EditText,用于将手写的字显示在当中,而且,要将两个自己定义View通过FrameLayout帧式布局重叠在起,以实现全屏手写的功能。

2  在TouchView中实现写字,并截取画布中的字以Bitmap保存。

3. 设置定时器,利用handle更新界面。

以下是实现的细节:

1. 手写的界面设计:

如上图所看到的,和上节的画板界面一致,底部分选项菜单条,有5个选项,各自是调整画笔大小,画笔颜色,撤销,恢复,以及清空,对于这些功能,之后几节再实现。

布局文件activity_handwrite.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/white"><FrameLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:id="@+id/finger_layout"  ><com.example.notes.LineEditTextandroid:id="@+id/et_handwrite"android:layout_width="match_parent"android:layout_height="match_parent"android:scrollbars="vertical"android:fadingEdge="vertical"android:inputType="textMultiLine"android:gravity="top"android:textSize="20sp"android:layout_margin="5dp"android:focusable="true"android:lineSpacingExtra="10dp"android:textColor="#00000000"android:background="#00000000"/><com.example.notes.TouchViewandroid:id="@+id/touch_view"android:layout_width="fill_parent"android:layout_height="wrap_content"android:background="@android:color/transparent" ></com.example.notes.TouchView></FrameLayout><ImageView android:layout_width="match_parent"android:layout_height="wrap_content"android:src="@drawable/line"android:layout_above="@+id/paintBottomMenu"/><GridView android:id="@+id/paintBottomMenu" android:layout_width="match_parent"android:layout_height="45dp"android:numColumns="auto_fit"android:background="@drawable/navigationbar_bg"android:horizontalSpacing="10dp"android:layout_alignParentBottom="true"></GridView></RelativeLayout>

能够看出,里面有两个自己定义view,而且通过FrameLayout重叠在一起。

先来看com.example.notes.LineEditText,这个事实上和加入记事中的界面一样,就是自己定义EditText,而且在字的以下画一条线。

LineEditText.java

public class LineEditText extends EditText {private Rect mRect;private Paint mPaint;public LineEditText(Context context, AttributeSet attrs) {// TODO Auto-generated constructor stubsuper(context,attrs);mRect = new Rect();mPaint = new Paint();mPaint.setColor(Color.GRAY);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//得到EditText的总行数int lineCount = getLineCount();Rect r = mRect;Paint p = mPaint;//为每一行设置格式 for(int i = 0; i < lineCount;i++){//取得每一行的基准Y坐标,并将每一行的界限值写到r中int baseline = getLineBounds(i, r);//设置每一行的文字带下划线canvas.drawLine(r.left, baseline+20, r.right, baseline+20, p);}}
}

还有一个就是com.example.notes.TouchView,实现了绘制,及定时更新界面的功能,详细看代码

TouchView.java

public class TouchView extends View {private Bitmap  mBitmap,myBitmap;private Canvas  mCanvas;private Path    mPath;private Paint   mBitmapPaint;private Paint mPaint;private Handler bitmapHandler;GetCutBitmapLocation getCutBitmapLocation;private Timer timer;DisplayMetrics dm;private int w,h;public TouchView(Context context) {super(context);dm = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);w = dm.widthPixels;h = dm.heightPixels;initPaint();}public TouchView(Context context, AttributeSet attrs) {super(context,attrs);dm = new DisplayMetrics();((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);w = dm.widthPixels;h = dm.heightPixels;initPaint();}//设置handlerpublic void setHandler(Handler mBitmapHandler){bitmapHandler = mBitmapHandler;}//初始化画笔,画布private void initPaint(){mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setDither(true);mPaint.setColor(0xFF00FF00);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeJoin(Paint.Join.ROUND);mPaint.setStrokeCap(Paint.Cap.ROUND);mPaint.setStrokeWidth(15);  getCutBitmapLocation = new GetCutBitmapLocation();//画布大小 mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);mCanvas = new Canvas(mBitmap);  //全部mCanvas画的东西都被保存在了mBitmap中mCanvas.drawColor(Color.TRANSPARENT);mPath = new Path();mBitmapPaint = new Paint(Paint.DITHER_FLAG);timer = new Timer(true);}/*** 处理屏幕显示*/Handler handler = new Handler(){public void handleMessage(Message msg) {switch (msg.what) {            case 1: myBitmap = getCutBitmap(mBitmap); Message message = new Message();message.what=1;Bundle bundle = new Bundle();;bundle.putParcelable("bitmap",myBitmap);message.setData(bundle);bitmapHandler.sendMessage(message);RefershBitmap();break;}super.handleMessage(msg);}};/*** 发送消息给handler更新ACTIVITY      */TimerTask task = new TimerTask() {public void run() {Message message = new Message();message.what=1;Log.i("线程", "来了");handler.sendMessage(message);}};//分割画布中的字并返回public Bitmap getCutBitmap(Bitmap mBitmap){//得到手写字的四周位置,并向外延伸10pxfloat cutLeft = getCutBitmapLocation.getCutLeft() - 10;float cutTop = getCutBitmapLocation.getCutTop() - 10;float cutRight = getCutBitmapLocation.getCutRight() + 10;float cutBottom = getCutBitmapLocation.getCutBottom() + 10;cutLeft = (0 > cutLeft ? 0 : cutLeft);cutTop = (0 > cutTop ? 0 : cutTop);cutRight = (mBitmap.getWidth() < cutRight ? mBitmap.getWidth() : cutRight);cutBottom = (mBitmap.getHeight() < cutBottom ? mBitmap.getHeight() : cutBottom);//取得手写的的高度和宽度 float cutWidth = cutRight - cutLeft;float cutHeight = cutBottom - cutTop;Bitmap cutBitmap = Bitmap.createBitmap(mBitmap, (int)cutLeft, (int)cutTop, (int)cutWidth, (int)cutHeight);if (myBitmap!=null ) {myBitmap.recycle();myBitmap= null;}return cutBitmap;}//刷新画布private void RefershBitmap(){initPaint();invalidate();if(task != null)task.cancel();}@Overrideprotected void onDraw(Canvas canvas) {            canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);     //显示旧的画布       canvas.drawPath(mPath, mPaint);  //画最后的path}private float mX, mY;private static final float TOUCH_TOLERANCE = 4;//手按下时private void touch_start(float x, float y) {mPath.reset();//清空pathmPath.moveTo(x, y);mX = x;mY = y;if(task != null)task.cancel();//取消之前的任务task = new TimerTask() {@Overridepublic void run() {Message message = new Message();message.what=1;Log.i("线程", "来了");handler.sendMessage(message);}};getCutBitmapLocation.setCutLeftAndRight(mX,mY);}//手移动时private void touch_move(float x, float y) {float dx = Math.abs(x - mX);float dy = Math.abs(y - mY);if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {mPath.quadTo(mX, mY, x, y);// mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);//源码是这样写的,但是我没有弄明确,为什么要这样?mX = x;mY = y;if(task != null)task.cancel();//取消之前的任务task = new TimerTask() {@Overridepublic void run() {Message message = new Message();message.what=1;Log.i("线程", "来了");handler.sendMessage(message);}};getCutBitmapLocation.setCutLeftAndRight(mX,mY);}}//手抬起时private void touch_up() {//mPath.lineTo(mX, mY);mCanvas.drawPath(mPath, mPaint);mPath.reset();if (timer!=null) {if (task!=null) {task.cancel();task = new TimerTask() {public void run() {Message message = new Message();message.what = 1;handler.sendMessage(message);}};timer.schedule(task, 1000, 1000);             //2200秒后发送消息给handler更新Activity}}else {timer = new Timer(true);timer.schedule(task, 1000, 1000);                    //2200秒后发送消息给handler更新Activity}}//处理界面事件@Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:touch_start(x, y);invalidate(); //刷新break;case MotionEvent.ACTION_MOVE:touch_move(x, y);invalidate();break;case MotionEvent.ACTION_UP:touch_up();invalidate();break;}return true;}}

这里面的难点就是利用TimerTask和Handle来更新界面显示,须要在onTouchEvent的三个事件中都要通过handle发送消息来更新显示界面。

接下来就是在activity里通过handle来得到绘制的字,并加入在editText中。

关于配置底部菜单,以及顶部标题栏,这里不再赘述,直接怎样将绘制的字得到,并加入在edittext中:

得到绘制字体的Bitmap

    //处理界面Handler handler = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Bundle bundle = new Bundle();bundle = msg.getData();Bitmap myBitmap = bundle.getParcelable("bitmap"); InsertToEditText(myBitmap);}};

当中myBitmap就是取得的手写字,保存在Bitmap中,  InsertToEditText(myBitmap);是将该图片加入在edittext中,详细例如以下:

   private LineEditText et_handwrite;      
    et_handwrite = (LineEditText)findViewById(R.id.et_handwrite);
      //将手写字插入到EditText中private void InsertToEditText(Bitmap mBitmap){int imgWidth = mBitmap.getWidth();int imgHeight = mBitmap.getHeight();//缩放比例float scaleW = (float) (80f/imgWidth);float scaleH = (float) (100f/imgHeight);Matrix mx = new Matrix();//对原图片进行缩放mx.postScale(scaleW, scaleH);mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, imgWidth, imgHeight, mx, true);//将手写的字插入到edittext中SpannableString ss = new SpannableString("1");ImageSpan span = new ImageSpan(mBitmap, ImageSpan.ALIGN_BOTTOM);ss.setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);et_handwrite.append(ss);}

这样,就实现了手写的功能,下节就实现手写字的撤销,恢复,以及清空的功能。

转载于:https://www.cnblogs.com/blfshiye/p/4264408.html

android项目 之 记事本(6)----- 加入手写相关推荐

  1. android记事本 图文存储,android项目 之 记事本(15) ----- 保存手写及绘图

    之前,忘了写如何将手写和绘图保存,现在补上. 首先看如何保存绘图,先看效果图: 因为记事本的绘图功能主要用到了画布,而在构建画布时,指定了Bitmap,也就是说在画布上的所画的东西都被保存在了Bitm ...

  2. Android项目之记事本

    1.需求分析 1)业务需求分析: 近年来,随着生活节奏的加快,工作和生活的双重压力全面侵袭着人们,如何避免忘记工作和生活中的诸多事情而造成不良的后果,就显得非常重要.为此,我们开发了一款基于Andro ...

  3. 如何在pdf中加入手写签名

    目录 1.手写签名 2. 抠字符 3. 插入签名 1.手写签名 在白纸上写下需要的签名,然后用手机拍下来,再用扫描全能王将图片进行扫描,得到白底黑字的图像. 2. 抠字符 将第一步得到的图像中的字符部 ...

  4. word html签名,如何在Word中加入手写签名签名?

    回答: 点击"开始"→"程序"→"附件"→"造字程序"(如果没找到"造字程序",可通过"控 ...

  5. android mysql 记事本_android项目 之 记事本(11) ----- 加入数据库

    本文是自己学习所做笔记.欢迎转载.但请注明出处:http://blog.csdn.net/jesson20121020 通过之前的10节,已实现了记事本的大部分功能,有加入拍照.加入照片,加入录音,加 ...

  6. Android Studio实现记事本项目

    项目目录 一.需求分析 1.业务需求分析 2.架构分析 3.数据库类设计分析 4.界面需求分析 4.1.记事本界面 4.2.添加记录界面 4.3.修改记录界面 二.开发环境介绍 三.记事本功能业务实现 ...

  7. android 直播sdk 抖音,从零开始仿写一个抖音App——跨平台视频编辑SDK项目搭建

    不知不觉已经到了2019年,本系列的文章也更新到了8篇.很庆幸笔者能坚持下来,从我司的代码中学习到了很多东西.当然更庆幸的是收获了众多读者的鼓励和支持.从本篇文章开始,我们将接触短视频 app 中比较 ...

  8. 【Android项目实战 | 从零开始写app(十二)】实现app首页智慧服务热门推荐热门主题、新闻

    说在前面,由于各种adapter,xml布局,bean实体类,Activity,也为了让看懂,代码基本都是"简单粗暴直接不好看",没啥okhttp和util工具类之类的封装,本篇幅 ...

  9. 【Android项目实战 | 从零开始写app (六) 】用TabLayout+ViewPager搭建App 框架主页面底部导航栏

    本篇实现效果: 搭建app框架的方式有很多,本节主要用TabLayout+ViewPager搭建App框架,这种方式简单易实现,在主页中加载Fragment碎片,实现不同功能页面的切换效果图如下: 文 ...

最新文章

  1. php 7.1 寿命,PHP 7.1新特性
  2. 「完结」总结12大CNN主流模型架构设计思想
  3. MaterialImageView
  4. 机器学习:一步步教你理解反向传播方法
  5. wireshark从入门到精通(协议排错安全篇)4
  6. UVA 10803 Thunder Mountain
  7. React DnD简明教程
  8. IDEA中Git操作
  9. d3 v5 api arrays
  10. 关于||逻辑或运算符运算符
  11. Map先排序value小->大再排序key小->大,ArrayList与Stream分别实现
  12. 廖雪峰Python教程梳理
  13. android 打印机 万能驱动,万能打印机驱动专家
  14. 不要把精力浪费在“吃瓜”上
  15. python动物重量排序详解
  16. string和字符数组的reverse函数
  17. dynaform6.1.3视频教程
  18. 3月刊特别策划:移动应用排名与开发者的机会
  19. 关于前端框架vue/react及UI框架的配合
  20. [WVR系列路由器] 微信连Wi-Fi功能使用指南

热门文章

  1. express路由管理的几种自动化方法分享-js教程-PHP中文网
  2. mongoose 笔记
  3. vue从入门到精通之进阶篇(一)vue-router基础
  4. http --- 网关、隧道、中继
  5. ES5-17/18 错误信息、try_catch、严格模式
  6. RabbitMQ 延迟队列,消息延迟推送
  7. es中的一些知识点记录
  8. 真静态和伪静态的区别
  9. 一个YII社区学习网站
  10. (七)Maven使用的最佳实践