Android画板开发之添加文本文字
一、前言
添加文本,也是属于 一个比较简单的功能,在第二篇的时候,添加了橡皮擦,在橡皮擦里面通过一个模式的形式进行画笔的判断,当然文本也是如此,添加一个文本模式,在ontouchdown的时候,弹出popupwindow,输入文本,然后popupwindow消失的时候,利用staticlayout绘制到画布上即可。当然也有些需要注意的地方
下面一步步来实现
二、实现
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) }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Android自定义ImageView实现圆角功能
下一篇: FFmpeg-ios 编译