Android畫板開發(fā)之添加文本文字
一、前言
添加文本,也是屬于 一個(gè)比較簡(jiǎn)單的功能,在第二篇的時(shí)候,添加了橡皮擦,在橡皮擦里面通過(guò)一個(gè)模式的形式進(jìn)行畫筆的判斷,當(dāng)然文本也是如此,添加一個(gè)文本模式,在onTouchDown的時(shí)候,彈出PopupWindow,輸入文本,然后PopupWindow消失的時(shí)候,利用staticLayout繪制到畫布上即可。當(dāng)然也有些需要注意的地方

下面一步步來(lái)實(shí)現(xiàn)
二、實(shí)現(xiàn)
2.1 添加文本模式
例如橡皮擦那樣,添加多一個(gè)文本模式,然后setModel的時(shí)候,需要把畫筆的樣式修改為FILL,如果是STROKE進(jìn)行文字繪制會(huì)變成空心文字。
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
/**
* 設(shè)置畫筆模式
*/
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 是一個(gè)為不可編輯的文本布局的類,這意味著一旦布局完成,文本內(nèi)容就不可以改變。在單純地使用TextView來(lái)展示靜態(tài)文本的時(shí)候,創(chuàng)建的就是 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);
我們畫板的繪制文字也是用到了這個(gè)StaticLayout,它有三個(gè)構(gòu)造方法,我們用最少那個(gè)即可:
public StaticLayout(CharSequence source, //字符串 TextPaint paint, //畫筆對(duì)象 int width, //layout的寬度,字符串超出寬度時(shí)自動(dòng)換行。 Layout.Alignment align, //layout的對(duì)其方式,有ALIGN_CENTER, ALIGN_NORMAL, ALIGN_OPPOSITE 三種。 float spacingmult, //相對(duì)行間距,相對(duì)字體大小,1.5f表示行間距為1.5倍的字體高度。 float spacingadd, //在基礎(chǔ)行距上添加多少 boolean includepad) //文本頂部和底部是否留白
所以,bean類在之前的基礎(chǔ)上,添加了文本、寬度、xy軸的偏移,然后繪制的時(shí)候,利用staticLayout進(jìn)行了繪制。
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)){
//調(diào)節(jié)畫布起始坐標(biāo)進(jìn)行繪制
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("@@","長(zhǎng)度:"+staticLayout.width)
//canvas.drawText(mText,mTextOffX,mTextOffY,mPaint)
//恢復(fù)畫布坐標(biāo)
canvas.translate(-mOffX,-mOffY)
}
}
else -> {
canvas.drawPath(mPath,mPaint)
}
}
}
fun getMode():Long = mMode
}
2.3 彈窗處理
接下來(lái),設(shè)置一個(gè)彈框PopupWindow進(jìn)行文本的輸入,彈窗里面的控件就是一個(gè)EditText。 在彈窗消失的時(shí)候添加到畫筆列表,然后進(jìn)行重繪。 在這里有三點(diǎn)注意點(diǎn)
- 軟鍵盤自動(dòng)彈出
- 編輯框顯示在軟鍵盤上面
- 彈框顯示的位置
- 右邊越界
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()
//自動(dòng)彈出軟鍵盤,會(huì)導(dǎo)致布局變化,重測(cè)量、繪制
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)
}
在觸摸的時(shí)候,進(jìn)行顯示。 移動(dòng)的時(shí)候不用操作,手指起來(lái)的時(shí)候也不用操作
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> { //手指按下的時(shí)候
//記錄上次觸摸的坐標(biāo),注意ACTION_DOWN方法只會(huì)執(zhí)行一次
preX = event.x
preY = event.y
when (mMode) {
EDIT_MODE_TEXT -> {
//彈出popupWidnwo輸入text
showTextPopup()
//文字在隱藏的時(shí)候添加到list
}
else -> {
//將起始點(diǎn)移動(dòng)到當(dāng)前坐標(biāo)
mPath.moveTo(event.x, event.y)
mPaintedList.add(PaintBean(Paint(mPaint), Path(mPath)))
}
}
}
MotionEvent.ACTION_MOVE -> { //手指移動(dòng)的時(shí)候
when (mMode) {
EDIT_MODE_TEXT -> {
}
else -> {
//繪制圓滑曲線,即貝塞爾曲線,貝塞爾曲線這個(gè)知識(shí)自行了解
mPaintedList.get(mPaintedList.size - 1).mPath?.quadTo(preX, preY, event.x, event.y)
preX = event.x
preY = event.y
//重新繪制,會(huì)調(diào)用onDraw方法
invalidate()
}
}
}
MotionEvent.ACTION_UP -> {}
}
return true
}
因?yàn)槔L制在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!!)
}
//繪制固化的內(nèi)容到緩存Canvas
mBufferCanvas?.drawBitmap(mHoldBitmap, 0f, 0f, null)
//繪制記錄的畫筆
for (paint in mPaintedList) {
paint.draw(mBufferCanvas!!)
}
//畫出緩存bitmap的內(nèi)容
canvas.drawBitmap(mBufferBitmap, 0f, 0f, null)
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android實(shí)現(xiàn)畫板功能(二)
- Android實(shí)現(xiàn)畫板功能(一)
- Android實(shí)現(xiàn)簡(jiǎn)單畫圖畫板
- Android studio實(shí)現(xiàn)畫板功能
- Android SurfaceView畫板操作
- Android實(shí)現(xiàn)畫畫板案例
- Android畫板開發(fā)之添加背景和保存畫板內(nèi)容為圖片
- Android畫板開發(fā)之撤銷反撤銷功能
- Android畫板開發(fā)之基本畫筆功能
- Android畫板開發(fā)之橡皮擦功能
- Android曲線更圓滑的簽名畫板
- Android實(shí)現(xiàn)繪畫板功能
相關(guān)文章
OpenGL Shader實(shí)例分析(3)等待標(biāo)識(shí)效果
這篇文章主要介紹了OpenGL Shader實(shí)例分析第3篇,等待標(biāo)識(shí)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-02-02
Android自定義實(shí)現(xiàn)頂部粘性下拉刷新效果
這篇文章主要為大家詳細(xì)介紹了Android自定義實(shí)現(xiàn)頂部粘性下拉刷新效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
android實(shí)現(xiàn)自動(dòng)關(guān)機(jī)的具體方法
android實(shí)現(xiàn)自動(dòng)關(guān)機(jī)的具體方法,需要的朋友可以參考一下2013-06-06
Android底部菜單欄(RadioGroup+Fragment)美化
這篇文章主要介紹了Android底部菜單欄RadioGroup+Fragment美化,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
android編程實(shí)現(xiàn)圖片庫(kù)的封裝方法
這篇文章主要介紹了android編程實(shí)現(xiàn)圖片庫(kù)的封裝方法,涉及Android針對(duì)圖片的下載、保存、獲取及操作緩存圖片等相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11

