先上图:
主要代码:
package com.example.administrator.androidtest; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.os.Environment; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.Toast; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class TuyaView extends View { private int view_width=0;//屏幕宽 private int view_height=0;//屏幕高 private float preX;//起点X private float preY;//起点Y; private Path path;//路径 public Paint paint=null;//画笔 public Paint mBitmapPaint;// 画布的画笔 Bitmap cacheBitmap=null;//定义一个内存图片为缓存区 Canvas cacheCanvas=null;//定义cacheBitmap上的canvas对象 private static List<DrawPath> savePath;// 保存Path路径的集合,用List集合来模拟栈 private DrawPath dp;// 记录Path 路径的对象 int name=100; public TuyaView(Context context, AttributeSet set){ super(context,set); view_width=context.getResources().getDisplayMetrics().widthPixels; view_height=context.getResources().getDisplayMetrics().heightPixels; cacheBitmap=Bitmap.createBitmap(view_width, view_height, Bitmap.Config.ARGB_8888); // 保存一次一次绘制出来的图形 cacheCanvas=new Canvas(cacheBitmap); mBitmapPaint=new Paint(Paint.DITHER_FLAG); paint=new Paint(); paint.setColor(Color.RED);//默认画笔颜色 //设置画笔风格 paint.setStyle(Paint.Style.STROKE);//设置填充方式为描边 paint.setStrokeJoin(Paint.Join.ROUND);//设置笔刷的图形样式 paint.setStrokeCap(Paint.Cap.ROUND);//设置画笔转弯处的连接风格 paint.setStrokeWidth(5);//设置默认宽度5像素 paint.setAntiAlias(true);//抗锯齿功能 paint.setDither(true);//抖动效果 savePath=new ArrayList<DrawPath>(); } public void clear() { // paint.setAlpha(0); //设置透明度为0 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); paint.setStrokeWidth(25);//设置笔触宽度 } //清空画布 public void clearall() { cacheBitmap=Bitmap.createBitmap(view_width,view_height, Bitmap.Config.ARGB_8888); cacheCanvas.setBitmap(cacheBitmap);// 重新设置画布,相当于清空画布 invalidate();// 刷新 } @Override public void onDraw(Canvas canvas) { // canvas.drawColor(0xFFFFFFFF);//默认背景颜色白色 canvas.drawColor(MainActivity.getcolor());//选择背景颜色 // 将前面已经画过得显示出来 canvas.drawBitmap(cacheBitmap, 0, 0, mBitmapPaint); if (path!=null) { // 实时的显示 canvas.drawPath(path, paint); } } // 撤销就是将画布清空,将保存下来的Path路径最后一个移除掉,重新将路径画在画布上面。 public void undo() { cacheBitmap = Bitmap.createBitmap(view_width, view_height, Bitmap.Config.ARGB_8888); cacheCanvas.setBitmap(cacheBitmap);// 重新设置画布,相当于清空画布 // 清空画布,但是如果图片有背景的话,则使用上面的重新初始化的方法,用该方法会将背景清空掉... if (savePath != null && savePath.size() > 0) { // 移除最后一个path,相当于出栈操作 savePath.remove(savePath.size() - 1); Iterator<DrawPath> iter = savePath.iterator(); while (iter.hasNext()) { DrawPath drawPath = iter.next(); cacheCanvas.drawPath(drawPath.path, drawPath.paint); } invalidate();// 刷新 } } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 每次down下去重新new一个Path path = new Path(); //每一次记录的路径对象是不一样的 dp = new DrawPath(); dp.path = path; dp.paint = paint; path.moveTo(x, y); preX = x; preY = y; invalidate(); break; case MotionEvent.ACTION_MOVE: float dx=Math.abs(x - preX); float dy=Math.abs(preY - y); if (dx>=4||dy>=4) { // 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用path.lineTo也是可以的) path.quadTo(preX, preY,(x + preX) / 2,(y + preY) / 2); preX=x; preY=y; } invalidate(); break; case MotionEvent.ACTION_UP: path.lineTo(preX, preY); cacheCanvas.drawPath(path, paint); //将一条完整的路径保存下来(相当于入栈操作) savePath.add(dp); path = null;// 重新置空 invalidate(); break; } return true; } public void save() { try{ saveBitmap("myPicture"); }catch(IOException e){ e.printStackTrace(); } } //保存绘制好的位图 public void saveBitmap(String fileName) throws IOException{ if (cacheBitmap!= null) { String path = Environment.getExternalStorageDirectory().getPath(); name+=1; File file = new File(path+"/Pictures/",name+".png"); file.createNewFile();//创建一个新文件 try { FileOutputStream bos= new FileOutputStream(file); cacheBitmap.compress(Bitmap.CompressFormat.PNG, 100, bos); bos.flush(); bos.close(); Toast.makeText(getContext(), "保存成功!", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); } } } public class DrawPath { public Path path;// 路径 public Paint paint;// 画笔 } }
//-----------------------------------------------------------------
package com.example.administrator.androidtest; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.ActivityCompat; import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Main3Activity extends Activity implements OnClickListener, OnTouchListener { private static final int REQUEST_EXTERNAL_STORAGE = 1; private static String[] PERMISSIONS_STORAGE = { "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE" }; String num,col; int dw,dh; //显示部分宽高 int w=0,h=0;//矩形宽高 int r=0;//圆半径 int judge=0,judge1=0,name=0; String col1,col2;//填充颜色 private ImageView ima; private Button choose, save,choose_width,choose_color,choose_shape,clear,undo, rect,rect1,cicle,cicle1,back; private Bitmap bitmap, mbitmap; private Canvas canvas; private Paint paint,mpaint; private Path path;//路径 private Matrix matrix; private float preX=0,preY=0; int hRatio,wRatio; private DrawPath dp;// 记录Path 路径的对象 private static List<DrawPath> savePath;// 保存Path路径的集合,用List集合来模拟栈 public class DrawPath { public Path path;// 路径 public Paint paint;// 画笔 } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main3); verifyStoragePermissions(this); ima=(ImageView)findViewById(R.id.choosePic); choose=(Button)findViewById(R.id.btn_choose); save=(Button)findViewById(R.id.btn_save); choose_width=(Button)findViewById(R.id.choose_width); choose_color=(Button)findViewById(R.id.choose_color); choose_shape=(Button)findViewById(R.id.rotate); clear=(Button)findViewById(R.id.btn_clear); undo=(Button)findViewById(R.id.btn_undo); back=(Button)findViewById(R.id.back); rect=(Button)findViewById(R.id.rect); rect1=(Button)findViewById(R.id.rect1); cicle=(Button)findViewById(R.id.cicle); cicle1=(Button)findViewById(R.id.cicle1); //设置事件监听 choose.setOnClickListener(this); save.setOnClickListener(this); choose_width.setOnClickListener(this); choose_color.setOnClickListener(this); choose_shape.setOnClickListener(this); clear.setOnClickListener(this); undo.setOnClickListener(this); back.setOnClickListener(this); ima.setOnTouchListener(this); rect.setOnClickListener(this); rect1.setOnClickListener(this); cicle.setOnClickListener(this); cicle1.setOnClickListener(this); //设置save为不可用 save.setEnabled(false); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_choose: // 设置save为不可用 if (save.isEnabled()) save.setEnabled(false); // 使用Intent来选择图片 Intent it=new Intent( Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(it,0); break; case R.id.btn_clear: clear(); break; case R.id.btn_undo: undo(); break; case R.id.btn_save: if (mbitmap!= null) { String path = Environment.getExternalStorageDirectory().getPath(); name+=1; File file = new File(path+"/Pictures/",name+".png"); try { verifyStoragePermissions(this); FileOutputStream bos= new FileOutputStream(file); mbitmap.compress(Bitmap.CompressFormat.PNG, 100, bos); bos.flush(); bos.close(); Toast.makeText(this,"保存成功",Toast.LENGTH_SHORT).show(); System.out.println(path); } catch (Exception e) { e.printStackTrace(); } } break; case R.id.choose_width: Intent it3=new Intent(Main3Activity.this,widthActivity.class); startActivityForResult(it3,2); break; case R.id.choose_color: Intent it2=new Intent(Main3Activity.this,colorActivity.class); startActivityForResult(it2,3); break; case R.id.rotate: Roate(); break; case R.id.back: Intent it1=new Intent(this,Main2Activity.class); startActivity(it1); break; case R.id.rect: Intent it4=new Intent(this,rectActivity.class); startActivityForResult(it4,4); break; case R.id.rect1: drawRect2(xx,yy,w,h,col1); break; case R.id.cicle: Intent it5=new Intent(this,cicleActivity.class); startActivityForResult(it5,5); break; case R.id.cicle1: drawCicle2(xx,yy,r,col2); break; } } // 撤销就是将画布清空,将保存下来的Path路径最后一个移除掉,重新将路径画在画布上面。 public void undo() { canvas=new Canvas(mbitmap); matrix=new Matrix(); canvas.drawBitmap(bitmap, matrix, paint);// 绘制bitmap ima.setImageBitmap(mbitmap);// 把mbitmap设置到ima上面 ima.setOnTouchListener(this); if (savePath != null && savePath.size() > 0) { // 移除最后一个path,相当于出栈操作 savePath.remove(savePath.size() - 1); Iterator<DrawPath> iter = savePath.iterator(); while (iter.hasNext()) { DrawPath drawPath = iter.next(); canvas.drawPath(drawPath.path, drawPath.paint); } ima.invalidate();// 刷新 } } //橡皮檫 public void clear() { paint.setAlpha(0); //设置透明度为0 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); paint.setStrokeWidth(25);//设置笔触宽度 } //实现涂鸦实时显示 public void onDraw(Canvas canvas) { canvas.drawBitmap(mbitmap, 0, 0, paint); if (path!=null) { // 实时的显示 canvas.drawPath(path, paint); } } //图像点击一次做15度旋转 int angle=0; public void Roate() { matrix=ima.getImageMatrix(); if(angle>=360) angle=0; else angle+=15; matrix.setRotate(angle,ima.getWidth()/2,ima.getHeight()/2); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); canvas.drawPaint(paint); // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); // ima.postInvalidate(); paint.setXfermode(null);//取消擦除效果 canvas.drawBitmap(bitmap, matrix, mpaint);// 绘制bitmap ima.setImageBitmap(mbitmap);// 把mbitmap设置到ima上面 ima.invalidate(); ima.setOnTouchListener(this); } int xx,yy;//选点坐标 private void drawRect2(int x,int y,int w,int h,String col1)//画矩形 { Rect r=new Rect(); r.left=x; r.top=y; r.right=x+w; r.bottom=y+h; paint.setColor(Integer.parseInt(col1)); if(judge==0) paint.setStyle(Paint.Style.STROKE); else paint.setStyle(Paint.Style.FILL); //实心矩形 canvas.drawRect(r,paint); paint.setStyle(Paint.Style.STROKE); } private void drawCicle2(int x,int y,int r,String col1)//画圆形 { if(judge1==0) paint.setStyle(Paint.Style.STROKE); else paint.setStyle(Paint.Style.FILL);//实心圆 paint.setColor(Integer.parseInt(col1)); canvas.drawCircle(x,y,r,paint); paint.setStyle(Paint.Style.STROKE); } @Override protected void onActivityResult(int requestCode,int resultCode,Intent data) { super.onActivityResult(requestCode,resultCode,data); if (resultCode==RESULT_OK) { Uri uri=data.getData();//获取URI // 获取默认Display Display currentDisplay=getWindowManager().getDefaultDisplay(); //显示部分的宽高 dw=currentDisplay.getWidth(); dh=currentDisplay.getHeight(); BitmapFactory.Options bmFactoryOptions=new Options();// 使用BitmapFactory创建位图Options bmFactoryOptions.inJustDecodeBounds=true;// 设置只是获取图片的尺寸 // 使用decodeStream方法创建位图,getContentResolver()获取內容提供器 try { bitmap=BitmapFactory.decodeStream(getContentResolver() .openInputStream(uri), null, bmFactoryOptions); } catch (FileNotFoundException e) { e.printStackTrace(); } // 计算图像宽高与当前宽高的比率 hRatio=(int)Math.ceil(bmFactoryOptions.outHeight/(float)dh); wRatio=(int)Math.ceil(bmFactoryOptions.outWidth/(float)dw); //判断是按高比率缩放还是宽比例缩放 if (hRatio>1||wRatio>1) { if (hRatio>wRatio) { bmFactoryOptions.inSampleSize=hRatio; } else { bmFactoryOptions.inSampleSize=wRatio; } } // 对图像进行解码 bmFactoryOptions.inJustDecodeBounds=false; try { bitmap=BitmapFactory.decodeStream(getContentResolver() .openInputStream(uri),null,bmFactoryOptions); }catch(FileNotFoundException e){ e.printStackTrace(); } // 在加载位圆之后,创建一个可变的位图对象mbitmap,并在其中绘制bitmap对象 mbitmap=Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); canvas=new Canvas(mbitmap);// 使用mbitmap作为构造参数,创建canvas mpaint=new Paint(Paint.DITHER_FLAG); paint=new Paint();// 画笔 paint.setAntiAlias(true);//抗锯齿功能 paint.setDither(true);//抖动效果 paint.setStyle(Paint.Style.STROKE);//设置填充方式为描边 paint.setStrokeJoin(Paint.Join.ROUND);//设置笔刷的图形样式 paint.setStrokeCap(Paint.Cap.ROUND);//设置画笔转弯处的连接风格 paint.setColor(Color.BLACK);// 设置画笔颜色为黑色 paint.setStrokeWidth(5);// 设置画笔大小 matrix=new Matrix();// 创建matrix,此处对图像不进行任何的缩放,旋转等操作 canvas.drawBitmap(bitmap, matrix, paint);// 绘制bitmap ima.setImageBitmap(mbitmap);// 把mbitmap设置到ima上面 save.setEnabled(true);// 此时设置save为可用状态 ima.setOnTouchListener(this); savePath=new ArrayList<DrawPath>(); } if(requestCode==2) { if(resultCode==3) { num=data.getStringExtra("num"); paint.setStrokeWidth(Integer.parseInt(num)); } } if(requestCode==3) { if(resultCode==2) { col=data.getStringExtra("col"); paint.setColor(Integer.parseInt(col)); } } if(requestCode==4) { if(resultCode==4) { w=Integer.parseInt(data.getStringExtra("w")); h=Integer.parseInt(data.getStringExtra("h")); judge=Integer.parseInt(data.getStringExtra("yn")); col1=data.getStringExtra("col1"); } } if(requestCode==5) { if(resultCode==5) { r=Integer.parseInt(data.getStringExtra("r")); judge1=Integer.parseInt(data.getStringExtra("yn")); col2=data.getStringExtra("col2"); } } } @Override public boolean onTouch(View v,MotionEvent event) { float x=event.getX(); float y=event.getY(); xx=(int) Math.ceil(x); yy=(int) Math.ceil(y); // 判断不同状态 switch(event.getAction()){ case MotionEvent.ACTION_DOWN: // 每次down下去重新new一个Path path = new Path(); //每一次记录的路径对象是不一样的 dp = new DrawPath(); dp.path = path; dp.paint = paint; path.moveTo(x, y); // 记录按下坐标 preX=x; preY=y; ima.invalidate(); break; case MotionEvent.ACTION_MOVE: // 移动过程中,不断绘制Line float dx=Math.abs(x - preX); float dy=Math.abs(preY - y); if (dx>=4||dy>=4) { path.quadTo(preX, preY,(x + preX) / 2,(y + preY) / 2); onDraw(canvas);//实时显示 preX=x; preY=y; } ima.invalidate(); break; case MotionEvent.ACTION_UP: path.lineTo(preX, preY); canvas.drawPath(path, paint); //将一条完整的路径保存下来(相当于入栈操作) savePath.add(dp); path = null;// 重新置空 ima.invalidate(); break; } return true; } //Android6.0需要设置动态访问权限 public static void verifyStoragePermissions(Activity activity) { try { //检测是否有写的权限 int permission = ActivityCompat.checkSelfPermission(activity, "android.permission.WRITE_EXTERNAL_STORAGE"); if (permission != PackageManager.PERMISSION_GRANTED) { // 没有写的权限,去申请写的权限,会弹出对话框 ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE); } } catch (Exception e) { e.printStackTrace(); } } }