Android畫板開發(fā)之撤銷反撤銷功能
一、分析
這篇將會講解撤銷反撤銷功能的實現(xiàn),先討論一下這個原理是怎么樣實現(xiàn)的。
每次撤回的內(nèi)容,內(nèi)容是怎么定義呢? 其實就是每一筆,每一筆作為撤回的內(nèi)容,那每一筆怎么算呢,就是算手指從按下-移動-放開這一個過程就是一筆。
我們只需記錄這個過程為一筆,然后用一個已畫列表list列表來記錄這個過程的paint畫筆和路徑path。
撤銷的時候就把后面的一個數(shù)據(jù)移到另一個撤銷列表
反撤銷的時候,就把撤銷列表的最后面那條數(shù)據(jù)移動到已畫列表。
然后,還有一個重點,就是畫筆的保存數(shù)量,上面說記錄每一筆畫筆,這當然是有個限度,不可能畫了好幾百筆都記錄下來,這樣子內(nèi)存消耗很大的,所以超出顯示畫筆數(shù)量的時候,我們就把以前的畫死在畫板上。
基本原理是這樣子的。接下來跟著我實現(xiàn)
二、實現(xiàn)
如何實現(xiàn)撤回功能
2.1 定義數(shù)據(jù)類
首先,需要一個bean類存儲每一筆的數(shù)據(jù),這里定義一個PaintData,里面需要定義個draw方法,因為撤銷的時候,需要重新繪制。
data class PaintData( var mPaint: Paint, //保存畫筆 var mPath: Path //保存路徑 ) { /** * 撤銷和反撤銷之后 重新繪制 * @param canvas 繪制的畫布 */ fun draw(canvas: Canvas){ canvas.drawPath(mPath,mPaint) } }
2.2 修改清空畫板方法
因為多了列表,所以清空畫板的方法需要把列表也清除了
/** * 清空畫布 * @param isClearList 時候清空數(shù)據(jù)列表 */ fun clear(isClearList:Boolean) { if(isClearList){ mRevokedList.clear() mPaintedList.clear() } mBufferCanvas.drawColor(0, PorterDuff.Mode.CLEAR) invalidate() }
2.3 實現(xiàn)撤銷方法
在view定義兩個列表,一個是已經(jīng)畫的內(nèi)容列表,一個是撤銷內(nèi)容的列表
//儲存已經(jīng)寫的筆畫 private var mPaintedList: MutableList<PaintData> = ArrayList<PaintData>() //已經(jīng)撤銷的列表 private var mRevokedList: MutableList<PaintData> = ArrayList<PaintData>()
添加固話層canvas和bitmap,超出記錄的畫筆就寫死在固化層了
//固化層,超出最大筆畫就先繪制到這個層 private lateinit var mHoldBitmap: Bitmap private lateinit var mHoldCanvas: Canvas //最多記錄20畫筆跡 private val MAX_PAINT_RECORED = 20 //在測量的時候進行初始化: override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { if(mBufferCanvas == null){ mBufferBitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888) //canvas繪制的內(nèi)容,將會在這個mBufferBitmap內(nèi) mBufferCanvas = Canvas(mBufferBitmap) } if(mHoldCanvas == null){ mHoldBitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888) mHoldCanvas = Canvas(mHoldBitmap) } }
然后定義撤回的方法
/** * 撤回一個筆跡 * @return 是否撤回成功 */ fun revoked(){ reDraw(mPaintedList) }
反撤銷方法基本一致,只是改了個列表:
/** * 反撤回一個筆跡 */ fun unRevoked(){ reDraw(mRevokedList) }
然后重新繪制的方法為:
/** * 重新繪制 * @param paintList 需要操作的list */ private fun reDraw(paintList:MutableList<PaintData>){ if(paintList.size > 0){ val paint = paintList.removeAt(paintList.size-1) if(paintList === mPaintedList){ mRevokedList.add(paint) }else{ mPaintedList.add(paint) } //清空緩存畫板 mBufferCanvas.drawColor(0, PorterDuff.Mode.CLEAR) invalidate() } }
然后就是畫筆的保存,在觸摸按下的時候,進行畫筆的保存
override fun onTouchEvent(event: MotionEvent): Boolean { when(event.action){ MotionEvent.ACTION_DOWN -> { //手指按下的時候 //將起始點移動到當前坐標 mPath.moveTo(event.x,event.y) //記錄上次觸摸的坐標,注意ACTION_DOWN方法只會執(zhí)行一次 preX = event.x preY = event.y //保存畫筆 mPaintedList.add(PaintData(Paint(mPaint),Path(mPath))) } MotionEvent.ACTION_MOVE -> { //手指移動的時候 //繪制圓滑曲線,即貝塞爾曲線,貝塞爾曲線這個知識自行了解 mPaintedList.get(mPaintedList.size-1).mPath.quadTo(preX,preY,event.x,event.y) //重新繪制,會調(diào)用onDraw方法 invalidate() preX = event.x preY = event.y } MotionEvent.ACTION_UP ->{ //清除路徑的內(nèi)容 mPath.reset() } } // true:告訴系統(tǒng),這個觸摸事件由我來處理 // false:告訴系統(tǒng),這個觸摸事件我不處理,這時系統(tǒng)會把觸摸事件傳遞給imageview的父節(jié)點 return true }
最后繪制的時候:
override fun onDraw(canvas: Canvas) { super.onDraw(canvas) //超出緩存的就固化到緩存bitmap while(mPaintedList.size > MAX_PAINT_RECORED){ val paintData = mPaintedList.removeAt(0) paintData.draw(mHoldCanvas) } //繪制固化的內(nèi)容到緩存Canvas mBufferCanvas.drawBitmap(mHoldBitmap,0f,0f,null) //繪制記錄的畫筆 for(paint in mPaintedList){ //重新繪制每個path paint.draw(mBufferCanvas) } //畫出緩存bitmap的內(nèi)容 canvas.drawBitmap(mBufferBitmap,0f,0f,null) }
最后清除畫布的時候,需要把畫筆列表也清除了:
/** * 清空畫布 */ fun clear() { mRevokedList.clear() mPaintedList.clear() mHoldCanvas.drawColor(0, PorterDuff.Mode.CLEAR) mBufferCanvas.drawColor(0, PorterDuff.Mode.CLEAR) invalidate() }
重點就是在于利用一個bean類來保存每筆的 畫筆和路徑,然后撤銷時候重新繪制。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android ActionBar完全解析使用官方推薦的最佳導航欄(上)
Action Bar是一種新増的導航欄功能,在Android 3.0之后加入到系統(tǒng)的API當中,它標識了用戶當前操作界面的位置,并提供了額外的用戶動作、界面導航等功能2017-04-04Flutter基于Dart Unwrapping Multiple Optional小技巧
這篇文章主要為大家介紹了Flutter Unwrapping Multiple Optional打開多個選項小技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12關(guān)于Android 6.0權(quán)限的動態(tài)適配詳解
Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用戶體驗, 同時也為程序員帶來新的負擔. 動態(tài)權(quán)限管理就是這樣, 一方面讓用戶更加容易的控制自己的隱私, 一方面需要重新適配應用權(quán)限,本文介紹了關(guān)于Android 6.0權(quán)限動態(tài)適配的相關(guān)資料,需要的朋友可以參考下。2017-11-11Android創(chuàng)建與解析XML(二)——詳解Dom方式
本篇文章主要介紹了Android創(chuàng)建與解析XML(二)——詳解Dom方式 ,這里整理了詳細的代碼,有需要的小伙伴可以參考下。2016-11-11Android編程基于自定義view實現(xiàn)公章效果示例【附源碼下載】
這篇文章主要介紹了Android編程基于自定義view實現(xiàn)公章效果,結(jié)合實例形式分析了Android使用自定義view進行圖形繪制的相關(guān)操作技巧,并附帶完整實例源碼供讀者下載參考,需要的朋友可以參考下2017-11-11