十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
在上一篇實(shí)現(xiàn)了簡(jiǎn)單的畫板功能, 這篇實(shí)現(xiàn)橡皮擦功能,首先分析一下應(yīng)該如何實(shí)現(xiàn),
公司主營(yíng)業(yè)務(wù):做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)建站是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。創(chuàng)新互聯(lián)建站推出高郵免費(fèi)做網(wǎng)站回饋大家。
在Andriod有個(gè)圖像混合(Xfermode)概念,利用這個(gè)概念我們就可以實(shí)現(xiàn)橡皮擦功能。
一、Xfermode
Paint有一個(gè)方法setXfermode(Xfermode),這個(gè)方法設(shè)置圖像的混合模式。參數(shù)有三個(gè)子類:
前面兩個(gè)因?yàn)椴恢С钟布铀僭贏PI 16已經(jīng)已經(jīng)過(guò)時(shí)棄用了。 簡(jiǎn)單講一下第三個(gè)。
1.1 PorterDuffXfermode
該類有且只有一個(gè)含參的構(gòu)造方法PorterDuffXfermode(PorterDuff.Mode mode),參數(shù)就是設(shè)置圖像的混合模式,下面這張圖片形象地說(shuō)明了各種模式的作用
我們的做橡皮擦的時(shí)候,就是用到了PorterDuff.Mode.CLEAR這個(gè)模式清除圖像,所以說(shuō)橡皮擦也是Path,只是繪制的模式不一樣了。
二、實(shí)現(xiàn)
在上一篇的文章中,實(shí)現(xiàn)了最簡(jiǎn)單筆畫畫板,就是只有一個(gè)畫筆模式,所以首先添加一個(gè)橡皮擦的繪制模式。
companion object { const val EDIT_MODE_PEN = 0x1L //畫筆模式 const val EDIT_MODE_ERASER = 0x2L //橡皮擦模式 } @Retention(AnnotationRetention.SOURCE) @IntDef(EDIT_MODE_PEN, EDIT_MODE_ERASER) annotation class EditMode //當(dāng)前編輯模式默認(rèn)為畫筆模式 @EditMode private var mMode: Long = EDIT_MODE_PEN /** * 設(shè)置畫筆模式 */ fun setModel(@EditMode model:Long){ mMode = model when(model){ EDIT_MODE_PEN -> { //畫線 mPaint.xfermode = null } EDIT_MODE_ERASER ->{ mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) } } }
然后捋一下整個(gè)流程:
現(xiàn)在重點(diǎn)是解決第2點(diǎn),一個(gè)Path怎么做到不改變?cè)瓉?lái)的path基礎(chǔ)上換個(gè)繪制模式繼續(xù)畫呢?
如果你考慮第2點(diǎn)的話,效果是這樣子的:
What the fuck?(黑人問(wèn)號(hào)) 這什么情況? 其實(shí)是因?yàn)閜ath只有一條,一直沒(méi)改變。所以,引入緩存Canvas和緩存Bitmap,添加兩個(gè)變量:
//想要繪制的內(nèi)容先繪制到這個(gè)增加的canvas對(duì)應(yīng)的bitmap上, // 寫完后再把這個(gè)bitmap的ARGB信息一次提交給上下文的canvas去繪制 private lateinit var mBufferBitmap: Bitmap private lateinit var mBufferCanvas: Canvas
然后在onMeasure中進(jìn)行初始化:
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) if(mBufferCanvas == null){ mBufferBitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888) //canvas繪制的內(nèi)容,將會(huì)在這個(gè)mBufferBitmap內(nèi) mBufferCanvas = Canvas(mBufferBitmap) } }
然后在onTouchEvent方面里面手指移動(dòng)的時(shí)候,我們?cè)诰彺鍯anvas里面進(jìn)行繪制path:
MotionEvent.ACTION_MOVE -> { //手指移動(dòng)的時(shí)候 //繪制圓滑曲線,即貝塞爾曲線,貝塞爾曲線這個(gè)知識(shí)自行了解 mPath.quadTo(preX,preY,event.x,event.y) //在緩存里面繪制 mBufferCanvas.drawPath(mPath,mPaint) //重新繪制,會(huì)調(diào)用onDraw方法 invalidate() preX = event.x preY = event.y }
然后onDraw的時(shí)候,就把緩存的Canvas的bitmap當(dāng)前view的Canvas:
override fun onDraw(canvas: Canvas) { super.onDraw(canvas) //畫出緩存bitmap的內(nèi)容 canvas.drawBitmap(mBufferBitmap,0f,0f,null) }
就可以了,看看完整的代碼100多行:
class TPEraserView(context: Context, attr: AttributeSet) : View(context,attr) { companion object { const val EDIT_MODE_PEN = 0x1L //畫筆模式 const val EDIT_MODE_ERASER = 0x2L //橡皮擦模式 } @Retention(AnnotationRetention.SOURCE) @IntDef(EDIT_MODE_PEN, EDIT_MODE_ERASER) annotation class EditMode //當(dāng)前編輯模式默認(rèn)為畫筆模式 @EditMode private var mMode: Long = EDIT_MODE_PEN private var preX: Float = 0.0f //上一次的觸摸點(diǎn)x坐標(biāo) private var preY: Float = 0.0f //上一次觸摸點(diǎn)y坐標(biāo) private var mPath = Path() //path路徑 //畫筆 private var mPaint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG) //想要繪制的內(nèi)容先繪制到這個(gè)增加的canvas對(duì)應(yīng)的bitmap上, // 寫完后再把這個(gè)bitmap的ARGB信息一次提交給上下文的canvas去繪制 private lateinit var mBufferBitmap: Bitmap private lateinit var mBufferCanvas: Canvas init { mPaint.style = Paint.Style.STROKE //畫筆為實(shí)心 mPaint.color = Color.RED //顏色 mPaint.strokeCap = Paint.Cap.ROUND //筆觸為圓形 mPaint.strokeWidth = 10f //畫筆大小 } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int){ super.onMeasure(widthMeasureSpec, heightMeasureSpec) mBufferBitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888) //canvas繪制的內(nèi)容,將會(huì)在這個(gè)mBufferBitmap內(nèi) mBufferCanvas = Canvas(mBufferBitmap) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) //畫出緩存bitmap的內(nèi)容 canvas.drawBitmap(mBufferBitmap,0f,0f,null) } override fun onTouchEvent(event: MotionEvent): Boolean { when(event.action){ MotionEvent.ACTION_DOWN -> { //手指按下的時(shí)候 //將起始點(diǎn)移動(dòng)到當(dāng)前坐標(biāo) mPath.moveTo(event.x,event.y) //記錄上次觸摸的坐標(biāo),注意ACTION_DOWN方法只會(huì)執(zhí)行一次 preX = event.x preY = event.y } MotionEvent.ACTION_MOVE -> { //手指移動(dòng)的時(shí)候 //繪制圓滑曲線,即貝塞爾曲線,貝塞爾曲線這個(gè)知識(shí)自行了解 mPath.quadTo(preX,preY,event.x,event.y) //在緩存里面繪制 mBufferCanvas.drawPath(mPath,mPaint) //重新繪制,會(huì)調(diào)用onDraw方法 invalidate() preX = event.x preY = event.y } MotionEvent.ACTION_UP ->{ //清除路徑的內(nèi)容 mPath.reset() } } // true:告訴系統(tǒng),這個(gè)觸摸事件由我來(lái)處理 // false:告訴系統(tǒng),這個(gè)觸摸事件我不處理,這時(shí)系統(tǒng)會(huì)把觸摸事件傳遞給imageview的父節(jié)點(diǎn) return true } /** * 設(shè)置畫筆模式 */ fun setModel(@EditMode model:Long){ mMode = model when(model){ EDIT_MODE_PEN -> { mPaint.xfermode = null } EDIT_MODE_ERASER ->{ mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) } } } }
三、清空畫布實(shí)現(xiàn)
添加一個(gè)方法,按照上面的套路,把緩存canvas繪制清除即可。
/** * 清空畫布 */ fun clear() { mBufferCanvas.drawColor(0, PorterDuff.Mode.CLEAR) invalidate() }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。