温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2020-10-04 21:50:38 来源:脚本之家 阅读:195 作者:tpnet 栏目:移动开发

一、前言

添加文本,也是属于 一个比较简单的功能,在第二篇的时候,添加了橡皮擦,在橡皮擦里面通过一个模式的形式进行画笔的判断,当然文本也是如此,添加一个文本模式,在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)

  }

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

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI