欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android画板开发之添加文本文字

程序员文章站 2022-08-20 17:18:01
一、前言 添加文本,也是属于 一个比较简单的功能,在第二篇的时候,添加了橡皮擦,在橡皮擦里面通过一个模式的形式进行画笔的判断,当然文本也是如此,添加一个文本模式,在o...

一、前言

添加文本,也是属于 一个比较简单的功能,在第二篇的时候,添加了橡皮擦,在橡皮擦里面通过一个模式的形式进行画笔的判断,当然文本也是如此,添加一个文本模式,在ontouchdown的时候,弹出popupwindow,输入文本,然后popupwindow消失的时候,利用staticlayout绘制到画布上即可。当然也有些需要注意的地方

Android画板开发之添加文本文字

下面一步步来实现

二、实现

2.1 添加文本模式

例如橡皮擦那样,添加多一个文本模式,然后setmodel的时候,需要把画笔的样式修改为fill,如果是stroke进行文字绘制会变成空心文字。

companion object {
    const val edit_mode_pen = 0x1l    //画笔模式
    const val edit_mode_eraser = 0x2l  //橡皮擦模式
    const val edit_mode_text = 0x3l  //文字模式

  }

  @retention(annotationretention.source)
  @intdef(edit_mode_pen, edit_mode_eraser, edit_mode_text)
  annotation class editmode

  /**
   * 设置画笔模式
   */
  fun setmodel(@editmode model: long) {
    mmode = model
    when (model) {
      edit_mode_pen -> {
        //画线
        mpaint.xfermode = null
        mpaint.style = paint.style.stroke
      }
      edit_mode_eraser -> {
        mpaint.xfermode = porterduffxfermode(porterduff.mode.clear)

      }
      edit_mode_text -> {
        mpaint.style = paint.style.fill
      }
    }
  }

2.2 修改bean类型

staticlayout 是一个为不可编辑的文本布局的类,这意味着一旦布局完成,文本内容就不可以改变。在单纯地使用textview来展示静态文本的时候,创建的就是 staticlayout,在api25,textview源码6858行可以看到。

staticlayout.builder builder = staticlayout.builder.obtain(mhint, 0,
     mhint.length(), mtextpaint, hintwidth)
     .setalignment(alignment)
     .settextdirection(mtextdir)
     .setlinespacing(mspacingadd, mspacingmult)
     .setincludepad(mincludepad)
     .setbreakstrategy(mbreakstrategy)
     .sethyphenationfrequency(mhyphenationfrequency);

我们画板的绘制文字也是用到了这个staticlayout,它有三个构造方法,我们用最少那个即可:

public staticlayout(charsequence source, //字符串
   textpaint paint, //画笔对象
   int width,  //layout的宽度,字符串超出宽度时自动换行。
   layout.alignment align, //layout的对其方式,有align_center, align_normal, align_opposite 三种。
   float spacingmult,  //相对行间距,相对字体大小,1.5f表示行间距为1.5倍的字体高度。
   float spacingadd,  //在基础行距上添加多少
   boolean includepad)  //文本顶部和底部是否留白

所以,bean类在之前的基础上,添加了文本、宽度、xy轴的偏移,然后绘制的时候,利用staticlayout进行了绘制。

data class paintbean(
    var mpaint: paint,  //保存画笔
    var mpath: path?,     //保存路径
    var mtext: string,    //文本
    var mwidth: int,
    var moffx: float,
    var moffy: float,
    private @tptextview.editmode var mmode:long
) {

  constructor(mpaint: paint, mpath: path) : this(mpaint,mpath,"",0,0f,0f,tptextview.edit_mode_pen)

  /**
   * 撤销和反撤销之后 重新绘制
   * @param canvas 绘制的画布
   */
  fun draw(canvas: canvas){

    when(mmode){

      tptextview.edit_mode_text -> {

        if(!textutils.isempty(mtext)){
          //调节画布起始坐标进行绘制
          canvas.translate(moffx,moffy)
          //利用staticlayout生成文字,不然不能换行
          val staticlayout = staticlayout(mtext,mpaint as textpaint,mwidth, layout.alignment.align_normal, 1.0f, 0.0f, false)
          staticlayout.draw(canvas)
          //log.e("@@","长度:"+staticlayout.width)
          //canvas.drawtext(mtext,mtextoffx,mtextoffy,mpaint)
          //恢复画布坐标
          canvas.translate(-moffx,-moffy)

        }

      }
      else -> {
        canvas.drawpath(mpath,mpaint)
      }

    }

  }

  fun getmode():long = mmode

}

2.3 弹窗处理

接下来,设置一个弹框popupwindow进行文本的输入,弹窗里面的控件就是一个edittext。 在弹窗消失的时候添加到画笔列表,然后进行重绘。 在这里有三点注意点

  • 软键盘自动弹出
  • 编辑框显示在软键盘上面
  • 弹框显示的位置
  • 右边越界
private var mtextpopup: popupwindow? = null
private var mtextview: edittext? = null

  /**
   * 显示popup文本输入弹窗
   */
  private fun showtextpopup() {

    if (null == mtextpopup) {
      mtextview = edittext(context)
      mtextview?.hint = "文字"

      mtextpopup = popupwindow(mtextview,
          windowmanager.layoutparams.wrap_content, windowmanager.layoutparams.wrap_content,
          true)
      mtextpopup?.setondismisslistener {
        if (!textutils.isempty(mtextview?.text)) {
          //添加到列表
          mpaintedlist.add(
              paintbean(textpaint(mpaint), null, mtextview?.text.tostring(), (width - prex).toint(),prex,prey - mtextview!!.height / 2, edit_mode_text))

          invalidate()
        }
      }
      //让popup显示在软键盘上面
      mtextpopup?.softinputmode = windowmanager.layoutparams.soft_input_adjust_resize
    }

    mtextview?.requestfocus()

    //自动弹出软键盘,会导致布局变化,重测量、绘制
    val imm = context.getsystemservice(service.input_method_service) as inputmethodmanager
    imm.togglesoftinput(0, inputmethodmanager.hide_not_always)


    mtextpopup?.showatlocation(this, gravity.top and gravity.left, prex.toint(), prey.toint()+mtextview!!.height)

  }

在触摸的时候,进行显示。 移动的时候不用操作,手指起来的时候也不用操作

@suppresslint("clickableviewaccessibility")
  override fun ontouchevent(event: motionevent): boolean {

    when (event.action) {
      motionevent.action_down -> { //手指按下的时候
        //记录上次触摸的坐标,注意action_down方法只会执行一次
        prex = event.x
        prey = event.y
        when (mmode) {

          edit_mode_text -> {
            //弹出popupwidnwo输入text
            showtextpopup()
            //文字在隐藏的时候添加到list
          }
          else -> {
            //将起始点移动到当前坐标
            mpath.moveto(event.x, event.y)
            mpaintedlist.add(paintbean(paint(mpaint), path(mpath)))
          }
        }

      }
      motionevent.action_move -> { //手指移动的时候
        when (mmode) {
          edit_mode_text -> {

          }
          else -> {
            //绘制圆滑曲线,即贝塞尔曲线,贝塞尔曲线这个知识自行了解
            mpaintedlist.get(mpaintedlist.size - 1).mpath?.quadto(prex, prey, event.x, event.y)
            prex = event.x
            prey = event.y
            //重新绘制,会调用ondraw方法
            invalidate()
          }

        }
      }
      motionevent.action_up -> {}
    }

    return true
  }

因为绘制在bean类里面,所以view的ondraw方法还是以前那样,不需要变化:

@suppresslint("drawallocation")
  override fun ondraw(canvas: canvas) {
    super.ondraw(canvas)

    //超出缓存的就固化到缓存bitmap
    while (mpaintedlist.size > paint_recored_num) {
      val paint = mpaintedlist.removeat(0)
      paint.draw(mholdcanvas!!)
    }

    //绘制固化的内容到缓存canvas
    mbuffercanvas?.drawbitmap(mholdbitmap, 0f, 0f, null)

    //绘制记录的画笔
    for (paint in mpaintedlist) {
      paint.draw(mbuffercanvas!!)
    }

    //画出缓存bitmap的内容
    canvas.drawbitmap(mbufferbitmap, 0f, 0f, null)

  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。