首页 > 系统 > Android > 正文

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

2019-10-21 21:32:28
字体:
来源:转载
供稿:网友

一、前言

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

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


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表