一、分析
這篇將會講解撤銷反撤銷功能的實現,先討論一下這個原理是怎么樣實現的。
每次撤回的內容,內容是怎么定義呢? 其實就是每一筆,每一筆作為撤回的內容,那每一筆怎么算呢,就是算手指從按下-移動-放開這一個過程就是一筆。
我們只需記錄這個過程為一筆,然后用一個已畫列表list列表來記錄這個過程的paint畫筆和路徑path。
撤銷的時候就把后面的一個數據移到另一個撤銷列表
反撤銷的時候,就把撤銷列表的最后面那條數據移動到已畫列表。
然后,還有一個重點,就是畫筆的保存數量,上面說記錄每一筆畫筆,這當然是有個限度,不可能畫了好幾百筆都記錄下來,這樣子內存消耗很大的,所以超出顯示畫筆數量的時候,我們就把以前的畫死在畫板上。
基本原理是這樣子的。接下來跟著我實現
二、實現
如何實現撤回功能
2.1 定義數據類
首先,需要一個bean類存儲每一筆的數據,這里定義一個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 時候清空數據列表*/ fun clear(isClearList:Boolean) { if(isClearList){ mRevokedList.clear() mPaintedList.clear() } mBufferCanvas.drawColor(0, PorterDuff.Mode.CLEAR) invalidate() }
2.3 實現撤銷方法
在view定義兩個列表,一個是已經畫的內容列表,一個是撤銷內容的列表
//儲存已經寫的筆畫private var mPaintedList: MutableList<PaintData> = ArrayList<PaintData>()//已經撤銷的列表private var mRevokedList: MutableList<PaintData> = ArrayList<PaintData>()
添加固話層canvas和bitmap,超出記錄的畫筆就寫死在固化層了
//固化層,超出最大筆畫就先繪制到這個層private lateinit var mHoldBitmap: Bitmapprivate 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繪制的內容,將會在這個mBufferBitmap內 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方法只會執行一次 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) //重新繪制,會調用onDraw方法 invalidate() preX = event.x preY = event.y } MotionEvent.ACTION_UP ->{ //清除路徑的內容 mPath.reset() } } // true:告訴系統,這個觸摸事件由我來處理 // false:告訴系統,這個觸摸事件我不處理,這時系統會把觸摸事件傳遞給imageview的父節點 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) } //繪制固化的內容到緩存Canvas mBufferCanvas.drawBitmap(mHoldBitmap,0f,0f,null) //繪制記錄的畫筆 for(paint in mPaintedList){ //重新繪制每個path paint.draw(mBufferCanvas) } //畫出緩存bitmap的內容 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類來保存每筆的 畫筆和路徑,然后撤銷時候重新繪制。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
新聞熱點
疑難解答