这是API Demo中的一个例子,效果图如上,代码如下:
public class TouchPaint extends Activity{ /**Used as a pluse to gradually fade the contents of the window*/ private static final int FADE_MSG = 1; /**Menu ID for the command to clear the window*/ private static final int CLEAR_ID = Menu.FIRST; /**Menu ID for the command to toggle fading*/ private static final int FADE_ID = Menu.FIRST + 1; /**How often to fade the contents of the window(in ms)*/ private static final int FADE_DELAY = 100; /**The view responsible for drawing the window*/ private TouchPaintView mTouchPaintView; /**Is fading mode enabled?*/ private boolean mFading; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); mTouchPaintView = new TouchPaintView(this); setContentView(mTouchPaintView); mTouchPaintView.requestFocus(); mFading = savedInstanceState != null ? savedInstanceState.getBoolean("fading", true) : true; } @Override protected void onSaveInstanceState(Bundle outState) { // TODO Auto-generated method stub super.onSaveInstanceState(outState); outState.putBoolean("fading", true); } @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); if(mFading){ startFading(); } } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); stopFading(); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, CLEAR_ID, 0, "Clear"); menu.add(0, FADE_ID, 0, "Fade"); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case CLEAR_ID: mTouchPaintView.clear(); break; case FADE_ID: mFading = !mFading; if(mFading){ stopFading(); }else { startFading(); } break; default: break; } return super.onOptionsItemSelected(item); } private void stopFading() { mHandler.removeMessages(FADE_MSG); } private void startFading() { mHandler.removeMessages(FADE_MSG); mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_MSG), FADE_DELAY); } private Handler mHandler = new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { //Upon receiving the fade pulse,we have the view perform a //fade and then enqueue a new message to pulse at the desired next time case FADE_MSG: mTouchPaintView.fade(); mHandler.sendEmptyMessageDelayed(FADE_MSG, FADE_DELAY); break; default: break; } }; }; } class TouchPaintView extends View{ private static final int FADE_ALPHA = 0x06; private static final int MAX_FADE_STEPS = 256/FADE_ALPHA + 4; private Bitmap mBitmap; private Canvas mCanvas; private Paint mPaint; private Paint mFadePaint; private Rect mRect = new Rect(); private boolean mCurDown; private int mCurX; private int mCurY; private float mCurPressure; private float mCurSize; private int mCurWidth; private int mFadeStepsIndex = MAX_FADE_STEPS; public TouchPaintView(Context context) { super(context); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setARGB(255, 255, 255, 255); mFadePaint = new Paint(); mFadePaint.setDither(true); mFadePaint.setARGB(FADE_ALPHA, 0, 0, 0); } public void clear() { if (null != mCanvas) { mPaint.setARGB(0xff, 0, 0, 0); //Fill the entire canvas' bitmap (restricted to the current clip) with the specified paint. //This is equivalent (but faster) to drawing an infinitely large rectangle with the specified paint. mCanvas.drawPaint(mPaint); invalidate(); mFadeStepsIndex = MAX_FADE_STEPS; } } public void fade() { if (null != mCanvas &&mFadeStepsIndex <= MAX_FADE_STEPS) { mCanvas.drawPaint(mFadePaint); invalidate(); mFadeStepsIndex++; } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { int curW = mBitmap != null ? mBitmap.getWidth() : 0; int curH = mBitmap != null ? mBitmap.getHeight() : 0; if (curW > w && curH > h) { return; } if (curW < w) { curW = w; } if (curH <h) { curH = h; } Bitmap newBitmap = Bitmap.createBitmap(curW, curH, Bitmap.Config.RGB_565); Canvas newCanvas = new Canvas(); //Specify a bitmap for the canvas to draw into. newCanvas.setBitmap(newBitmap); mBitmap = newBitmap; mCanvas = newCanvas; mFadeStepsIndex = MAX_FADE_STEPS; } @Override protected void onDraw(Canvas canvas) { if (null != mBitmap) { canvas.drawBitmap(mBitmap, 0, 0, mPaint); } } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); mCurDown = (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE); int N = event.getPointerCount(); for (int i = 0; i < N; i++) { drawCircle(event.getHistoricalX(i), event.getHistoricalY(i), event.getHistoricalPressure(i), event.getHistoricalSize(i)); } drawCircle(event.getX(), event.getY(), event.getPressure(), event.getSize()); return true; } private void drawCircle(float x, float y, float pressure, float size){ mCurX = (int)x; mCurY = (int)y; mCurPressure = pressure; mCurSize = size; mCurWidth = (int)(mCurSize*(getWidth()/3)); if(mCurWidth < 1){ mCurWidth = 1; } if (mCurDown && null != mBitmap) { int pressureLevel = (int)(mCurPressure * 255); mPaint.setARGB(pressureLevel, 255, 255, 255); mCanvas.drawCircle(mCurX, mCurY, mCurWidth, mPaint); mRect.set(mCurX - mCurWidth -2, mCurY - mCurWidth -2, mCurX + mCurWidth +2, mCurY + mCurWidth +2); invalidate(mRect); } mFadeStepsIndex = 0; } }备注一:
理解这个程序的关键是明白Bitmap、Canvas、Paint之间的关系。
Bitmap mBitmap = Bitmap.createBitmap(curW, curH, Bitmap.Config.RGB_565); Canvas mCanvas = new Canvas(); //Specify a bitmap for the canvas to draw into. mCanvas.setBitmap(mBitmap);这里就可以理解为这个mCanvas和mBitmap绑定了,我以后用mCanvas画的东西都是画到mBitmap上的,然后不管之前是画的什么最后在onDraw的时候,我只要画出这个bitmap就可以了,即使画这个mBitmap的不是原来的mCanvas,因为我的mBitmap上已经有mCanvas画上的东西了。
@Override protected void onDraw(Canvas canvas) { if (null != mBitmap) { canvas.drawBitmap(mBitmap, 0, 0, mPaint); } }备注二:
在程序中我可以清空整个画布的内容,也可以设置是是否渐变的效果,这都是通过设置mCanvas的Paint来实现的
public void clear() { if (null != mCanvas) { mPaint.setARGB(0xff, 0, 0, 0); //Fill the entire canvas' bitmap (restricted to the current clip) with the specified paint. //This is equivalent (but faster) to drawing an infinitely large rectangle with the specified paint. mCanvas.drawPaint(mPaint); invalidate(); mFadeStepsIndex = MAX_FADE_STEPS; } } public void fade() { if (null != mCanvas &&mFadeStepsIndex <= MAX_FADE_STEPS) { mCanvas.drawPaint(mFadePaint); invalidate(); mFadeStepsIndex++; } }以上面的清空画布为例:当将画笔设置成透明黑色mPaint.setARGB(0xff, 0, 0, 0);,然后mCanvas.drawPaint(mPaint);,关键弄明白API中的解释:
//Fill the entire canvas' bitmap (restricted to the current clip) with the specified paint.
//This is equivalent (but faster) to drawing an infinitely large rectangle with the specified paint.
mCanvas.drawPaint(mPaint);
也就是将mCanvas的bitmap设置成指定画笔,下面的渐变效果也是这样设置的