android开发中如何实现手写输入的记事本

android开发中如何实现手写输入的记事本,第1张

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

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

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

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

下面是实现的细节:

1. 手写的界面设计:

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

布局文件activity_handwrite.xml

<!--?xml version=1.0 encoding=utf-8?-->

<relativelayout android:background="@android:color/white" android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"><imageview android:layout_above="@+id/paintBottomMenu" android:layout_height="wrap_content" android:layout_width="match_parent" android:src="@drawable/line">

</imageview></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 stub

      super(context,attrs)

      mRect = new Rect()

      mPaint = new Paint()

      mPaint.setColor(Color.GRAY)

  }

 

  @Override

  protected void onDraw(Canvas canvas) {

      super.onDraw(canvas)

      //得到EditText的总行数

      int lineCount = getLineCount()

      Rect r = mRect

      Paint p = mPaint

      //为每一行设置格式

      for(int i = 0i <lineCounti++){

          //取得每一行的基准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()

  }

  //设置handler

  public 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){

      //得到手写字的四周位置,并向外延伸10px

      float 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()

  }

 

  @Override

  protected 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()//清空path

      mPath.moveTo(x, y)

      mX = x

      mY = y

      if(task != null)

          task.cancel()//取消之前的任务

      task = new TimerTask() {

         

          @Override

          public 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() {

             

              @Override

              public 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

      }

     

  }

 

  //处理界面事件

  @Override

  public 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(){

   @Override

   public 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中,具体如下:

?

1

private LineEditText et_handwrite 

?

1

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)

}

1、创建新的文件夹。

2、将已存在的笔记移动至新建的文件夹中。

3、在文件夹中添加新的笔记。

4、可以通过文件夹查看、编辑、删除笔记。

5、文件夹和笔记可以进行重命名。

6、可以在不同文件夹之间移动笔记。

7、可以使用搜索功能查找笔记。

8、文件夹可以进行排序,并且设置默认打开的文件夹。

9、可以设置导入、导出笔记到本地或云端。

10、支持多账号登录,每个账号拥有各自的文件夹和笔记。

界面篇

1、打开Authorware,新建一文件并命名为“记事本”。2、拖动一显示(Display)图标到流程线上,命名为“Background”,双击打开该显示图标,在其中应用Authorware的绘图工具完成图1所示的界面并输入文字。当然还可以在专业绘图软件中绘制更为美观的界面,然后作为图片引入进来。

图1

功能篇

由于该记事本仅应用于课件中,所以功能上并不需要非常完善,在此我们仅建立5个功能按钮,即“新建文件”、“打开文件”、“保存文件”、“最小化”及“退出”。 1、增加“新建文件”按钮(1)在流程线中“界面”的下方放置一个交互(Interaction)图标并命名为“Interaction”。(2)拖放一个群组(Map)图标至交互图标的右方,在d出的交互方式选择对话框中选择为“按钮(Button)交互”并命名为“新建文件”。(3)打开群组图标,在其中拖放一个计算(Calculation)图标,并输入“Neirong:=EntryText”,该语句的含义为将输入的文本内容赋值给自定义变量Neirong。2、增加“打开文件”按钮(1)拖放一个群组(Map)图标至“新建文件”的右方,并命名为“打开文件”。(2)打开对应的群组图标,拖放一个计算图标并输入内容“Diaoyong:=Callobject(Childlnstance,"displayopen")”,该语句的含义为:将用户选定的文件名及地址赋值给变量Diaoyong。(3)然后再拖放一个计算图标,在其中输入“Neirong:=Readextfile(Diaoyong)”,该语句的含义为:通过引用上一个计算图标的变量值将选定文件的内容赋给变量Neirong。(4)拖放一个显示图标,双击打开,在其中输入“{Neirong}”,也就是通过显示图标将变量的值即选定文件的内容显示出来。3、增加“保存文件”按钮(1)参照“新建文件”的设计方法,拖放一个群组(Map)图标至“打开文件”的右方并命名为“保存文件”。(2)打开对应的群组图标,拖放一个计算图标并输入如下内容:Wenjianming:=EntrytextWriteextfile(Filelocation^Wenjianming,Neirong)此部分语句的含义为:将输入的文字内容赋值给自定义变量Wenjianming,然后将“新建文件”部分输入的文字内容保存到以Wenjianming命名的文本文件中。4、增加“最小化”、“退出”按钮(1)最小化拖放一个群组图标至“保存文件”的右方并命名为“最小化”,打开该群组图标并拖放一个计算图标,在其中输入内容“Showwindow(Windowhandle,2)”。(2)退出拖放一个群组图标至“最小化”的右方并命名为“退出”,打开该群组图标并拖放一个计算图标,在其中输入“Quit()”。完善篇1、调整界面及按钮的位置运行该程序,你会发现虽然界面设计好了,但是按钮的位置排列并不是很整齐,调整方法如下:双击打开流程线上的交互图标,将“最小化”按钮拖放至界面的右上角,将其他按钮拖放至界面的下方水平排列,通过按住“Shift”键将按钮选中(除“最小化”按钮外),然后应用“Modify/Align”功能将其对齐,最终效果如图2所示。

图2

2、设置按钮交互的选项运行程序后,当点击“新建文件”后,你会发现所有其他按钮均变成了灰色,失去了作用,调整方法如下:双击按钮响应的标签,打开“Properties:Response”属性对话框,在“Response”选项卡中,选中“Scope:Perpetual”,把“Branch”选项设置为“Return”。3、调整正文输入框及保存文件中的文件名输入框的位置在程序运行后,点击了“新建文件”或者“保存文件”后,你会发现文字输入框不是位置不合适就是文字输入框太小,调整方法如下:双击打开主流程线上的交互图标,然后点击文字输入框,通过周围的小正方形进行大小的调整。4、调整输入文字的大小、颜色及字体双击打开“新建文件”群组图标中的交互图标,然后双击文字输入框的周围,系统将d出对话框,在其中的“Text”标签选项中进行文字的大小、颜色及字体的设置。5、为按钮功能增加快捷键双击打开按钮交互图标的响应标志,在d出对话框的“Key(s)”后输入与该按钮相对应的快捷键,如果要使用多个快捷键,字符中间应插入符号“|”,如果要使用“Ctrl”或者“Alt”的组合键时,后面直接加上该字母。整个流程线的结构如图3所示。

图3

总结

以上我们通过按钮交互设置了“记事本”的一些基本功能,当然还可以参照Windows下的记事本,利用Authorware提供的菜单交互功能将其实现,相信可以有异曲同工之妙。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/bake/11612041.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-17
下一篇2023-05-17

发表评论

登录后才能评论

评论列表(0条)

    保存