您好,登錄后才能下訂單哦!
一、前言
添加文本,也是屬于 一個比較簡單的功能,在第二篇的時候,添加了橡皮擦,在橡皮擦里面通過一個模式的形式進行畫筆的判斷,當然文本也是如此,添加一個文本模式,在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) }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。