將一個請求封裝成一個對象,從而讓用戶使用不一樣的請求把客戶端參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操做。canvas
命令模式的本質是對命令進行封裝,將**發出命令的責任和執行命令的責任**分割開。 請求的一方發出請求,要求執行一個操做; 接收的一方收到請求,並執行操做; 請求的一方沒必要知道接收請求一方的接口,更沒必要知道請求是怎麼被接收、操做是否被執行、什麼時候執行以及如何執行的; 請求自己也是一個對象,這個對象和其餘對象同樣能夠被存儲和傳遞。bash
定義因此具體命令類的抽象接口ide
ConcreteCommand 具體命令類測試
實現抽象命令類的接口,實現執行方法excute()來調用接收者對象的相應操做ui
Invoker 調用者this
請求的發送者,它經過命令對象來執行請求,它與抽象命令類之間存在關聯關係。在程序運行時,將調用具體命令對象的excute()方法,間接調用接收者的相關操做spa
負責具體實現或實施一個請求,是執行具體邏輯的角色日誌
Client 客戶類code
在客戶類中須要建立發送者對象和具體命令類對象,在建立具體命令對象時指定其對應的接收者,發送者和接收者之間經過具體命令對象實現間接調用orm
public interface IBrush {
/**
* 觸點接觸時
*
* @param path 路徑對象
* @param x 當前位置的x座標
* @param y 當前位置的y座標
*/
void down(Path path, float x, float y);
/**
* 觸點移動時
*
* @param path 路徑對象
* @param x 當前位置的x座標
* @param y 當前位置的y座標
*/
void move(Path path, float x, float y);
/**
* 觸點離開時
*
* @param path 路徑對象
* @param x 當前位置的x座標
* @param y 當前位置的y座標
*/
void up(Path path, float x, float y);
}
複製代碼
抽象筆觸 抽象接收者 執行命令的抽象
public class CircleBrush implements IBrush {
@Override public void down(Path path, float x, float y) {
}
@Override public void move(Path path, float x, float y) {
path.addCircle(x, y, 10, Path.Direction.CW);
}
@Override public void up(Path path, float x, float y) {
}
}
複製代碼
圓點線條 具體接收者
public class NormalBrush implements IBrush {
@Override public void down(Path path, float x, float y) {
path.moveTo(x, y);
}
@Override public void move(Path path, float x, float y) {
path.lineTo(x, y);
}
@Override public void up(Path path, float x, float y) {
}
}
複製代碼
普通線條 具體接收者
public interface IDraw {
/**
* 繪製命令
*
* @param canvas 畫筆對象
*/
void draw(Canvas canvas);
/**
* 撤銷命令
*/
void undo();
}
複製代碼
抽象命令類
public class DrawPath implements IDraw {
public Path path;
public Paint paint;
@Override public void draw(Canvas canvas) {
canvas.drawPath(path, paint);
}
@Override public void undo() {
}
}
複製代碼
繪製路徑的方法 具體命令類
public class DrawInvoker {
/**
* 繪製列表
*/
private List<DrawPath> drawList = Collections.synchronizedList(new ArrayList<DrawPath>());
/**
* 重作列表
*/
private List<DrawPath> redoList = Collections.synchronizedList(new ArrayList<DrawPath>());
/**
* 增長一個命令 設值注入形式
*
* @parm command 具體命令類對象
*/
public void add(DrawPath command) {
redoList.clear();
drawList.add(command);
}
/**
* 撤銷上一步的操做
*/
public void undo() {
if (drawList.size() > 0) {
DrawPath undo = drawList.get(drawList.size() - 1);
drawList.remove(drawList.size() - 1);
undo.undo();
redoList.add(undo);
}
}
/**
* 恢復撤銷的操做
*/
public void redo() {
if (redoList.size() > 0) {
DrawPath redoCommand = redoList.get(redoList.size() - 1);
redoList.remove(redoList.size() - 1);
drawList.add(redoCommand);
}
}
/**
* 執行命令
* 業務方法 調用命令類的draw()方法
*/
public void execute(Canvas canvas) {
if (drawList != null) {
for (DrawPath tmp : drawList) {
tmp.draw(canvas);
}
}
}
/**
* 判斷是否能夠重作
*/
public boolean canRedo() {
return redoList.size() > 0;
}
/**
* 判斷是否能夠撤銷
*/
public boolean canUndo() {
return drawList.size() > 0;
}
}
複製代碼
調用者類,對繪製命令的進一步封裝,實現撤銷和重作方法
public class DrawCanvas extends SurfaceView implements SurfaceHolder.Callback {
public boolean isDrawing, isRunning;
private Bitmap mBitmap;
private DrawInvoker mInvoker;
private DrawThread mDrawThread;
public DrawCanvas(Context context, AttributeSet attrs) {
super(context, attrs);
mInvoker = new DrawInvoker();
mDrawThread = new DrawThread();
getHolder().addCallback(this);
}
@Override public void surfaceCreated(SurfaceHolder holder) {
isRunning = true;
mDrawThread.start();
}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
}
@Override public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
isRunning = false;
while (retry) {
try {
mDrawThread.join();
retry = false;
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 增長繪製路徑
*/
public void add(DrawPath path) {
mInvoker.add(path);
}
public void redo() {
isDrawing = true;
mInvoker.redo();
}
public void undo() {
isDrawing = true;
mInvoker.undo();
}
public boolean canRedo() {
return mInvoker.canRedo();
}
public boolean canUndo() {
return mInvoker.canUndo();
}
private class DrawThread extends Thread {
@Override public void run() {
Canvas canvas = null;
while (isRunning) {
if (isDrawing) {
try {
canvas = getHolder().lockCanvas(null);
if (mBitmap == null) {
mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
}
Canvas c = new Canvas(mBitmap);
c.drawColor(0, PorterDuff.Mode.CLEAR);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
mInvoker.execute(c);
canvas.drawBitmap(mBitmap, 0, 0, null);
} finally {
getHolder().unlockCanvasAndPost(canvas);
}
isDrawing = false;
}
}
}
}
@Override public boolean performClick() {
return super.performClick();
}
}
複製代碼
自定義view ,承擔畫板功能,真正的執行者畫布
public class DrawActivity extends AppCompatActivity implements View.OnClickListener {
private DrawPath mPath;
private DrawCanvas mCanvas;
private Paint mPaint;
private IBrush mBrush;
private Button btnRedo, btnUndo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test2);
mPaint = new Paint();
mPaint.setColor(0xFFFFFFFF);
mPaint.setStrokeWidth(3);
mBrush = new NormalBrush();
mCanvas = findViewById(R.id.draw_canvas);
mCanvas.setOnTouchListener(new DrawTouchListener());
btnRedo = findViewById(R.id.redo);
btnUndo = findViewById(R.id.undo);
btnRedo.setEnabled(false);
btnUndo.setEnabled(false);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.red:
mPaint = new Paint();
mPaint.setColor(0xFFFF0000);
mPaint.setStrokeWidth(3);
break;
case R.id.green:
mPaint = new Paint();
mPaint.setColor(0xFF00FF00);
mPaint.setStrokeWidth(3);
break;
case R.id.blue:
mPaint = new Paint();
mPaint.setColor(0xFF0000FF);
mPaint.setStrokeWidth(3);
break;
case R.id.normal:
mBrush = new NormalBrush();
break;
case R.id.circle:
mBrush = new CircleBrush();
break;
case R.id.redo:
mCanvas.redo();
if (!mCanvas.canRedo()) {
btnRedo.setEnabled(false);
}
btnUndo.setEnabled(true);
break;
case R.id.undo:
mCanvas.undo();
if (!mCanvas.canUndo()) {
btnUndo.setEnabled(false);
}
btnRedo.setEnabled(true);
break;
default:
break;
}
}
private class DrawTouchListener implements View.OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath = new DrawPath();
mPath.paint = mPaint;
mPath.path = new Path();
mBrush.down(mPath.path, event.getX(), event.getY());
break;
case MotionEvent.ACTION_UP:
mBrush.up(mPath.path, event.getX(), event.getY());
mCanvas.add(mPath);
mCanvas.isDrawing = true;
btnUndo.setEnabled(true);
btnRedo.setEnabled(false);
break;
case MotionEvent.ACTION_MOVE:
mBrush.move(mPath.path, event.getX(), event.getY());
break;
default:
break;
}
return true;
}
}
}
複製代碼
測試Activity,Client類
在ACTION_DOWN、ACTION_UP事件觸發時,mBrush會執行對應的事件方法,而不用關心具體的畫筆是什麼樣子的。若是要增長一個新的樣式也只須要添加一個繼承IBrush接口的具體接收者對象,修改畫筆樣式只須要將mBrush指向對應的具體畫筆類
諸如撤銷,重作,或者日誌保存等等之類的操做,交由DrawCanvas去實現,Client類只須要調用對應的方法,而不需關係具體實現邏輯,在DrawCanvas中則關聯了DrawInvoker對象,實現撤銷和重作等方法正式有DrawInvoker正式實現的。
Client在這裏只作了這件事情,1發出執行請求,2給定接收對象,3處理一些本身的邏輯。
命令模式基本上能夠運用在任何地方,可是在實際開發中,需不須要或者說值不值得使用命令模式須要斟酌,由於這務必會涉及到大量類的建立