通过手指,Android在canvas中绘制
我需要build立一个手指画在canvas上的项目,
获取手指的触摸事件和运动事件,然后绘制。
任何人都可以告诉我如何开始项目,
什么是最好的组成部分做这样的事情?
开始通过在sdk示例中进行Fingerpaint演示。
另一个样本:
public class MainActivity extends Activity { DrawingView dv ; private Paint mPaint; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); dv = new DrawingView(this); setContentView(dv); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setColor(Color.GREEN); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(12); } public class DrawingView extends View { public int width; public int height; private Bitmap mBitmap; private Canvas mCanvas; private Path mPath; private Paint mBitmapPaint; Context context; private Paint circlePaint; private Path circlePath; public DrawingView(Context c) { super(c); context=c; mPath = new Path(); mBitmapPaint = new Paint(Paint.DITHER_FLAG); circlePaint = new Paint(); circlePath = new Path(); circlePaint.setAntiAlias(true); circlePaint.setColor(Color.BLUE); circlePaint.setStyle(Paint.Style.STROKE); circlePaint.setStrokeJoin(Paint.Join.MITER); circlePaint.setStrokeWidth(4f); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint); canvas.drawPath( mPath, mPaint); canvas.drawPath( circlePath, circlePaint); } private float mX, mY; private static final float TOUCH_TOLERANCE = 4; private void touch_start(float x, float y) { mPath.reset(); mPath.moveTo(x, y); mX = x; mY = y; } private void touch_move(float x, float y) { float dx = Math.abs(x - mX); float dy = Math.abs(y - mY); if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2); mX = x; mY = y; circlePath.reset(); circlePath.addCircle(mX, mY, 30, Path.Direction.CW); } } private void touch_up() { mPath.lineTo(mX, mY); circlePath.reset(); // commit the path to our offscreen mCanvas.drawPath(mPath, mPaint); // kill this so we don't double draw mPath.reset(); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touch_start(x, y); invalidate(); break; case MotionEvent.ACTION_MOVE: touch_move(x, y); invalidate(); break; case MotionEvent.ACTION_UP: touch_up(); invalidate(); break; } return true; } } }
快照
说明:
你正在创build一个视图类,然后扩展视图。 你重写onDraw()。 您添加手指触摸和移动的path。 你重写onTouch()这个目的。 在你的onDraw()中,使用你select的绘画来绘制path。 你应该调用invalidate()刷新视图。
要select选项,您可以单击菜单并select选项。
以下可以作为参考。 您可以根据您的需要修改以下内容。
public class FingerPaintActivity extends Activity implements ColorPickerDialog.OnColorChangedListener { MyView mv; AlertDialog dialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mv= new MyView(this); mv.setDrawingCacheEnabled(true); mv.setBackgroundResource(R.drawable.afor);//set the back ground if you wish to setContentView(mv); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setColor(0xFFFF0000); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(20); mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f); mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL); } private Paint mPaint; private MaskFilter mEmboss; private MaskFilter mBlur; public void colorChanged(int color) { mPaint.setColor(color); } public class MyView extends View { private static final float MINP = 0.25f; private static final float MAXP = 0.75f; private Bitmap mBitmap; private Canvas mCanvas; private Path mPath; private Paint mBitmapPaint; Context context; public MyView(Context c) { super(c); context=c; mPath = new Path(); mBitmapPaint = new Paint(Paint.DITHER_FLAG); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); canvas.drawPath(mPath, mPaint); } private float mX, mY; private static final float TOUCH_TOLERANCE = 4; private void touch_start(float x, float y) { //showDialog(); mPath.reset(); mPath.moveTo(x, y); mX = x; mY = y; } private void touch_move(float x, float y) { float dx = Math.abs(x - mX); float dy = Math.abs(y - mY); if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2); mX = x; mY = y; } } private void touch_up() { mPath.lineTo(mX, mY); // commit the path to our offscreen mCanvas.drawPath(mPath, mPaint); // kill this so we don't double draw mPath.reset(); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); //mPaint.setMaskFilter(null); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: touch_start(x, y); invalidate(); break; case MotionEvent.ACTION_MOVE: touch_move(x, y); invalidate(); break; case MotionEvent.ACTION_UP: touch_up(); invalidate(); break; } return true; } } private static final int COLOR_MENU_ID = Menu.FIRST; private static final int EMBOSS_MENU_ID = Menu.FIRST + 1; private static final int BLUR_MENU_ID = Menu.FIRST + 2; private static final int ERASE_MENU_ID = Menu.FIRST + 3; private static final int SRCATOP_MENU_ID = Menu.FIRST + 4; private static final int Save = Menu.FIRST + 5; @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); menu.add(0, COLOR_MENU_ID, 0, "Color").setShortcut('3', 'c'); menu.add(0, EMBOSS_MENU_ID, 0, "Emboss").setShortcut('4', 's'); menu.add(0, BLUR_MENU_ID, 0, "Blur").setShortcut('5', 'z'); menu.add(0, ERASE_MENU_ID, 0, "Erase").setShortcut('5', 'z'); menu.add(0, SRCATOP_MENU_ID, 0, "SrcATop").setShortcut('5', 'z'); menu.add(0, Save, 0, "Save").setShortcut('5', 'z'); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { mPaint.setXfermode(null); mPaint.setAlpha(0xFF); switch (item.getItemId()) { case COLOR_MENU_ID: new ColorPickerDialog(this, this, mPaint.getColor()).show(); return true; case EMBOSS_MENU_ID: if (mPaint.getMaskFilter() != mEmboss) { mPaint.setMaskFilter(mEmboss); } else { mPaint.setMaskFilter(null); } return true; case BLUR_MENU_ID: if (mPaint.getMaskFilter() != mBlur) { mPaint.setMaskFilter(mBlur); } else { mPaint.setMaskFilter(null); } return true; case ERASE_MENU_ID: mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); mPaint.setAlpha(0x80); return true; case SRCATOP_MENU_ID: mPaint.setXfermode(new PorterDuffXfermode( PorterDuff.Mode.SRC_ATOP)); mPaint.setAlpha(0x80); return true; case Save: AlertDialog.Builder editalert = new AlertDialog.Builder(FingerPaintActivity.this); editalert.setTitle("Please Enter the name with which you want to Save"); final EditText input = new EditText(FingerPaintActivity.this); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT); input.setLayoutParams(lp); editalert.setView(input); editalert.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String name= input.getText().toString(); Bitmap bitmap = mv.getDrawingCache(); String path = Environment.getExternalStorageDirectory().getAbsolutePath(); File file = new File("/sdcard/"+name+".png"); try { if(!file.exists()) { file.createNewFile(); } FileOutputStream ostream = new FileOutputStream(file); bitmap.compress(CompressFormat.PNG, 10, ostream); ostream.close(); mv.invalidate(); } catch (Exception e) { e.printStackTrace(); }finally { mv.setDrawingCacheEnabled(false); } } }); editalert.show(); return true; } return super.onOptionsItemSelected(item); } }
选色器
public class ColorPickerDialog extends Dialog { public interface OnColorChangedListener { void colorChanged(int color); } private OnColorChangedListener mListener; private int mInitialColor; private static class ColorPickerView extends View { private Paint mPaint; private Paint mCenterPaint; private final int[] mColors; private OnColorChangedListener mListener; ColorPickerView(Context c, OnColorChangedListener l, int color) { super(c); mListener = l; mColors = new int[] { 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000 }; Shader s = new SweepGradient(0, 0, mColors, null); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setShader(s); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(32); mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCenterPaint.setColor(color); mCenterPaint.setStrokeWidth(5); } private boolean mTrackingCenter; private boolean mHighlightCenter; @Override protected void onDraw(Canvas canvas) { float r = CENTER_X - mPaint.getStrokeWidth()*0.5f; canvas.translate(CENTER_X, CENTER_X); canvas.drawOval(new RectF(-r, -r, r, r), mPaint); canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint); if (mTrackingCenter) { int c = mCenterPaint.getColor(); mCenterPaint.setStyle(Paint.Style.STROKE); if (mHighlightCenter) { mCenterPaint.setAlpha(0xFF); } else { mCenterPaint.setAlpha(0x80); } canvas.drawCircle(0, 0, CENTER_RADIUS + mCenterPaint.getStrokeWidth(), mCenterPaint); mCenterPaint.setStyle(Paint.Style.FILL); mCenterPaint.setColor(c); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(CENTER_X*2, CENTER_Y*2); } private static final int CENTER_X = 100; private static final int CENTER_Y = 100; private static final int CENTER_RADIUS = 32; private int floatToByte(float x) { int n = java.lang.Math.round(x); return n; } private int pinToByte(int n) { if (n < 0) { n = 0; } else if (n > 255) { n = 255; } return n; } private int ave(int s, int d, float p) { return s + java.lang.Math.round(p * (d - s)); } private int interpColor(int colors[], float unit) { if (unit <= 0) { return colors[0]; } if (unit >= 1) { return colors[colors.length - 1]; } float p = unit * (colors.length - 1); int i = (int)p; p -= i; // now p is just the fractional part [0...1) and i is the index int c0 = colors[i]; int c1 = colors[i+1]; int a = ave(Color.alpha(c0), Color.alpha(c1), p); int r = ave(Color.red(c0), Color.red(c1), p); int g = ave(Color.green(c0), Color.green(c1), p); int b = ave(Color.blue(c0), Color.blue(c1), p); return Color.argb(a, r, g, b); } private int rotateColor(int color, float rad) { float deg = rad * 180 / 3.1415927f; int r = Color.red(color); int g = Color.green(color); int b = Color.blue(color); ColorMatrix cm = new ColorMatrix(); ColorMatrix tmp = new ColorMatrix(); cm.setRGB2YUV(); tmp.setRotate(0, deg); cm.postConcat(tmp); tmp.setYUV2RGB(); cm.postConcat(tmp); final float[] a = cm.getArray(); int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b); int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b); int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b); return Color.argb(Color.alpha(color), pinToByte(ir), pinToByte(ig), pinToByte(ib)); } private static final float PI = 3.1415926f; @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX() - CENTER_X; float y = event.getY() - CENTER_Y; boolean inCenter = java.lang.Math.sqrt(x*x + y*y) <= CENTER_RADIUS; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mTrackingCenter = inCenter; if (inCenter) { mHighlightCenter = true; invalidate(); break; } case MotionEvent.ACTION_MOVE: if (mTrackingCenter) { if (mHighlightCenter != inCenter) { mHighlightCenter = inCenter; invalidate(); } } else { float angle = (float)java.lang.Math.atan2(y, x); // need to turn angle [-PI ... PI] into unit [0....1] float unit = angle/(2*PI); if (unit < 0) { unit += 1; } mCenterPaint.setColor(interpColor(mColors, unit)); invalidate(); } break; case MotionEvent.ACTION_UP: if (mTrackingCenter) { if (inCenter) { mListener.colorChanged(mCenterPaint.getColor()); } mTrackingCenter = false; // so we draw w/o halo invalidate(); } break; } return true; } } public ColorPickerDialog(Context context, OnColorChangedListener listener, int initialColor) { super(context); mListener = listener; mInitialColor = initialColor; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); OnColorChangedListener l = new OnColorChangedListener() { public void colorChanged(int color) { mListener.colorChanged(color); dismiss(); } }; setContentView(new ColorPickerView(getContext(), l, mInitialColor)); setTitle("Pick a Color"); } }
关于上面Raghunandan美丽的代码。
许多人问如何“清除”图纸。 以下是如何做到这一点:
public void clearDrawing() { Utils.Log("RaghunandanDraw, how to clear...."); setDrawingCacheEnabled(false); // don't forget that one and the match below, // or you just keep getting a duplicate when you save. onSizeChanged(width, height, width, height); invalidate(); setDrawingCacheEnabled(true); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); width = w; // don't forget these height = h; mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); }
许多人问如何“保存”图纸。 以下是如何做到这一点:
public DrawingView(Context c) { circlePaint.setStrokeJoin(Paint.Join.MITER); circlePaint.setStrokeWidth(4f); etc... // in the class where you set up the view, add this: setDrawingCacheEnabled( true ); } public void saveDrawing() { Bitmap whatTheUserDrewBitmap = getDrawingCache(); // don't forget to clear it (see above) or you just get duplicates // almost always you will want to reduce res from the very high screen res whatTheUserDrewBitmap = ThumbnailUtils.extractThumbnail(whatTheUserDrewBitmap, 256, 256); // NOTE that's an incredibly useful trick for cropping/resizing squares // while handling all memory problems etc // http://stackoverflow.com/a/17733530/294884 // you can now save the bitmap to a file, or display it in an ImageView: ImageView testArea = ... testArea.setImageBitmap( whatTheUserDrewBitmap ); // these days you often need a "byte array". for example, // to save to parse.com or other cloud services ByteArrayOutputStream baos = new ByteArrayOutputStream(); whatTheUserDrewBitmap.compress(Bitmap.CompressFormat.PNG, 0, baos); byte[] yourByteArray; yourByteArray = baos.toByteArray(); }
希望它能帮助别人,因为这对我有帮助。
教程画线使用位图,canvas和涂料类。 在手指触摸和androiddraw 上画线
这里有一个简单的课用canvas画线,如下所示。
public class TestLineView extends View { private Paint paint; private PointF startPoint, endPoint; private boolean isDrawing; public TestLineView(Context context) { super(context); init(); } private void init() { paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Style.STROKE); paint.setStrokeWidth(2); paint.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { if(isDrawing) { canvas.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, paint); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startPoint = new PointF(event.getX(), event.getY()); endPoint = new PointF(); isDrawing = true; break; case MotionEvent.ACTION_MOVE: if(isDrawing) { endPoint.x = event.getX(); endPoint.y = event.getY(); invalidate(); } break; case MotionEvent.ACTION_UP: if(isDrawing) { endPoint.x = event.getX(); endPoint.y = event.getY(); isDrawing = false; invalidate(); } break; default: break; } return true; } }
你可以简单地使用这个类:
public class DoodleCanvas extends View{ private Paint mPaint; private Path mPath; public DoodleCanvas(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(10); mPath = new Path(); } @Override protected void onDraw(Canvas canvas) { canvas.drawPath(mPath, mPaint); super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mPath.moveTo(event.getX(), event.getY()); break; case MotionEvent.ACTION_MOVE: mPath.lineTo(event.getX(), event.getY()); invalidate(); break; case MotionEvent.ACTION_UP: break; } return true; } }
我认为添加一个东西是非常重要的,如果你使用布局通货膨胀的构造函数在绘制不正确,添加这些构造函数在类中:
public DrawingView(Context c, AttributeSet attrs) { super(c, attrs); ... } public DrawingView(Context c, AttributeSet attrs, int defStyle) { super(c, attrs, defStyle); ... }
或者android系统无法膨胀布局文件。 我希望这可以帮助。