package com.lfk.drawapictiure; import android.content.ContentResolver; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.TextView; import android.widget.Toast; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; class PaintView extends View { // drawing board private Bitmap mBitmap; // if you set a picture in you will use it private Bitmap mBitmapInit; private int mBitmapBackGround = R.drawable.whitbackground; private Canvas mCanvas; private Path mPath; private Paint mBitmapPaint; private Paint mEraserPaint; private Paint mPaint; // width of screen private int width; // height of screen private int height; private Context context; // pass judgement on paint/eraser private boolean IsPaint = true; // drawing x,y private float mX, mY; // judge your fingers' tremble private static final float TOUCH_TOLERANCE = 4; // judge long pressed private static final long TOUCH_LONG_PRESSED = 500; private boolean IsRecordPath = true; // private PathNode pathNode; private boolean mIsLongPressed; private boolean IsShowing = false; private boolean IsFirstTime = true; private long Touch_Down_Time; private long Touch_Up_Time; private OnPathListener listener; private static final int CHOOSEPATH = 0; private static final int INDIVIDE = 1; private boolean ReDoOrUnDoFlag = false; private PathNode pathNode; private ArrayList<PathNode.Node> ReDoNodes = new ArrayList<>(); public PaintView(Context context,AttributeSet attrs) { super(context,attrs); this.context = context; mPaint = new Paint(); mEraserPaint = new Paint(); Init_Paint(UserInfo.PaintColor,UserInfo.PaintWidth); Init_Eraser(UserInfo.EraserWidth); WindowManager manager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); width = manager.getDefaultDisplay().getWidth(); height = manager.getDefaultDisplay().getHeight(); mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); mPath = new Path(); mBitmapPaint = new Paint(Paint.DITHER_FLAG); } public PaintView(Context context) { super(context); this.context = context; mPaint = new Paint(); mEraserPaint = new Paint(); Init_Paint(UserInfo.PaintColor, UserInfo.PaintWidth); Init_Eraser(UserInfo.EraserWidth); WindowManager manager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); width = manager.getDefaultDisplay().getWidth(); height = manager.getDefaultDisplay().getHeight(); // mBitmap = BitmapFactory.decodeResource(context.getResources(), mBitmapBackGround). // copy(Bitmap.Config.ARGB_8888, false); // mBitmap = Bitmap.createScaledBitmap(mBitmap,width,height,false); mBitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mBitmap); mPath = new Path(); mBitmapPaint = new Paint(Paint.DITHER_FLAG); } // init paint private void Init_Paint(int color ,int width){ mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setColor(color); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(width); } // init eraser private void Init_Eraser(int width){ mEraserPaint.setAntiAlias(true); mEraserPaint.setDither(true); mEraserPaint.setColor(0xFF000000); mEraserPaint.setStrokeWidth(width); mEraserPaint.setStyle(Paint.Style.STROKE); mEraserPaint.setStrokeJoin(Paint.Join.ROUND); mEraserPaint.setStrokeCap(Paint.Cap.SQUARE); // The most important mEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); } // while size is changed @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if(IsPaint) Init_Paint(UserInfo.PaintColor, UserInfo.PaintWidth); else Init_Eraser(UserInfo.EraserWidth); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); if(IsPaint) canvas.drawPath(mPath, mPaint); else canvas.drawPath(mPath, mEraserPaint); } private void Touch_Down(float x, float y) { mPath.reset(); mPath.moveTo(x, y); mX = x; mY = y; if(IsRecordPath) { listener.addNodeToPath(x, y, MotionEvent.ACTION_DOWN, IsPaint); } } 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; if(IsRecordPath) { listener.addNodeToPath(x, y, MotionEvent.ACTION_MOVE, IsPaint); } } } private void Touch_Up(Paint paint){ mPath.lineTo(mX, mY); mCanvas.drawPath(mPath, paint); mPath.reset(); if(IsRecordPath) { listener.addNodeToPath(mX, mY, MotionEvent.ACTION_UP, IsPaint); } } public void setColor(int color) { showCustomToast("已选择颜色" + colorToHexString(color)); mPaint.setColor(color); } public void setPenWidth(int width) { showCustomToast("设定笔粗为:" + width); mPaint.setStrokeWidth(width); } public void save(){ mCanvas.save(); } public void setIsPaint(boolean isPaint) { IsPaint = isPaint; } public void setOnPathListener(OnPathListener listener) { this.listener = listener; } public void setmEraserPaint(int width){ showCustomToast("设定橡皮粗为:"+width); mEraserPaint.setStrokeWidth(width); } public void setIsRecordPath(boolean isRecordPath,PathNode pathNode) { this.pathNode = pathNode; IsRecordPath = isRecordPath; } public void setIsRecordPath(boolean isRecordPath) { IsRecordPath = isRecordPath; } public boolean isShowing() { return IsShowing; } private static String colorToHexString(int color) { return String.format("#%06X", 0xFFFFFFFF & color); } // switch eraser/paint public void Eraser(){ showCustomToast("切换为橡皮"); IsPaint = false; Init_Eraser(UserInfo.EraserWidth); } public void Paint(){ showCustomToast("切换为铅笔"); IsPaint = true; Init_Paint(UserInfo.PaintColor, UserInfo.PaintWidth); } public Paint getmEraserPaint() { return mEraserPaint; } public Paint getmPaint() { return mPaint; } /** * @author lfk_dsk@hotmail.com * clean the canvas * */ public void clean() { mBitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888); mCanvas.setBitmap(mBitmap); try { Message msg = new Message(); msg.obj = PaintView.this; msg.what = INDIVIDE; handler.sendMessage(msg); Thread.sleep(0); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } IsFirstTime = true; } /** * @author lfk_dsk@hotmail.com * @param uri get the uri of a picture * */ public void setmBitmap(Uri uri){ Log.e("图片路径", String.valueOf(uri)); ContentResolver cr = context.getContentResolver(); try { mBitmapInit = BitmapFactory.decodeStream(cr.openInputStream(uri)); drawBitmapToCanvas(mBitmapInit); } catch (FileNotFoundException e) { e.printStackTrace(); } invalidate(); } private void drawBitmapToCanvas(Bitmap bitmap){ if(bitmap.getHeight() > height || bitmap.getWidth() > width){ RectF rectF = new RectF(0,0,width,height); mCanvas.drawBitmap(bitmap, null, rectF, mBitmapPaint); }else { mCanvas.drawBitmap(bitmap, 0, 0, mBitmapPaint); } } /** * @author lfk_dsk@hotmail.com * @param file Pictures' file * */ public void BitmapToPicture(File file){ FileOutputStream fileOutputStream; try { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); Date now = new Date(); File tempfile = new File(file+"/"+formatter.format(now)+".jpg"); fileOutputStream = new FileOutputStream(tempfile); Bitmap mBitmapbg = BitmapFactory.decodeResource(context.getResources(), mBitmapBackGround). copy(Bitmap.Config.ARGB_8888, false); mBitmapbg = Bitmap.createScaledBitmap(mBitmapbg,width,height,false); if(mBitmapInit != null){ mBitmapbg = toConformBitmap(mBitmapbg,mBitmapInit); mBitmapbg = toConformBitmap(mBitmapbg,mBitmap); }else { mBitmapbg = toConformBitmap(mBitmapbg,mBitmap); } mBitmapbg.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream); showCustomToast(tempfile.getName() + "已保存"); } catch (FileNotFoundException e) { e.printStackTrace(); } } public void PathNodeToJson(PathNode pathNode,File file){ ArrayList<PathNode.Node> arrayList = pathNode.getPathList(); String json = "["; for(int i = 0;i < arrayList.size();i++){ PathNode.Node node = arrayList.get(i); json += "{"+"\""+"x"+"\""+":"+node.x+"," + "\""+"y"+"\""+":"+node.y+","+ "\""+"PenColor"+"\""+":"+node.PenColor+","+ "\""+"PenWidth"+"\""+":"+node.PenWidth+","+ "\""+"EraserWidth"+"\""+":"+node.EraserWidth+","+ "\""+"TouchEvent"+"\""+":"+node.TouchEvent+","+ "\""+"IsPaint"+"\""+":"+"\""+node.IsPaint+"\""+","+ "\""+"time"+"\""+":"+node.time+ "},"; } json = json.substring(0,json.length()-1); json += "]"; try { json = enCrypto(json, "lfk_dsk@hotmail.com"); } catch (InvalidKeySpecException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException e) { e.printStackTrace(); } SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); Date now = new Date(); File tempfile = new File(file+"/"+formatter.format(now)+".lfk"); try { FileOutputStream fileOutputStream = new FileOutputStream(tempfile); byte[] bytes = json.getBytes(); fileOutputStream.write(bytes); fileOutputStream.close(); showCustomToast(tempfile.getName() + "已保存"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private Bitmap toConformBitmap(Bitmap background, Bitmap foreground) { if( background == null ) { return null; } int bgWidth = background.getWidth(); int bgHeight = background.getHeight(); Bitmap bitmap = Bitmap.createBitmap(bgWidth, bgHeight, Bitmap.Config.ARGB_8888); Canvas cv = new Canvas(bitmap); cv.drawBitmap(background, 0, 0, null);//在 0,0坐标开始画入bg cv.drawBitmap(foreground, 0, 0, null);//在 0,0坐标开始画入fg ,可以从任意位置画入 cv.save(Canvas.ALL_SAVE_FLAG);//保存 cv.restore();//存储 return bitmap; } public void clearReUnList(){ ReDoNodes.clear(); mBitmapInit = null; } public void JsonToPathNodeToHandle(Uri uri){ Message message = new Message(); message.obj = uri.getPath(); message.what = CHOOSEPATH; handler.sendMessage(message); } /** * @author lfk_dsk@hotmail.com * @param file the file of .lfk * */ private void JsonToPathNode(String file){ String res = ""; ArrayList<PathNode.Node> arrayList = new ArrayList<>(); try { Log.e("绝对路径",file); FileInputStream in = new FileInputStream(file); ByteArrayOutputStream bufferOut = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; for(int i = in.read(buffer, 0, buffer.length); i > 0 ; i = in.read(buffer, 0, buffer.length)) { bufferOut.write(buffer, 0, i); } res = new String(bufferOut.toByteArray(), Charset.forName("utf-8")); } catch (IOException e) { e.printStackTrace(); } try { res = deCrypto(res, "lfk_dsk@hotmail.com"); } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException | InvalidKeySpecException e) { e.printStackTrace(); } try { JSONArray jsonArray = new JSONArray(res); for(int i = 0;i < jsonArray.length();i++){ JSONObject jsonObject = new JSONObject(jsonArray.getString(i)); PathNode.Node node = new PathNode().NewAnode(); // dp node.x = jsonObject.getInt("x"); node.y = jsonObject.getInt("y"); node.TouchEvent = jsonObject.getInt("TouchEvent"); node.PenWidth = jsonObject.getInt("PenWidth"); node.PenColor = jsonObject.getInt("PenColor"); node.EraserWidth = jsonObject.getInt("EraserWidth"); node.IsPaint = jsonObject.getBoolean("IsPaint"); node.time = jsonObject.getLong("time"); arrayList.add(node); } } catch (JSONException e) { e.printStackTrace(); } pathNode.setPathList(arrayList); } public int px2dip(float pxValue) { final float scale = this.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } public int dip2px(float dpValue) { final float scale = this.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } @Override public boolean onTouchEvent(@NonNull MotionEvent event) { if(!isShowing()) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Touch_Down(x, y); invalidate(); break; case MotionEvent.ACTION_MOVE: Touch_Move(x, y); invalidate(); break; case MotionEvent.ACTION_UP: if (IsPaint) { Touch_Up(mPaint); } else { Touch_Up(mEraserPaint); } invalidate(); break; } } return true; } public void preview(ArrayList<PathNode.Node> arrayList) { IsRecordPath = false; PreviewThread previewThread = new PreviewThread(this, arrayList); Thread thread = new Thread(previewThread); thread.start(); } private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case INDIVIDE: ((View) msg.obj).invalidate(); break; case CHOOSEPATH: JsonToPathNode(msg.obj.toString()); break; } super.handleMessage(msg); } }; public void showCustomToast(String toast) { LayoutInflater inflater = LayoutInflater.from(context); View view = inflater.inflate(R.layout.toast_item, (ViewGroup)findViewById(R.id.toast_item)); TextView text = (TextView) view.findViewById(R.id.toast_text); text.setText(toast); Toast tempToast = new Toast(context); tempToast.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER, 0, 0); tempToast.setDuration(Toast.LENGTH_SHORT); tempToast.setView(view); tempToast.show(); } class PreviewThread implements Runnable{ private long time; private ArrayList<PathNode.Node> nodes; private View view; public PreviewThread(View view, ArrayList<PathNode.Node> arrayList) { this.view = view; this.nodes = arrayList; } public void run() { time = 0; IsShowing = true; clean(); if(mBitmapInit != null){ drawBitmapToCanvas(mBitmapInit); } for(int i = 0 ;i < nodes.size();i++) { PathNode.Node node = nodes.get(i); float x = dip2px(node.x); float y = dip2px(node.y); Log.e("pre"+x,"pre"+y); if(i < nodes.size() - 1) { time = nodes.get(i+1).time - node.time; } IsPaint = node.IsPaint; if(node.IsPaint){ UserInfo.PaintColor = node.PenColor; UserInfo.PaintWidth = node.PenWidth; Init_Paint(node.PenColor,node.PenWidth); }else { UserInfo.EraserWidth = node.EraserWidth; Init_Eraser(node.EraserWidth); } switch (node.TouchEvent) { case MotionEvent.ACTION_DOWN: Touch_Down(x,y); break; case MotionEvent.ACTION_MOVE: Touch_Move(x,y); break; case MotionEvent.ACTION_UP: if(node.IsPaint){ Touch_Up(mPaint); }else { Touch_Up(mEraserPaint); } break; } Message msg = new Message(); msg.obj = view; msg.what = INDIVIDE; handler.sendMessage(msg); if(!ReDoOrUnDoFlag) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } } ReDoOrUnDoFlag = false; IsShowing = false; IsRecordPath = true; } } /** * 加密(使用DES算法) * * @param txt * 需要加密的文本 * @param key * 密钥 * @return 成功加密的文本 * @throws InvalidKeySpecException * @throws InvalidKeyException * @throws NoSuchPaddingException * @throws IllegalBlockSizeException * @throws BadPaddingException */ private static String enCrypto(String txt, String key) throws InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { StringBuffer sb = new StringBuffer(); DESKeySpec desKeySpec = new DESKeySpec(key.getBytes()); SecretKeyFactory skeyFactory = null; Cipher cipher = null; try { skeyFactory = SecretKeyFactory.getInstance("DES"); cipher = Cipher.getInstance("DES"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } SecretKey deskey = skeyFactory != null ? skeyFactory.generateSecret(desKeySpec) : null; if (cipher != null) { cipher.init(Cipher.ENCRYPT_MODE, deskey); } byte[] cipherText = cipher != null ? cipher.doFinal(txt.getBytes()) : new byte[0]; for (int n = 0; n < cipherText.length; n++) { String stmp = (java.lang.Integer.toHexString(cipherText[n] & 0XFF)); if (stmp.length() == 1) { sb.append("0" + stmp); } else { sb.append(stmp); } } return sb.toString().toUpperCase(); } /** * 解密(使用DES算法) * * @param txt * 需要解密的文本 * @param key * 密钥 * @return 成功解密的文本 * @throws InvalidKeyException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws IllegalBlockSizeException * @throws BadPaddingException */ private static String deCrypto(String txt, String key) throws InvalidKeyException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { DESKeySpec desKeySpec = new DESKeySpec(key.getBytes()); SecretKeyFactory skeyFactory = null; Cipher cipher = null; try { skeyFactory = SecretKeyFactory.getInstance("DES"); cipher = Cipher.getInstance("DES"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } SecretKey deskey = skeyFactory != null ? skeyFactory.generateSecret(desKeySpec) : null; if (cipher != null) { cipher.init(Cipher.DECRYPT_MODE, deskey); } byte[] btxts = new byte[txt.length() / 2]; for (int i = 0, count = txt.length(); i < count; i += 2) { btxts[i / 2] = (byte) Integer.parseInt(txt.substring(i, i + 2), 16); } return (new String(cipher.doFinal(btxts))); } public void ReDoORUndo(boolean flag){ if(!IsShowing) { ReDoOrUnDoFlag = true; try { if (flag) { Log.e("redo",""); ReDoNodes.add(pathNode.getTheLastNote()); pathNode.deleteTheLastNote(); preview(pathNode.getPathList()); invalidate(); // ReDoOrUnDoFlag = true; // if(!isShowing()) // preview(pathNode.getPathList()); } else { Log.e("undo",""); pathNode.addNode(ReDoNodes.get(ReDoNodes.size() - 1)); ReDoNodes.remove(ReDoNodes.size() - 1); preview(pathNode.getPathList()); // ReDoOrUnDoFlag = true; // if(!isShowing()) // preview(pathNode.getPathList()); } } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); showCustomToast("无法操作=-="); } } } }