Android 白板代码实现

上一篇文章
Android白板方案调研
讲述了Android 白板实现的一些细节和问题整理。本篇主要从代码角度展现Android 白板。

白板的调用:

public class CanvasDemoActivity extends Activity { @Override           protected void onCreate(Bundle savedInstanceState) {               super.onCreate(savedInstanceState);                setContentView(R.layout.activity_map);            FragmentTransaction ts = getFragmentManager().beginTransaction();            ts.add(R.id.fl_main, new WhiteBoardFragment(), "wb").commit();

activity_map.xml 布局如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <FrameLayout         android:id="@+id/fl_main"        android:layout_width="match_parent"        android:layout_height="match_parent"/>LinearLayout>

从中可以看出直接使用了 WhiteBoardFragment, 下面看 WhiteBoardFragment.java

项目结构如下图所示:

Android 白板代码实现_第1张图片

WhiteBoardFragment.java

package com.example.whiteboard.fragment;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_CIRCLE;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_DRAW;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_ERASER;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_LINE;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_RECTANGLE;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_TEXT;import java.io.File;import java.io.FileOutputStream;import android.app.Activity;import android.app.AlertDialog;import android.app.Fragment;import android.content.Context;import android.content.DialogInterface;import android.graphics.Bitmap;import android.graphics.Color;import android.graphics.Point;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.os.AsyncTask;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.Gravity;import android.view.KeyEvent;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.view.WindowManager;import android.view.inputmethod.EditorInfo;import android.view.inputmethod.InputMethodManager;import android.widget.EditText;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.PopupWindow;import android.widget.RadioGroup;import android.widget.RadioGroup.OnCheckedChangeListener;import android.widget.SeekBar;import android.widget.SeekBar.OnSeekBarChangeListener;import android.widget.TextView;import android.widget.Toast;import com.example.test.R;import com.example.whiteboard.SketchView;import com.example.whiteboard.SketchView.OnDrawChangedListener;import com.example.whiteboard.SketchView.TextWindowCallback;import com.example.whiteboard.bean.StrokeRecord;import com.example.whiteboard.utils.CommonUtils;import com.example.whiteboard.utils.ScreenUtils;import com.example.whiteboard.utils.SdCardStatus;import com.example.whiteboard.utils.TimeUtils;import com.example.whiteboard.utils.ViewUtils;public class WhiteBoardFragment extends Fragment implements OnClickListener,OnDrawChangedListener{    static final int COLOR_BLACK = Color.parseColor("#ff000000");    static final int COLOR_RED = Color.parseColor("#ffff4444");    static final int COLOR_GREEN = Color.parseColor("#ff99cc00");    static final int COLOR_ORANGE = Color.parseColor("#ffffbb33");    static final int COLOR_BLUE = Color.parseColor("#ff33b5e5");    private static final float BTN_ALPHA = 0.4f;    final String TAG = getClass().getSimpleName();    public static final String FILE_PATH = SdCardStatus.getDefaulstCacheDirInSdCard() + File.separator + "sketchPhoto";    public static final String SCREENSHOT_PATH = SdCardStatus.getDefaulstCacheDirInSdCard() + File.separator +"screenShot";    Activity activity;//上下文    private SketchView mSketchView; // 画布    private ImageView btn_pen; // 画笔    private ImageView btn_eraser;    private ImageView btn_undo;    private ImageView btn_redo;    private ImageView btn_pic;    private ImageView btn_background;    private ImageView btn_drag;    private ImageView btn_save;    private ImageView btn_empty;    private ImageView btn_screenshot;    private int penBaseSize;    private int textOffX,textOffY;    private EditText saveET; // 保存文件的EditText    private EditText textET; // 输入文字的EditText    private AlertDialog saveDialog,dialog, screenshotDialog;    private int mPenType = STROKE_TYPE_DRAW; // 画笔类型    private int mPenColor = COLOR_BLACK; // 画笔颜色    private PopupWindow mPenPopupWindow; // 画笔弹窗    private PopupWindow mEraserPopupWindow;// 橡皮弹窗    private PopupWindow mTextPopupWindow;// 文字输入    private View mPenLayoutView; // 画笔布局    private View mEraserLayoutView;// 橡皮布局    private View mTextLayoutView; // 文字输入框    private ImageView mPenSizeCircle, mPenAlphaCircle;    private SeekBar mPenSizeSeekBar, mPenAlphaSeekBar;    private RadioGroup mPenTypeGroup, mPenColorGroup;    private ImageView mEraserSizeCircle;    private SeekBar mEraserSizeSeekBar;    private static boolean isScreenShoting = false; // 是否正在截屏,保持两次截屏时间间隔大于3秒    private static Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            isScreenShoting = false;        }    };    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        activity = getActivity();//初始化上下文    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        View rootView = inflater.inflate(R.layout.fragment_white_board, container,  false);        initView(rootView, inflater);        initDrawParams();//初始化绘画参数        initPopupWindow();//初始化弹窗        initSaveDialog();//初始化保存文件对话框        return rootView;    }    private void initDrawParams() {        //画笔宽度缩放基准参数        Drawable circleDrawable = getResources().getDrawable(R.drawable.circle);        if (circleDrawable != null){            penBaseSize = circleDrawable.getIntrinsicWidth();        }    }    private void initPopupWindow() {        initPenPopupWindow();        initEraserPopupWindow();        initTextPop();    }    private void initSaveDialog(){        saveET = new EditText(activity);        saveET.setHint("新文件名");        saveET.setGravity(Gravity.CENTER);        saveET.setSingleLine();        saveET.setInputType(EditorInfo.TYPE_CLASS_TEXT);        saveET.setImeOptions(EditorInfo.IME_ACTION_DONE);        saveET.setSelectAllOnFocus(true);        saveET.setOnEditorActionListener(new TextView.OnEditorActionListener() {            @Override            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {                if (actionId == EditorInfo.IME_ACTION_DONE) {                    ScreenUtils.hideInput(saveDialog.getCurrentFocus());                    saveDialog.dismiss();                    String input = saveET.getText().toString();                    Log.e("wmb", "--onEditorAction--input:"+input);                    saveInUI(input + ".png");                }                return true;            }        });        saveDialog = new AlertDialog.Builder(activity)                .setTitle("请输入保存文件名")                .setMessage("")                .setView(saveET)                .setPositiveButton("确定", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        ScreenUtils.hideInput(saveDialog.getCurrentFocus());                        String input = saveET.getText().toString();                        Log.e("wmb", "--onClick--input:"+input);                        saveInUI(input + ".png");                    }                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        ScreenUtils.hideInput(saveDialog.getCurrentFocus());                    }                })                .setCancelable(false)                .create();    }    /**     * 初始化橡皮弹窗     */    private void initEraserPopupWindow() {        mEraserPopupWindow = new PopupWindow(activity);        mEraserPopupWindow.setContentView(mEraserLayoutView);        mEraserPopupWindow.setWidth(550);        mEraserPopupWindow.setHeight(240);        mEraserPopupWindow.setBackgroundDrawable(new BitmapDrawable());        mEraserPopupWindow.setOutsideTouchable(true);        mEraserSizeSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {            @Override            public void onStopTrackingTouch(SeekBar arg0) {            }            @Override            public void onStartTrackingTouch(SeekBar arg0) {            }            @Override            public void onProgressChanged(SeekBar arg0, int progress, boolean arg2) {                setSeekBarProgress(progress, STROKE_TYPE_ERASER);            }        });        mEraserSizeSeekBar.setProgress(SketchView.DEFAULT_ERASER_SIZE);    }    /**     * 初始化画笔弹窗     */    private void initPenPopupWindow() {        mPenPopupWindow = new PopupWindow(activity);        mPenPopupWindow.setContentView(mPenLayoutView);        mPenPopupWindow.setWidth(550);        mPenPopupWindow.setHeight(650);        mPenPopupWindow.setBackgroundDrawable(new BitmapDrawable());        mPenPopupWindow.setOutsideTouchable(true);        // 画笔类型        mPenTypeGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {            @Override            public void onCheckedChanged(RadioGroup group, int checkedId) {                int resId = R.drawable.stroke_type_rbtn_draw_checked;                switch (checkedId) {                case R.id.type_pen:                    mPenType = STROKE_TYPE_DRAW;                    break;                case R.id.type_line:                    mPenType = STROKE_TYPE_LINE;                    resId = R.drawable.stroke_type_rbtn_line_checked;                    break;                case R.id.type_circle:                    mPenType = STROKE_TYPE_CIRCLE;                    resId = R.drawable.stroke_type_rbtn_circle_checked;                    break;                case R.id.type_rectangle:                    mPenType = STROKE_TYPE_RECTANGLE;                    resId = R.drawable.stroke_type_rbtn_rectangle_checked;                    break;                case R.id.type_text:                    mPenType = STROKE_TYPE_TEXT;                    resId = R.drawable.stroke_type_rbtn_text_checked;                    break;                default:                    break;                }                btn_pen.setImageResource(resId);                mSketchView.setStrokeType(mPenType);                mPenPopupWindow.dismiss();            }        });        // 画笔颜色        mPenColorGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {            @Override            public void onCheckedChanged(RadioGroup group, int checkedId) {                switch (checkedId) {                case R.id.color_black:                    mPenColor = COLOR_BLACK;                    break;                case R.id.color_red:                    mPenColor = COLOR_RED;                    break;                case R.id.color_green:                    mPenColor = COLOR_GREEN;                    break;                case R.id.color_orange:                    mPenColor = COLOR_ORANGE;                    break;                case R.id.color_blue:                    mPenColor = COLOR_BLUE;                    break;                default:                    break;                }                mSketchView.setStrokeColor(mPenColor);            }        });        // 画笔大小        mPenSizeSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {            @Override            public void onStopTrackingTouch(SeekBar arg0) {            }            @Override            public void onStartTrackingTouch(SeekBar arg0) {            }            @Override            public void onProgressChanged(SeekBar arg0, int progress, boolean arg2) {                setSeekBarProgress(progress, STROKE_TYPE_DRAW);            }        });        mPenSizeSeekBar.setProgress(SketchView.DEFAULT_STROKE_SIZE);        // 画笔透明度        mPenAlphaSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {            @Override            public void onStopTrackingTouch(SeekBar arg0) {            }            @Override            public void onStartTrackingTouch(SeekBar arg0) {            }            @Override            public void onProgressChanged(SeekBar arg0, int progress, boolean arg2) {                int alpha = progress * 255 / 100; // 百分比转换为透明度                mSketchView.setStrokeAlpha(alpha);                mPenAlphaCircle.setAlpha(alpha);            }        });        mPenAlphaSeekBar.setProgress(SketchView.DEFAULT_STROKE_ALPHA);    }    // 初始化文字输入    private void initTextPop() {        mTextPopupWindow = new PopupWindow(activity);        mTextPopupWindow.setContentView(mTextLayoutView);        mTextPopupWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);        mTextPopupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);        mTextPopupWindow.setFocusable(true);        mTextPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);        mTextPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);//        mTextPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {//            @Override//            public void onDismiss() {//                if (!textET.getText().toString().equals("")) {//                    StrokeRecord record = new StrokeRecord(mPenType);//                    record.text = textET.getText().toString();//                }//            }//        });    }    protected void setSeekBarProgress(int progress, int strokeTypeDraw) {        int realProgress = progress > 1 ? progress : 1;        int newSize = Math.round(penBaseSize * realProgress / 100); // 百分比转换为大小        int offset = Math.round((penBaseSize - newSize) / 2);        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(newSize, newSize);        lp.setMargins(offset, offset, offset, offset);        if(strokeTypeDraw == STROKE_TYPE_DRAW){            mPenSizeCircle.setLayoutParams(lp);        }else{            // 橡皮            mEraserSizeCircle.setLayoutParams(lp);        }        mSketchView.setSize(newSize, strokeTypeDraw);    }    private void initView(View root, LayoutInflater inflater){        mSketchView = ViewUtils.findViewById(root, R.id.id_sketch_view);        btn_pen = ViewUtils.findViewById(root, R.id.id_pen);        btn_eraser = ViewUtils.findViewById(root, R.id.id_eraser);        btn_undo = ViewUtils.findViewById(root, R.id.id_undo);        btn_redo = ViewUtils.findViewById(root, R.id.id_redo);        btn_pic = ViewUtils.findViewById(root, R.id.id_pic);        btn_background = ViewUtils.findViewById(root, R.id.id_bacground);        btn_drag = ViewUtils.findViewById(root, R.id.id_drag);        btn_save = ViewUtils.findViewById(root, R.id.id_save);        btn_empty = ViewUtils.findViewById(root, R.id.id_empty);        btn_screenshot = ViewUtils.findViewById(root, R.id.id_screenshot);        btn_pen.setOnClickListener(this);        btn_eraser.setOnClickListener(this);        btn_undo.setOnClickListener(this);        btn_redo.setOnClickListener(this);        btn_pic.setOnClickListener(this);        btn_background.setOnClickListener(this);        btn_drag.setOnClickListener(this);        btn_save.setOnClickListener(this);        btn_empty.setOnClickListener(this);        btn_screenshot.setOnClickListener(this);        mSketchView.setOnDrawChangedListener(this); // 设置绘画监听        mSketchView.setTextWindowCallback(new TextWindowCallback() {            @Override            public void onText(View view, StrokeRecord record) {                textOffX = record.textOffX;                textOffY = record.textOffY;                showTextPopupWindow(view, record);            }        });        // 初始化画笔弹窗views        mPenLayoutView = inflater.inflate(R.layout.popup_pen, null);        mPenSizeCircle = ViewUtils.findViewById(mPenLayoutView, R.id.pen_size_circle);        mPenAlphaCircle = ViewUtils.findViewById(mPenLayoutView, R.id.pen_alpha_circle);        mPenSizeSeekBar = ViewUtils.findViewById(mPenLayoutView, R.id.pen_size_seek_bar);        mPenAlphaSeekBar = ViewUtils.findViewById(mPenLayoutView, R.id.pen_alpha_seek_bar);        mPenTypeGroup = ViewUtils.findViewById(mPenLayoutView, R.id.pen_type_radio_group);        mPenColorGroup = ViewUtils.findViewById(mPenLayoutView, R.id.pen_color_radio_group);        //初始化橡皮弹窗views        mEraserLayoutView = inflater.inflate(R.layout.popup_eraser, null);        mEraserSizeCircle = ViewUtils.findViewById(mEraserLayoutView, R.id.eraser_size_circle);        mEraserSizeSeekBar = ViewUtils.findViewById(mEraserLayoutView, R.id.eraser_size_seek_bar);        //初始化文字输入        mTextLayoutView = inflater.inflate(R.layout.popup_text, null);        textET = ViewUtils.findViewById(mTextLayoutView, R.id.text_pupwindow_et);        btn_undo.setAlpha(0.4f);        btn_redo.setAlpha(0.4f);        showBtn(btn_pen);    }    @Override    public void onClick(View view) {        switch (view.getId()) {        case R.id.id_pen:            if(mSketchView.getEditMode() == SketchView.EDIT_STROKE && mSketchView.getStrokeType() != STROKE_TYPE_ERASER){                showPopupWindow(view, STROKE_TYPE_DRAW);            }else{                int checkedId = mPenTypeGroup.getCheckedRadioButtonId();                switch (checkedId) {                case R.id.type_pen:                    mPenType = STROKE_TYPE_DRAW;                    break;                case R.id.type_line:                    mPenType = STROKE_TYPE_LINE;                    break;                case R.id.type_circle:                    mPenType = STROKE_TYPE_CIRCLE;                    break;                case R.id.type_rectangle:                    mPenType = STROKE_TYPE_RECTANGLE;                    break;                case R.id.type_text:                    mPenType = STROKE_TYPE_TEXT;                    break;                default:                    break;                }                mSketchView.setStrokeType(mPenType);            }            mSketchView.setEditMode(SketchView.EDIT_STROKE);            showBtn(btn_pen);            break;        case R.id.id_eraser:            if(mSketchView.getEditMode() == SketchView.EDIT_STROKE && mSketchView.getStrokeType() == STROKE_TYPE_ERASER){                showPopupWindow(view, STROKE_TYPE_ERASER);            }else{                mSketchView.setStrokeType(STROKE_TYPE_ERASER);            }            mSketchView.setEditMode(SketchView.EDIT_STROKE);            showBtn(btn_eraser);            break;        case R.id.id_undo:            mSketchView.undo();            break;        case R.id.id_redo:            mSketchView.redo();            break;        case R.id.id_pic:            mSketchView.addPhotoByPath("persion.jpg");            mSketchView.setEditMode(SketchView.EDIT_PHOTO);            showBtn(btn_drag);            break;        case R.id.id_bacground:            mSketchView.setBackgroundByPath("najing.jpeg");            break;        case R.id.id_drag:            mSketchView.setEditMode(SketchView.EDIT_PHOTO);            showBtn(btn_drag);            break;        case R.id.id_save:            if(mSketchView.getRecordCount() == 0){                CommonUtils.showToast("您还没有绘图呢~", 3000);            }else{                showSaveDialog();            }            break;        case R.id.id_empty:            askforErase();            break;        case R.id.id_screenshot:            if(!isScreenShoting){                isScreenShoting = true;                new screenShotTast().execute(TimeUtils.getNowTimeString()+".png");                mHandler.sendEmptyMessageDelayed(0, 3000);            }else{                CommonUtils.showToast("正在截屏,请降低操作速度", 3000);            }            break;        default:            break;        }    }    /**     * 截屏方法     */    private File screenShot(String filepath, String fileName){        if(null == activity)            return null;        Point screenPoint = ScreenUtils.getScreenSize(activity);        if(null == screenPoint)            return null;        int statusBarAndTitleHeight = ScreenUtils.getStatusBarAndTitleHeight(activity);        View view = activity.getWindow().getDecorView();        view.setDrawingCacheEnabled(true);        Bitmap bitmapTemp = view.getDrawingCache();        Bitmap bmp = Bitmap.createBitmap(bitmapTemp, 0, statusBarAndTitleHeight, screenPoint.x, screenPoint.y - statusBarAndTitleHeight);        view.destroyDrawingCache();        try {            File dir = new File(filepath);            if(!dir.exists()){                boolean mk = dir.mkdirs();            }            File f = new File(dir, fileName);            if(!f.exists()){                f.createNewFile();            }else{                f.delete();            }            FileOutputStream out = new FileOutputStream(f);            bmp.compress(Bitmap.CompressFormat.PNG, 90, out);            out.flush();            out.close();            bitmapTemp.recycle();            bmp.recycle();            bitmapTemp = null;            bmp = null;            return f;        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    private void showTextPopupWindow(View anchor, final StrokeRecord record) {        textET.requestFocus();        mTextPopupWindow.showAsDropDown(anchor, record.textOffX, record.textOffY - mSketchView.getHeight());        mTextPopupWindow.setSoftInputMode(PopupWindow.INPUT_METHOD_NEEDED);        InputMethodManager imm = (InputMethodManager) activity                .getSystemService(Context.INPUT_METHOD_SERVICE);        imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);//        ScreenUtils.showInput(textET);        mTextPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {            @Override            public void onDismiss() {                if (!textET.getText().toString().equals("")) {                    record.text = textET.getText().toString();                    record.textPaint.setTextSize(textET.getTextSize());                    record.textWidth = textET.getMaxWidth();                    mSketchView.addStrokeRecord(record);                }            }        });    }    private void showSaveDialog() {        saveDialog.show();        saveET.setText(TimeUtils.getNowTimeString());        saveET.selectAll();        ScreenUtils.showInput(mSketchView);    }    private void askforErase() {        new AlertDialog.Builder(activity)                .setMessage("擦除手绘?")                .setPositiveButton("确定", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        mSketchView.erase();                    }                })                .setNegativeButton("取消", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface arg0, int arg1) {                    }                })                .create()                .show();    }    private void showBtn(ImageView iv) {        btn_eraser.setAlpha(BTN_ALPHA);        btn_pen.setAlpha(BTN_ALPHA);        btn_drag.setAlpha(BTN_ALPHA);        iv.setAlpha(1f);    }    private void showPopupWindow(View anchor, int drawMode){        if(drawMode == STROKE_TYPE_DRAW){            mPenPopupWindow.showAsDropDown(anchor, 0, 0);        }else if(drawMode == STROKE_TYPE_ERASER){            mEraserPopupWindow.showAsDropDown(anchor, 0, 0);        }    }    private void saveInUI(final String imgName) {        new saveToFileTask().execute(imgName);    }    public File saveInOI(String filePath, String imgName) {        return saveInOI(filePath, imgName, 80);    }    public File saveInOI(String filePath, String imgName, int compress) {        Log.e("wmb", "--saveInOI--filePath:" + filePath + "--imgName:"                + imgName + "--compress:" + compress);        if (!imgName.contains(".png")) {            imgName += ".png";        }        Log.e(TAG, "saveInOI: " + System.currentTimeMillis());        Bitmap newBM = mSketchView.getResultBitmap();        Log.e(TAG, "saveInOI: " + System.currentTimeMillis());        try {            File dir = new File(filePath);            if (!dir.exists()) {                boolean mk = dir.mkdirs();                Log.e("wmb", "--saveInOI--mk:" + mk);            }            File f = new File(filePath, imgName);            if (!f.exists()) {                boolean cr = f.createNewFile();                Log.e("wmb", "--saveInOI--cr:" + cr);            } else {                f.delete();            }            FileOutputStream out = new FileOutputStream(f);            Log.e(TAG, "saveInOI: " + System.currentTimeMillis());            if (compress >= 1 && compress <= 100)                newBM.compress(Bitmap.CompressFormat.PNG, compress, out);            else {                newBM.compress(Bitmap.CompressFormat.PNG, 80, out);            }            Log.e(TAG, "saveInOI: " + System.currentTimeMillis());            out.close();            newBM.recycle();            newBM = null;            return f;        } catch (Exception e) {            Log.e("wmb", "--e:" + e.getStackTrace());            return null;        }    }    class screenShotTast extends AsyncTask {        @Override        protected File doInBackground(String... screenShotName) {            Log.e("wmb", "screenShotTast--doInBackground");            return screenShot(SCREENSHOT_PATH, screenShotName[0]);        }        @Override        protected void onPostExecute(File result) {            super.onPostExecute(result);            if(null != result && result.exists()){                CommonUtils.showToast("截图成功,正在设置为画板背景...", 1000);//              mSketchView.setBackgroundByPath(result.getAbsolutePath());                mSketchView.addPhotoByPath(result.getAbsolutePath());                mSketchView.setEditMode(SketchView.EDIT_PHOTO);                showBtn(btn_drag);            }else{                CommonUtils.showToast("截图失败,请稍后重试", 3000);            }            screenshotDialog.dismiss();        }        @Override        protected void onPreExecute() {            super.onPreExecute();            Log.e("wmb", "screenShotTast--onPreExecute");            screenshotDialog = new AlertDialog.Builder(activity).setTitle("保存截图")                    .setMessage("截图保存中...").show();        }    }    class saveToFileTask extends AsyncTask {        @Override        protected void onPreExecute() {            super.onPreExecute();            Log.e("wmb", "--onPreExecute");            dialog = new AlertDialog.Builder(activity)                    .setTitle("保存画板")                    .setMessage("保存中...")                    .show();        }        @Override        protected File doInBackground(String... photoName) {             Log.e("wmb", "--doInBackground");            return saveInOI(FILE_PATH, photoName[0]);        }        @Override        protected void onPostExecute(File file) {            super.onPostExecute(file);            if (null != file && file.exists())                Toast.makeText(activity, file.getAbsolutePath(), Toast.LENGTH_SHORT).show();            else                Toast.makeText(activity, "保存失败!", Toast.LENGTH_SHORT).show();            dialog.dismiss();        }    }    @Override    public void onDrawChanged() {        Log.e("wmb", "--onDrawChanged-StrokeRecordCount:"+mSketchView.getStrokeRecordCount()+"--RedoCount:"+mSketchView.getRedoCount());        if(mSketchView.getStrokeRecordCount() > 0){            btn_undo.setAlpha(1f);        }else{            btn_undo.setAlpha(0.4f);        }        if(mSketchView.getRedoCount() > 0){            btn_redo.setAlpha(1f);        }else{            btn_redo.setAlpha(0.4f);        }    }}

StrokeRecord.java

package com.example.whiteboard.bean;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PointF;import android.graphics.RectF;import android.text.TextPaint;/** * stroke record * @author WMB * */public class StrokeRecord {    public static final int STROKE_TYPE_ERASER = 1;    public static final int STROKE_TYPE_DRAW = 2;    public static final int STROKE_TYPE_LINE = 3;    public static final int STROKE_TYPE_CIRCLE = 4;    public static final int STROKE_TYPE_RECTANGLE = 5;    public static final int STROKE_TYPE_TEXT = 6;    public int type;//记录类型    public Paint paint;//笔类    public Path path;//画笔路径数据    public PointF[] linePoints; //线数据    public RectF rect; //圆、矩形区域    public String text;//文字    public TextPaint textPaint;//笔类    public int textOffX;    public int textOffY;    public int textWidth;//文字位置    public StrokeRecord(int type) {        this.type = type;    }}

PhotoRecord.java

package com.example.whiteboard.bean;import android.graphics.Bitmap;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PointF;import android.graphics.RectF;import android.text.TextPaint;/** * photo record * @author WMB * */public class PhotoRecord {    public Bitmap bitmap;//图形    public Matrix matrix;//图形    public RectF photoRectSrc = new RectF();    public float scaleMax = 3;}

SketchData.java

package com.example.whiteboard.bean;import android.graphics.Bitmap;import java.io.File;import java.util.ArrayList;import java.util.List;/** * sketch data */public class SketchData {    public List photoRecordList;    public List strokeRecordList;    public List strokeRedoList;    public Bitmap thumbnailBM;//缩略图文件    public Bitmap backgroundBM;    public SketchData() {        strokeRecordList = new ArrayList<>();        photoRecordList = new ArrayList<>();        strokeRedoList = new ArrayList<>();        backgroundBM = null;        thumbnailBM = null;    }}

DrawView.java

package com.example.whiteboard;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.Rect;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;public class DrawView extends View {    float currentX, currentY;    Paint p = new Paint();    Bitmap bp;    public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public DrawView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public DrawView(Context context) {        super(context);        bp = Bitmap.createBitmap(2000, 1000, Config.ARGB_4444);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        Log.e("wmb", "--onDraw--canvas:"+canvas.toString());        p.setColor(Color.RED);        canvas.drawCircle(currentX, currentY, 30, p);        canvas.saveLayer(0, 0, 2000, 1000, p, Canvas.ALL_SAVE_FLAG);//      p.setColor(Color.GREEN);        bp = Bitmap.createBitmap(2000, 1000, Config.ARGB_4444);//      Canvas tmpcs = new Canvas(bp);//      p.setStyle(Style.STROKE);//      tmpcs.drawRect(new Rect(2, 2, 2000, 900), p);        tmpcs.drawColor(Color.GRAY);//      tmpcs.drawCircle(currentX, currentY, 30, p);//      canvas.drawBitmap(bp, 0, 0, null);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        currentX = event.getX();        currentY = event.getY();        Log.e("wmb", "--onTouchEvent--currentX:"+currentX+"--currentY:"+currentY);        this.invalidate();        return true;    }}

SketchView.java

package com.example.whiteboard;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_CIRCLE;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_DRAW;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_ERASER;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_LINE;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_RECTANGLE;import static com.example.whiteboard.bean.StrokeRecord.STROKE_TYPE_TEXT;import java.io.File;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.PaintFlagsDrawFilter;import android.graphics.Path;import android.graphics.PointF;import android.graphics.PorterDuff.Mode;import android.graphics.PorterDuffXfermode;import android.graphics.Rect;import android.graphics.RectF;import android.os.Environment;import android.support.annotation.NonNull;import android.text.Layout;import android.text.StaticLayout;import android.text.TextPaint;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.SurfaceHolder;import android.view.View;import android.view.View.OnTouchListener;import android.widget.Toast;import com.example.test.R;import com.example.whiteboard.bean.PhotoRecord;import com.example.whiteboard.bean.SketchData;import com.example.whiteboard.bean.StrokeRecord;import com.example.whiteboard.utils.BitmapUtils;import com.example.whiteboard.utils.ScreenUtils;/** * 画板整体布局 * */public class SketchView extends View implements OnTouchListener/*,Runnable,SurfaceHolder.Callback*/ {    final String TAG = getClass().getSimpleName();    public interface TextWindowCallback {        void onText(View view, StrokeRecord record);    }    public void setTextWindowCallback(TextWindowCallback textWindowCallback) {        this.textWindowCallback = textWindowCallback;    }    private TextWindowCallback textWindowCallback;    private static final float TOUCH_TOLERANCE = 4;    public static final int EDIT_STROKE = 1;    public static final int EDIT_PHOTO = 2;    private static final int ACTION_NONE = 0;    private static final int ACTION_DRAG = 1;    private static final int ACTION_SCALE = 2;    private static final int ACTION_ROTATE = 3;    public static final int DEFAULT_STROKE_SIZE = 7;    public static final int DEFAULT_STROKE_ALPHA = 100;    public static final int DEFAULT_ERASER_SIZE = 50;    private float strokeSize = DEFAULT_STROKE_SIZE;    private int strokeRealColor = Color.BLACK;//画笔实际颜色    private int strokeColor = Color.BLACK;//画笔颜色    private int strokeAlpha = 255;//画笔透明度    private float eraserSize = DEFAULT_ERASER_SIZE;    Paint boardPaint;    Bitmap mirrorMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.mark_copy);    Bitmap deleteMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.mark_delete);    Bitmap rotateMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.mark_rotate);    Bitmap resetMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.mark_reset);    //    Bitmap rotateMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.test);    RectF markerCopyRect = new RectF(0, 0, mirrorMarkBM.getWidth(), mirrorMarkBM.getHeight());//镜像标记边界    RectF markerDeleteRect = new RectF(0, 0, deleteMarkBM.getWidth(), deleteMarkBM.getHeight());//删除标记边界    RectF markerRotateRect = new RectF(0, 0, rotateMarkBM.getWidth(), rotateMarkBM.getHeight());//旋转标记边界    RectF markerResetRect = new RectF(0, 0, resetMarkBM.getWidth(), resetMarkBM.getHeight());//旋转标记边界    private Path strokePath;    private Paint strokePaint;//    private Paint erasePaint;    private float downX, downY, preX, preY, curX, curY;    private int mWidth, mHeight;    private Bitmap   mEraseMaskBitmap = null;// 橡皮擦    private Canvas mEraseCanvas;    SketchData curSketchData = new SketchData();    private Context mContext;    Rect backgroundSrcRect = new Rect();    Rect backgroundDstRect = new Rect();    StrokeRecord curStrokeRecord;    PhotoRecord curPhotoRecord;    int actionMode;    private int editMode = EDIT_STROKE;    private static float SCALE_MAX = 4.0f;    private static float SCALE_MIN = 0.2f;    private static float SCALE_MIN_LEN;    private boolean isFirstIn = true;    private int mScreenWidth = 720,mScreenHeight = 1134;    float simpleScale = 0.5f;//图片载入的缩放倍数    /**     * 缩放手势     */    private ScaleGestureDetector mScaleGestureDetector = null;    public void setStrokeType(int strokeType) {        this.strokeType = strokeType;    }    public int getStrokeType() {        return strokeType;    }    private int strokeType = StrokeRecord.STROKE_TYPE_DRAW;    private OnDrawChangedListener onDrawChangedListener;    public void setSketchData(SketchData sketchData) {        this.curSketchData = sketchData;        curPhotoRecord = null;    }    public void updateSketchData(SketchData sketchData) {        if (curSketchData != null)            curSketchData.thumbnailBM = getThumbnailResultBitmap();//更新数据前先保存上一份数据的缩略图        setSketchData(sketchData);    }    public SketchView(Context context, AttributeSet attr) {        super(context, attr);        this.mContext = context;        setFocusable(true);        initParams(context);        if (isFocusable()) {            this.setOnTouchListener(this);            mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.OnScaleGestureListener() {                @Override                public boolean onScale(ScaleGestureDetector detector) {                    onScaleAction(detector);                    return true;                }                @Override                public boolean onScaleBegin(ScaleGestureDetector detector) {                    return true;                }                @Override                public void onScaleEnd(ScaleGestureDetector detector) {                }            });        }        invalidate();    }    //初始化    private void initBmpMask(Canvas canvas) {        mScreenWidth = getWidth();        mScreenHeight = getHeight();        Log.e("iwmb", "--mScreenWidth:"+mScreenWidth+"--mScreenHeight:"+mScreenHeight);    }    private void initParams(Context context) {        strokePaint = new Paint();        strokePaint.setAntiAlias(true);        strokePaint.setDither(true);        strokePaint.setColor(strokeRealColor);        strokePaint.setStyle(Paint.Style.STROKE);        strokePaint.setStrokeJoin(Paint.Join.ROUND);        strokePaint.setStrokeCap(Paint.Cap.ROUND);        strokePaint.setStrokeWidth(strokeSize);//        erasePaint = new Paint();//        erasePaint.setAntiAlias(true);//        erasePaint.setDither(true);//        erasePaint.setColor(0xFF000000);//        erasePaint.setStyle(Paint.Style.STROKE);//        erasePaint.setStrokeJoin(Paint.Join.ROUND);//        erasePaint.setStrokeCap(Paint.Cap.ROUND);//        erasePaint.setStrokeWidth(200);//        erasePaint.setXfermode(new PorterDuffXfermode(Mode.DST_OUT));        boardPaint = new Paint();        boardPaint.setColor(Color.GREEN);        boardPaint.setStrokeWidth(ScreenUtils.dip2px(mContext, 0.8f));        boardPaint.setStyle(Paint.Style.STROKE);        SCALE_MIN_LEN = ScreenUtils.dip2px(context, 20);    }    public void setStrokeAlpha(int mAlpha) {        this.strokeAlpha = mAlpha;        calculColor();        strokePaint.setStrokeWidth(strokeSize);    }    public void setStrokeColor(int color) {        strokeColor = color;        calculColor();        strokePaint.setColor(strokeRealColor);    }    private void calculColor() {        strokeRealColor = Color.argb(strokeAlpha, Color.red(strokeColor), Color.green(strokeColor), Color.blue(strokeColor));    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mWidth = MeasureSpec.getSize(widthMeasureSpec);        mHeight = MeasureSpec.getSize(heightMeasureSpec);        setMeasuredDimension(mWidth, mHeight);    }    @Override    public boolean onTouch(View arg0, MotionEvent event) {        curX = event.getX();        curY = event.getY();        switch (event.getAction() & MotionEvent.ACTION_MASK) {            case MotionEvent.ACTION_POINTER_DOWN:                float downDistance = spacing(event);                Log.e("wmb", "--onTouch--"+event.getAction()+"--space:"+downDistance);                if (actionMode == ACTION_DRAG && downDistance > 10)//防止误触                    actionMode = ACTION_SCALE;                break;            case MotionEvent.ACTION_DOWN:                Log.e("wmb", "--onTouch--action down");                touch_down(event);                invalidate();                break;            case MotionEvent.ACTION_MOVE:                Log.e("wmb", "--onTouch--action move");                touch_move(event);                invalidate();                break;            case MotionEvent.ACTION_UP:                Log.e("wmb", "--onTouch--action up");                touch_up();                invalidate();                break;        }        preX = curX;        preY = curY;        return true;    }    private float spacing(MotionEvent event) {        float x = event.getX(0) - event.getX(1);        float y = event.getY(0) - event.getY(1);        return (float) Math.sqrt(x * x + y * y);    }    @Override    protected void onDraw(Canvas canvas) {        if(isFirstIn){            initBmpMask(canvas);            Log.e("iwmb", "--drawRecord isFristIN");            isFirstIn = false;        }        drawBackground(canvas);        drawRecord(canvas);//        if (onDrawChangedListener != null)//            onDrawChangedListener.onDrawChanged();    }    private void drawBackground(Canvas canvas) {        if (curSketchData.backgroundBM != null) {            //缩放图片,使之能够在屏幕中完整显示(大图压缩、小图放大)            Matrix matrix = new Matrix();            float wScale = (float) canvas.getWidth() / curSketchData.backgroundBM.getWidth();            float hScale = (float) canvas.getHeight() / curSketchData.backgroundBM.getHeight();            matrix.postScale(wScale, hScale);            canvas.drawBitmap(curSketchData.backgroundBM, matrix, null);            Log.e("iwmb", "--canvas.getWidth():"+canvas.getWidth()+"--canvas.getHeight():"+canvas.getHeight());            Log.d(TAG, "drawBackground:src= " + backgroundSrcRect.toString() + ";dst=" + backgroundDstRect.toString());        } else {            canvas.drawColor(Color.WHITE);        }    }    private void drawRecord(Canvas canvas) {        drawRecord(canvas, true);    }    private void drawRecord(Canvas canvas, boolean isDrawBoard) {        mEraseMaskBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.ARGB_8888);        mEraseCanvas = new Canvas(mEraseMaskBitmap);        mEraseCanvas.drawColor(Color.TRANSPARENT);        if (curSketchData != null) {            for (PhotoRecord record : curSketchData.photoRecordList) {                if (record != null)                    canvas.drawBitmap(record.bitmap, record.matrix, null);            }            if (isDrawBoard && editMode == EDIT_PHOTO && curPhotoRecord != null) {                SCALE_MAX = curPhotoRecord.scaleMax;                float[] photoCorners = calculateCorners(curPhotoRecord);//计算图片四个角点和中心点                drawBoard(canvas, photoCorners);//绘制图形边线                drawMarks(canvas, photoCorners);//绘制边角图片            }            for (StrokeRecord record : curSketchData.strokeRecordList) {                int type = record.type;                if (type == StrokeRecord.STROKE_TYPE_ERASER){ // 橡皮擦                    mEraseCanvas.drawPath(record.path, record.paint);                } else if (type == StrokeRecord.STROKE_TYPE_DRAW || type == StrokeRecord.STROKE_TYPE_LINE) {                    Log.e("iwmb", "--drawRecord:--path:"+record.path.toString()+"--type:"+type);//                    canvas.drawPath(record.path, record.paint);                    mEraseCanvas.drawPath(record.path, record.paint);                } else if (type == STROKE_TYPE_CIRCLE) {                    Log.e("iwmb", "--drawcircle:--path:"+record.rect.toString()+"--type:"+type);//                    canvas.drawOval(record.rect, record.paint);                    mEraseCanvas.drawOval(record.rect, record.paint);                } else if (type == STROKE_TYPE_RECTANGLE) {//                    canvas.drawRect(record.rect, record.paint);                    mEraseCanvas.drawRect(record.rect, record.paint);                } else if (type == STROKE_TYPE_TEXT) {                    if (record.text != null) {                        StaticLayout layout = new StaticLayout(record.text, record.textPaint, record.textWidth, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);//                        canvas.translate(record.textOffX, record.textOffY);//                        layout.draw(canvas);//                        canvas.translate(-record.textOffX, -record.textOffY);                        mEraseCanvas.translate(record.textOffX, record.textOffY);                        layout.draw(mEraseCanvas);                        mEraseCanvas.translate(-record.textOffX, -record.textOffY);                    }                }            }            canvas.drawBitmap(mEraseMaskBitmap, 0, 0, null);        }    }    //绘制图像边线(由于图形旋转或不一定是矩形,所以用Path绘制边线)    private void drawBoard(Canvas canvas, float[] photoCorners) {        Path photoBorderPath = new Path();        photoBorderPath.moveTo(photoCorners[0], photoCorners[1]);        photoBorderPath.lineTo(photoCorners[2], photoCorners[3]);        photoBorderPath.lineTo(photoCorners[4], photoCorners[5]);        photoBorderPath.lineTo(photoCorners[6], photoCorners[7]);        photoBorderPath.lineTo(photoCorners[0], photoCorners[1]);        canvas.drawPath(photoBorderPath, boardPaint);    }    //绘制边角操作图标    private void drawMarks(Canvas canvas, float[] photoCorners) {        float x;        float y;        x = photoCorners[0] - markerCopyRect.width() / 2;        y = photoCorners[1] - markerCopyRect.height() / 2;        markerCopyRect.offsetTo(x, y);        canvas.drawBitmap(mirrorMarkBM, x, y, null);        x = photoCorners[2] - markerDeleteRect.width() / 2;        y = photoCorners[3] - markerDeleteRect.height() / 2;        markerDeleteRect.offsetTo(x, y);        canvas.drawBitmap(deleteMarkBM, x, y, null);        x = photoCorners[4] - markerRotateRect.width() / 2;        y = photoCorners[5] - markerRotateRect.height() / 2;        markerRotateRect.offsetTo(x, y);        canvas.drawBitmap(rotateMarkBM, x, y, null);        x = photoCorners[6] - markerResetRect.width() / 2;        y = photoCorners[7] - markerResetRect.height() / 2;        markerResetRect.offsetTo(x, y);        canvas.drawBitmap(resetMarkBM, x, y, null);    }    private float[] calculateCorners(PhotoRecord record) {        float[] photoCornersSrc = new float[10];//0,1代表左上角点XY,2,3代表右上角点XY,4,5代表右下角点XY,6,7代表左下角点XY,8,9代表中心点XY        float[] photoCorners = new float[10];//0,1代表左上角点XY,2,3代表右上角点XY,4,5代表右下角点XY,6,7代表左下角点XY,8,9代表中心点XY        RectF rectF = record.photoRectSrc;        photoCornersSrc[0] = rectF.left;        photoCornersSrc[1] = rectF.top;        photoCornersSrc[2] = rectF.right;        photoCornersSrc[3] = rectF.top;        photoCornersSrc[4] = rectF.right;        photoCornersSrc[5] = rectF.bottom;        photoCornersSrc[6] = rectF.left;        photoCornersSrc[7] = rectF.bottom;        photoCornersSrc[8] = rectF.centerX();        photoCornersSrc[9] = rectF.centerY();        curPhotoRecord.matrix.mapPoints(photoCorners, photoCornersSrc);        return photoCorners;    }    private float getMaxScale(RectF photoSrc) {        return Math.max(getWidth(), getHeight()) / Math.max(photoSrc.width(), photoSrc.height());//        SCALE_MIN = SCALE_MAX / 5;    }    public void addStrokeRecord(StrokeRecord record) {        curSketchData.strokeRecordList.add(record);        invalidate();    }    private void touch_down(MotionEvent event) {        downX = event.getX();        downY = event.getY();        if (editMode == EDIT_STROKE) {            curSketchData.strokeRedoList.clear();            curStrokeRecord = new StrokeRecord(strokeType);            if (strokeType == STROKE_TYPE_ERASER) {                strokePath = new Path();                strokePath.moveTo(downX, downY);                curStrokeRecord.paint = new Paint(strokePaint); // Clones the mPaint object                curStrokeRecord.paint.setXfermode(new PorterDuffXfermode(Mode.DST_OUT));                curStrokeRecord.paint.setStrokeWidth(eraserSize);                curStrokeRecord.path = strokePath;            } else if (strokeType == STROKE_TYPE_DRAW || strokeType == STROKE_TYPE_LINE) {                strokePath = new Path();                strokePath.moveTo(downX, downY);                curStrokeRecord.path = strokePath;                strokePaint.setColor(strokeRealColor);                strokePaint.setStrokeWidth(strokeSize);                curStrokeRecord.paint = new Paint(strokePaint); // Clones the mPaint object            } else if (strokeType == STROKE_TYPE_CIRCLE || strokeType == STROKE_TYPE_RECTANGLE) {                RectF rect = new RectF(downX, downY, downX, downY);                curStrokeRecord.rect = rect;                strokePaint.setColor(strokeRealColor);                strokePaint.setStrokeWidth(strokeSize);                curStrokeRecord.paint = new Paint(strokePaint); // Clones the mPaint object            } else if (strokeType == STROKE_TYPE_TEXT) {                curStrokeRecord.textOffX = (int) downX;                curStrokeRecord.textOffY = (int) downY;                TextPaint tp = new TextPaint();                tp.setColor(strokeRealColor);                curStrokeRecord.textPaint = tp; // Clones the mPaint object                textWindowCallback.onText(this, curStrokeRecord);                return;            }            curSketchData.strokeRecordList.add(curStrokeRecord);        } else if (editMode == EDIT_PHOTO) {            float[] downPoint = new float[]{downX, downY};            if (isInMarkRect(downPoint)) {// 先判操作标记区域                return;            }            if (isInPhotoRect(curPhotoRecord, downPoint)) {//再判断是否点击了当前图片                actionMode = ACTION_DRAG;                return;            }            selectPhoto(downPoint);//最后判断是否点击了其他图片        }    }    //judge click which photo,then can edit the photo    private void selectPhoto(float[] downPoint) {        PhotoRecord clickRecord = null;        for (int i = curSketchData.photoRecordList.size() - 1; i >= 0; i--) {            PhotoRecord record = curSketchData.photoRecordList.get(i);            if (isInPhotoRect(record, downPoint)) {                clickRecord = record;                break;            }        }        if (clickRecord != null) {            setCurPhotoRecord(clickRecord);            actionMode = ACTION_DRAG;        } else {            actionMode = ACTION_NONE;        }    }    private boolean isInMarkRect(float[] downPoint) {        if (markerRotateRect.contains(downPoint[0], (int) downPoint[1])) {//判断是否在区域内            actionMode = ACTION_ROTATE;            return true;        }        if (markerDeleteRect.contains(downPoint[0], (int) downPoint[1])) {//判断是否在区域内            curSketchData.photoRecordList.remove(curPhotoRecord);            setCurPhotoRecord(null);            actionMode = ACTION_NONE;            return true;        }        if (markerCopyRect.contains(downPoint[0], (int) downPoint[1])) {//判断是否在区域内            PhotoRecord newRecord = initPhotoRecord(curPhotoRecord.bitmap);            newRecord.matrix = new Matrix(curPhotoRecord.matrix);            newRecord.matrix.postTranslate(ScreenUtils.dip2px(mContext, 20), ScreenUtils.dip2px(mContext, 20));//偏移小段距离以分辨新复制的图片            setCurPhotoRecord(newRecord);            actionMode = ACTION_NONE;            return true;        }        if (markerResetRect.contains(downPoint[0], (int) downPoint[1])) {//判断是否在区域内            curPhotoRecord.matrix.reset();            curPhotoRecord.matrix.setTranslate(getWidth() / 2 - curPhotoRecord.photoRectSrc.width() / 2,                    getHeight() / 2 - curPhotoRecord.photoRectSrc.height() / 2);            actionMode = ACTION_NONE;            return true;        }        return false;    }    private boolean isInPhotoRect(PhotoRecord record, float[] downPoint) {        if (record != null) {            float[] invertPoint = new float[2];            Matrix invertMatrix = new Matrix();            record.matrix.invert(invertMatrix);            invertMatrix.mapPoints(invertPoint, downPoint);            return record.photoRectSrc.contains(invertPoint[0], invertPoint[1]);        }        return false;    }    private void touch_move(MotionEvent event) {        if (editMode == EDIT_STROKE) {            if (strokeType == STROKE_TYPE_ERASER) {                strokePath.quadTo(preX, preY, (curX + preX) / 2, (curY + preY) / 2);//              curStrokeRecord.path.quadTo(preX, preY, (curX + preX) / 2, (curY + preY) / 2);            } else if (strokeType == STROKE_TYPE_DRAW) {                strokePath.quadTo(preX, preY, (curX + preX) / 2, (curY + preY) / 2);//              curStrokeRecord.path.quadTo(preX, preY, (curX + preX) / 2, (curY + preY) / 2);            } else if (strokeType == STROKE_TYPE_LINE) {                strokePath.reset();                strokePath.moveTo(downX, downY);                strokePath.lineTo(curX, curY);//              curStrokeRecord.path.reset();//              curStrokeRecord.path.moveTo(downX, downY);//              curStrokeRecord.path.lineTo(curX, curY);            } else if (strokeType == STROKE_TYPE_CIRCLE || strokeType == STROKE_TYPE_RECTANGLE) {                curStrokeRecord.rect.set(downX < curX ? downX : curX, downY < curY ? downY : curY, downX > curX ? downX : curX, downY > curY ? downY : curY);            } else if (strokeType == STROKE_TYPE_TEXT) {            }        } else if (editMode == EDIT_PHOTO && curPhotoRecord != null) {            if (actionMode == ACTION_DRAG) {                onDragAction(curX - preX, curY - preY);            } else if (actionMode == ACTION_ROTATE) {                onRotateAction(curPhotoRecord);            } else if (actionMode == ACTION_SCALE) {                mScaleGestureDetector.onTouchEvent(event);            }        }        preX = curX;        preY = curY;    }    private void onScaleAction(ScaleGestureDetector detector) {        float[] photoCorners = calculateCorners(curPhotoRecord);        //目前图片对角线长度        float len = (float) Math.sqrt(Math.pow(photoCorners[0] - photoCorners[4], 2) + Math.pow(photoCorners[1] - photoCorners[5], 2));        double photoLen = Math.sqrt(Math.pow(curPhotoRecord.photoRectSrc.width(), 2) + Math.pow(curPhotoRecord.photoRectSrc.height(), 2));        float scaleFactor = detector.getScaleFactor();        //设置Matrix缩放参数        if ((scaleFactor < 1 && len >= photoLen * SCALE_MIN && len >= SCALE_MIN_LEN) || (scaleFactor > 1 && len <= photoLen * SCALE_MAX)) {            Log.e(scaleFactor + "", scaleFactor + "");            curPhotoRecord.matrix.postScale(scaleFactor, scaleFactor, photoCorners[8], photoCorners[9]);        }    }    private void onRotateAction(PhotoRecord record) {        float[] corners = calculateCorners(record);        //放大        //目前触摸点与图片显示中心距离        float a = (float) Math.sqrt(Math.pow(curX - corners[8], 2) + Math.pow(curY - corners[9], 2));        //目前上次旋转图标与图片显示中心距离        float b = (float) Math.sqrt(Math.pow(corners[4] - corners[0], 2) + Math.pow(corners[5] - corners[1], 2)) / 2;        //设置Matrix缩放参数        double photoLen = Math.sqrt(Math.pow(record.photoRectSrc.width(), 2) + Math.pow(record.photoRectSrc.height(), 2));        if (a >= photoLen / 2 * SCALE_MIN && a >= SCALE_MIN_LEN && a <= photoLen / 2 * SCALE_MAX) {            //这种计算方法可以保持旋转图标坐标与触摸点同步缩放            float scale = a / b;            record.matrix.postScale(scale, scale, corners[8], corners[9]);        }        //旋转        //根据移动坐标的变化构建两个向量,以便计算两个向量角度.        PointF preVector = new PointF();        PointF curVector = new PointF();        preVector.set(preX - corners[8], preY - corners[9]);//旋转后向量        curVector.set(curX - corners[8], curY - corners[9]);//旋转前向量        //计算向量长度        double preVectorLen = getVectorLength(preVector);        double curVectorLen = getVectorLength(curVector);        //计算两个向量的夹角.        double cosAlpha = (preVector.x * curVector.x + preVector.y * curVector.y)                / (preVectorLen * curVectorLen);        //由于计算误差,可能会带来略大于1的cos,例如        if (cosAlpha > 1.0f) {            cosAlpha = 1.0f;        }        //本次的角度已经计算出来。        double dAngle = Math.acos(cosAlpha) * 180.0 / Math.PI;        // 判断顺时针和逆时针.        //判断方法其实很简单,这里的v1v2其实相差角度很小的。        //先转换成单位向量        preVector.x /= preVectorLen;        preVector.y /= preVectorLen;        curVector.x /= curVectorLen;        curVector.y /= curVectorLen;        //作curVector的逆时针垂直向量。        PointF verticalVec = new PointF(curVector.y, -curVector.x);        //判断这个垂直向量和v1的点积,点积>0表示俩向量夹角锐角。=0表示垂直,<0表示钝角        float vDot = preVector.x * verticalVec.x + preVector.y * verticalVec.y;        if (vDot > 0) {            //v2的逆时针垂直向量和v1是锐角关系,说明v1在v2的逆时针方向。        } else {            dAngle = -dAngle;        }        record.matrix.postRotate((float) dAngle, corners[8], corners[9]);    }    /**     * 获取p1到p2的线段的长度     *     * @return     */    double getVectorLength(PointF vector) {        return Math.sqrt(vector.x * vector.x + vector.y * vector.y);    }    private void onDragAction(float distanceX, float distanceY) {            curPhotoRecord.matrix.postTranslate((int) distanceX, (int) distanceY);    }    private void touch_up() {        if (onDrawChangedListener != null)          onDrawChangedListener.onDrawChanged();    }    @NonNull    public Bitmap getResultBitmap() {        Bitmap newBM = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);        Canvas canvas = new Canvas(newBM);        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));//抗锯齿        //绘制背景        drawBackground(canvas);        drawRecord(canvas, false);        canvas.save(Canvas.ALL_SAVE_FLAG);        canvas.restore();        return newBM;    }    @NonNull    public void createCurThumbnailBM() {        curSketchData.thumbnailBM = getThumbnailResultBitmap();    }    @NonNull    public Bitmap getThumbnailResultBitmap() {        return BitmapUtils.createBitmapThumbnail(getResultBitmap(), true, ScreenUtils.dip2px(mContext, 200), ScreenUtils.dip2px(mContext, 200));    }    /*     * 删除一笔     */    public void undo() {        if (curSketchData.strokeRecordList.size() > 0) {            curSketchData.strokeRedoList.add(curSketchData.strokeRecordList.get(curSketchData.strokeRecordList.size() - 1));            curSketchData.strokeRecordList.remove(curSketchData.strokeRecordList.size() - 1);            invalidate();            touch_up(); // refresh redo/undo icon status        }    }    /*     * 撤销     */    public void redo() {        if (curSketchData.strokeRedoList.size() > 0) {            curSketchData.strokeRecordList.add(curSketchData.strokeRedoList.get(curSketchData.strokeRedoList.size() - 1));            curSketchData.strokeRedoList.remove(curSketchData.strokeRedoList.size() - 1);            invalidate();            touch_up(); // refresh redo/undo icon status        }    }    public int getRedoCount() {        return curSketchData.strokeRedoList != null ? curSketchData.strokeRedoList.size() : 0;    }    public int getRecordCount() {        return (curSketchData.strokeRecordList != null && curSketchData.photoRecordList != null) ? curSketchData.strokeRecordList.size() + curSketchData.photoRecordList.size() : 0;    }    public int getStrokeRecordCount() {        return curSketchData.strokeRecordList != null ? curSketchData.strokeRecordList.size() : 0;    }    public int getStrokeSize() {        return Math.round(this.strokeSize);    }    public void setSize(int size, int eraserOrStroke) {        switch (eraserOrStroke) {            case STROKE_TYPE_DRAW:                strokeSize = size;                break;            case STROKE_TYPE_ERASER:                eraserSize = size;                break;        }    }    public void erase() {        // 先判断是否已经回收        for (PhotoRecord record : curSketchData.photoRecordList) {            if (record != null && record.bitmap != null && !record.bitmap.isRecycled()) {                record.bitmap.recycle();                record.bitmap = null;            }        }        if (curSketchData.backgroundBM != null && !curSketchData.backgroundBM.isRecycled()) {            // 回收并且置为null            curSketchData.backgroundBM.recycle();            curSketchData.backgroundBM = null;        }        curSketchData.strokeRecordList.clear();        curSketchData.photoRecordList.clear();        curSketchData.strokeRedoList.clear();        curPhotoRecord = null;        System.gc();        invalidate();    }    public void setOnDrawChangedListener(OnDrawChangedListener listener) {        this.onDrawChangedListener = listener;    }    public interface OnDrawChangedListener {        public void onDrawChanged();    }    public void addPhotoByPath(String path) {        Bitmap sampleBM = getSampleBitMap(path);        if (sampleBM != null) {            PhotoRecord newRecord = initPhotoRecord(sampleBM);            setCurPhotoRecord(newRecord);        } else {            Toast.makeText(mContext, "图片文件路径有误!", Toast.LENGTH_SHORT).show();        }    }    public void setBackgroundByPath(String path) {        Bitmap sampleBM = getSampleBitMap(path);        if (sampleBM != null) {            curSketchData.backgroundBM = sampleBM;            backgroundSrcRect = new Rect(0, 0, curSketchData.backgroundBM.getWidth(), curSketchData.backgroundBM.getHeight());            backgroundDstRect = new Rect(0, 0, mWidth, mHeight);            invalidate();        } else {            Toast.makeText(mContext, "图片文件路径有误!", Toast.LENGTH_SHORT).show();        }    }    public Bitmap getSampleBitMap(String path) {        Bitmap sampleBM = null;        if (path.contains(Environment.getExternalStorageDirectory().toString())) {            sampleBM = getSDCardPhoto(path);        } else {            sampleBM = getAssetsPhoto(path);        }        return sampleBM;    }    @NonNull    private PhotoRecord initPhotoRecord(Bitmap bitmap) {        PhotoRecord newRecord = new PhotoRecord();        newRecord.bitmap = bitmap;        newRecord.photoRectSrc = new RectF(0, 0, newRecord.bitmap.getWidth(), newRecord.bitmap.getHeight());        newRecord.scaleMax = getMaxScale(newRecord.photoRectSrc);//放大倍数        newRecord.matrix = new Matrix();        newRecord.matrix.postTranslate(getWidth() / 2 - bitmap.getWidth() / 2, getHeight() / 2 - bitmap.getHeight() / 2);        return newRecord;    }    private void setCurPhotoRecord(PhotoRecord record) {        curSketchData.photoRecordList.remove(record);        curSketchData.photoRecordList.add(record);        curPhotoRecord = record;        invalidate();    }    public Bitmap getSDCardPhoto(String path) {        File file = new File(path);        if (file.exists()) {            return BitmapUtils.decodeSampleBitMapFromFile(mContext, path, simpleScale);        } else {            return null;        }    }    public Bitmap getAssetsPhoto(String path) {        return BitmapUtils.getBitmapFromAssets(mContext, path);    }    public void setEditMode(int editMode) {        this.editMode = editMode;        invalidate();    }    public int getEditMode() {        return editMode;    }}

WhiteBoardView.java

package com.example.whiteboard;import java.util.ArrayList;import java.util.List;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.Path;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;/** * white board view * you can draw on it  * @author WMB * */public class WhiteBoardView extends View{    private Paint mPaint =  new Paint();    private List mPathList = new ArrayList();    private Path mPath = new Path();    private float mX,mY;    private Bitmap tempBM;    public WhiteBoardView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public WhiteBoardView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public WhiteBoardView(Context context) {        super(context);        mPaint.setAntiAlias(true);        mPaint.setStyle(Style.STROKE);        mPaint.setStrokeWidth(10);        mPaint.setColor(Color.BLACK);        tempBM = Bitmap.createBitmap(2000, 1500, Config.ARGB_4444);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction() & MotionEvent.ACTION_MASK) {        case MotionEvent.ACTION_POINTER_DOWN:            Log.e("wmb", "--ACTION_POINTER_DOWN");            break;        case MotionEvent.ACTION_POINTER_UP:            Log.e("wmb", "--ACTION_POINTER_UP");            break;        case MotionEvent.ACTION_DOWN:            Log.e("wmb", "--action donw");            touch_down(event);            invalidate();            break;        case MotionEvent.ACTION_MOVE:            Log.e("wmb", "--action move");            touch_move(event);            invalidate();            break;        case MotionEvent.ACTION_UP:            Log.e("wmb", "--action up");            touch_up(event);            invalidate();            break;        default:            break;        }        return true;    }    private void touch_up(MotionEvent event) {    }    private void touch_move(MotionEvent event) {        float x = event.getX();        float y = event.getY();        float preX = mX;        float preY = mY;        float dx = Math.abs(preX - x);        float dy = Math.abs(preY - y); //      if(dx >= 3 || dy >= 3){            float cX = (x + preX) / 2;            float cY = (y + preY) / 2;            mPath.reset();            mPath.moveTo(preX, preY);            mPath.lineTo(x, y);/*(x, y, cX, cY);*///          mX = x;//          mY = y;//      }    }    private void touch_down(MotionEvent event) {//      mPath.reset();        float x = event.getX();        float y = event.getY();        mX = x;        mY = y;        mPath.moveTo(x, y);    }    @Override    protected void onDraw(Canvas canvas) {//      for (int i = 0; i < mPathList.size(); i++) {//          Log.e("wmb", "--ondraw--i:"+i);        canvas.drawColor(Color.GREEN);        tempBM = Bitmap.createBitmap(2000, 1500, Config.ARGB_4444);        Canvas tmpCs = new Canvas(tempBM);        tmpCs.drawPath(mPath, mPaint);        canvas.drawBitmap(tempBM, 0, 0, null);//          canvas.drawPath(mPath, mPaint);//          canvas.save();        }//      super.onDraw(canvas);    }

BitmapUtils.java

package com.example.whiteboard.utils;import android.content.Context;import android.content.res.Configuration;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Matrix;import android.util.Log;import android.view.WindowManager;import java.io.InputStream;/** * Created by TangentLu on 2015/8/19. */public class BitmapUtils {    /**     * 是否为横屏     * @param context     * @return     */    public static boolean isLandScreen(Context context) {        int ori =context.getResources().getConfiguration().orientation;//获取屏幕方向        return ori == Configuration.ORIENTATION_LANDSCAPE;    }    public static Bitmap decodeSampleBitMapFromFile(Context context, String filePath, float sampleScale) {        //先得到bitmap的高宽        BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeFile(filePath, options);        //再用屏幕一半高宽、缩小后的高宽对比,取小值进行缩放        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);        int reqWidth = wm.getDefaultDisplay().getWidth();        int reqHeight = wm.getDefaultDisplay().getWidth();        int scaleWidth = (int) (options.outWidth * sampleScale);        int scaleHeight = (int) (options.outHeight * sampleScale);        reqWidth = Math.min(reqWidth, scaleWidth);        reqHeight = Math.min(reqHeight, scaleHeight);        options = sampleBitmapOptions(context, options, reqWidth, reqHeight);        Bitmap bm = BitmapFactory.decodeFile(filePath, options);        Log.e("xxx", bm.getByteCount() + "");        return bm;    }    public static Bitmap decodeSampleBitMapFromResource(Context context, int resId, int reqWidth, int reqHeight) {        BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;        BitmapFactory.decodeResource(context.getResources(), resId, options);        options = sampleBitmapOptions(context, options, reqWidth, reqHeight);        Bitmap bm = BitmapFactory.decodeResource(context.getResources(), resId, options);        Log.e("xxx", bm.getByteCount() + "");        return bm;    }    public static Bitmap createBitmapThumbnail(Bitmap bitMap, boolean needRecycle, int newHeight, int newWidth) {        int width = bitMap.getWidth();        int height = bitMap.getHeight();        // 计算缩放比例        float scale = Math.min((float) newWidth / width, (float) (newHeight) / height);        // 取得想要缩放的matrix参数        Matrix matrix = new Matrix();        matrix.postScale(scale, scale);        // 得到新的图片        Bitmap newBitMap = Bitmap.createBitmap(bitMap, 0, 0, width, height, matrix, true);        if (needRecycle)            bitMap.recycle();        return newBitMap;    }    public static BitmapFactory.Options sampleBitmapOptions(            Context context, BitmapFactory.Options options, int reqWidth, int reqHeight) {        int targetDensity = context.getResources().getDisplayMetrics().densityDpi;        // Calculate inSampleSize        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);        double xSScale = ((double) options.outWidth) / ((double) reqWidth);        double ySScale = ((double) options.outHeight) / ((double) reqHeight);        double startScale = xSScale > ySScale ? xSScale : ySScale;        options.inScaled = true;        options.inDensity = (int) (targetDensity * startScale);        options.inTargetDensity = targetDensity;        options.inJustDecodeBounds = false;        options.inPreferredConfig = Bitmap.Config.RGB_565;        return options;    }    public static int calculateInSampleSize(            BitmapFactory.Options options, int reqWidth, int reqHeight) {        // Raw height and width of image        final int height = options.outHeight;        final int width = options.outWidth;        int inSampleSize = 1;        if (height > reqHeight || width > reqWidth) {            final int halfHeight = height / 2;            final int halfWidth = width / 2;            // Calculate the largest inSampleSize value that is a power of 2 and keeps both            // height and width larger than the requested height and width.            while ((halfHeight / inSampleSize) > reqHeight                    && (halfWidth / inSampleSize) > reqWidth) {                inSampleSize *= 2;            }        }        return inSampleSize;    }    public static Bitmap getBitmapFromAssets(Context context,String path){        InputStream open = null;        Bitmap bitmap = null;        try {            String temp =  path;            open = context.getAssets().open(temp);            BitmapFactory.Options options = new BitmapFactory.Options();            options = sampleBitmapOptions(context, options, 10, 10);            bitmap = BitmapFactory.decodeStream(open, null, options);            return bitmap;        } catch (Exception e) {e.printStackTrace();            return null;        }    }}

CommonUtils.java

package com.example.whiteboard.utils;import android.content.Context;import android.util.Log;import android.widget.Toast;public class CommonUtils {    public static Context mContext;    public static final String TAG_WHITEBOARD = "--whiteboard";    public static void init(Context context){        mContext = context;    }    public static void showToast(String text, int time){        if(null != mContext){            Toast.makeText(mContext, text, time).show();        }        else{            Log.e(TAG_WHITEBOARD, "--showToast--mcontext is null");        }    }    public static void destory() {         mContext = null;           }}

ScreenUtils.java

package com.example.whiteboard.utils;import android.app.Activity;import android.content.Context;import android.content.res.Configuration;import android.graphics.Point;import android.os.Build;import android.util.Log;import android.view.Display;import android.view.View;import android.view.WindowManager;import android.view.inputmethod.InputMethodManager;import java.lang.reflect.Field;/** *  屏幕工具 * Created by nereo on 15/11/19. * Updated by nereo on 2016/1/19. */public class ScreenUtils {    public static Point getScreenSize(Context context){        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);        Display display = wm.getDefaultDisplay();        Point out = new Point();        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {            display.getSize(out);        }else{            int width = display.getWidth();            int height = display.getHeight();            out.set(width, height);        }        return out;    }    /**     * 获取状态栏高度     */    public static int getStatusBarHeight(Context context) {        Class<?> c = null;        Object obj = null;        Field field = null;        int x = 0, sbar = 0;        try {            c = Class.forName("com.android.internal.R$dimen");            obj = c.newInstance();            field = c.getField("status_bar_height");            x = Integer.parseInt(field.get(obj).toString());            sbar = context.getResources().getDimensionPixelSize(x);        } catch (Exception e1) {            Log.e("getStatusBarHight()", "get status bar height fail");            e1.printStackTrace();        }        int statusBarHeight = sbar;        Log.i("onPreDraw", "statusBarHeight: "+statusBarHeight);        return statusBarHeight;    }    /**     * 获取状态栏和标题栏的高度     * @param activity     * @return     */    public static int getStatusBarAndTitleHeight(Activity activity){        if(null == activity){            return 0;        }        View view = activity.getWindow().findViewById(android.R.id.content);        if(null == view){            return 0;        }        int height = view.getTop();        return height;    }    public static void hideInput(View v) {        InputMethodManager inputManager = (InputMethodManager) v                .getContext().getSystemService(Context.INPUT_METHOD_SERVICE);        //调用系统输入法        inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);    }    public static void showInput(View v) {        InputMethodManager inputManager = (InputMethodManager) v                .getContext().getSystemService(Context.INPUT_METHOD_SERVICE);        //调用系统输入法        inputManager.showSoftInput(v, InputMethodManager.SHOW_FORCED);    }    public static void toggleInput(Context context) {        InputMethodManager inputMethodManager =                (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);        inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);    }    public static int px2dip(Context context, float pxValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (pxValue / scale + 0.5f);    }    public static int dip2px(Context context, float dpValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dpValue * scale + 0.5f);    }    /**     * 返回当前屏幕是否为竖屏     * @param context     * @return     */    public static boolean isScreenOriatationPortrait(Context context) {         return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;         }}

SdCardStatus.java

package com.example.whiteboard.utils;import java.io.File;import java.lang.reflect.Field;import java.lang.reflect.Method;import android.content.Context;import android.content.Intent;import android.os.Environment;import android.os.IBinder;import android.os.storage.StorageManager;import android.text.TextUtils;import android.util.Log;/** * Created by gpy on 2015/10/20. */public class SdCardStatus {    private static String CACHE_FOLDER_NAME = "WMBWhiteBoard";    private static String NONE_SD_CARD_PROMPT = "您的手机中sd卡不存在";    public static void init(String cacheFolderName) {        CACHE_FOLDER_NAME = cacheFolderName;    }    public static String getDefaulstCacheDirInSdCard() throws IllegalStateException {        String sdCardPath = null;        sdCardPath = getSDPath();        if (null == sdCardPath) {            throw new IllegalStateException(NONE_SD_CARD_PROMPT);        }        return sdCardPath + File.separator + CACHE_FOLDER_NAME;    }    /**     * when not exist sd card,return null.     *     * @return     */    public static String getSDPath() {        boolean sdCardExist = Environment.getExternalStorageState().equals(                Environment.MEDIA_MOUNTED);        if (sdCardExist) {            return Environment.getExternalStorageDirectory().getAbsolutePath();        } else {                return null;        }    }    public static int getReflactField(String className,String fieldName){        int result = -1;        try {            Class<?> clz = Class.forName(className);            Field field = clz.getField(fieldName);            field.setAccessible(true);            result = field.getInt(null);         } catch (Exception e) {            e.printStackTrace();        }        return result;    }    int title_id = getReflactField("com.android.internal.R$id", "title");    int icon_id = getReflactField("com.android.internal.R$id", "icon");//    public static StorageVolume[] getVolumeList(StorageManager storageManager){//      try {//          Class clz = StorageManager.class;//          Method getVolumeList = clz.getMethod("getVolumeList", null);//          StorageVolume[] result = (StorageVolume[]) getVolumeList.invoke(storageManager, null);//          return result;//      } catch (Exception e) {//          e.printStackTrace();//      }//      return null;//    }    public static void shutDown(){        try {            Class<?> clz = Class.forName("android.os.ServiceManager");            Log.e("wmb", "--shutDown 111");            Method getService = clz.getMethod("getService", String.class);            Log.e("wmb", "--shutDown 222");            Object powerService = getService.invoke(null, Context.POWER_SERVICE);            Log.e("wmb", "--shutDown 333");            Class<?> cStub =  Class.forName("android.os.IPowerManager$Stub");            Log.e("wmb", "--shutDown 444");            Method asInterface = cStub.getMethod("asInterface", IBinder.class);            Log.e("wmb", "--shutDown 555");            Object IPowerManager = asInterface.invoke(null, powerService);            Log.e("wmb", "--shutDown 666");            Method shutDown = IPowerManager.getClass().getMethod("shutdown", boolean.class, boolean.class);            Log.e("wmb", "--shutDown 777");            shutDown.invoke(IPowerManager, false,true);            Log.e("wmb", "--shutDown 888");        } catch (Exception e) {            Log.e("wmb", "--shutDown has an exception");            e.printStackTrace();        }    }    public static void reboot(){        try {            Class<?> clz = Class.forName("android.os.ServiceManager");            Log.e("wmb", "--shutDown 111");            Method getService = clz.getMethod("getService", String.class);            Log.e("wmb", "--shutDown 222");            Object powerService = getService.invoke(null, Context.POWER_SERVICE);            Log.e("wmb", "--shutDown 333");            Class<?> cStub =  Class.forName("android.os.IPowerManager$Stub");            Log.e("wmb", "--shutDown 444");            Method asInterface = cStub.getMethod("asInterface", IBinder.class);            Log.e("wmb", "--shutDown 555");            Object IPowerManager = asInterface.invoke(null, powerService);            Log.e("wmb", "--shutDown 666");            Method reboot = IPowerManager.getClass().getMethod("reboot", boolean.class,String.class, boolean.class);            Log.e("wmb", "--shutDown 777");            reboot.invoke(IPowerManager, false,"wmb test",true);            Log.e("wmb", "--shutDown 888");        } catch (Exception e) {            Log.e("wmb", "--shutDown has an exception");            e.printStackTrace();        }    }    public static String getVolumeState(StorageManager storageManager, String path){        String result = "";        if(null == storageManager || TextUtils.isEmpty(path)){            return result;        }        try {            Class clz = StorageManager.class;            Method getVolumeList = clz.getMethod("getVolumeState", String.class);            result = (String) getVolumeList.invoke(storageManager, path);        } catch (Exception e) {            e.printStackTrace();        }        return result;    }}

TimeUtils.java

package com.example.whiteboard.utils;import java.io.File;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Locale;/** * 时间处理工具 * Created by Nereo on 2015/4/8. */public class TimeUtils {    public static String timeFormat(long timeMillis, String pattern){        SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.CHINA);        return format.format(new Date(timeMillis));    }    public static String formatPhotoDate(long time){        return timeFormat(time, "yyyy-MM-dd");    }    public static String formatPhotoDate(String path){        File file = new File(path);        if(file.exists()){            long time = file.lastModified();            return formatPhotoDate(time);        }        return "1970-01-01";    }    public static String getNowTimeString() {        SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd_hhmmss");        return sDateFormat.format(new java.util.Date());    }}

ViewUtils.java

package com.example.whiteboard.utils;import android.app.Activity;import android.view.View;public class ViewUtils {    public static  T findViewById(Activity activity, int id){        return (T) activity.findViewById(id);    }    public static  T findViewById(View view, int id){        return (T) view.findViewById(id);    }}

布局文件如下:

fragment_white_board.xml

<?xml version="1.0" encoding="utf-8"?>"http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#ffffff" >    "@+id/id_sketch_view"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:background="@color/green"            android:focusable="true"            android:focusableInTouchMode="true"/>    "@+id/id_controller"        android:layout_width="match_parent"        android:layout_height="50dp"        android:layout_alignParentBottom="true"        android:orientation="horizontal"        android:gravity="center"        android:background="@color/gray"        android:paddingLeft="@dimen/control_layout_padding"        android:paddingRight="@dimen/control_layout_padding"        android:paddingTop="@dimen/control_btn_padding"        android:paddingBottom="@dimen/control_btn_padding">        "@+id/id_pen"            style="@style/control_btn"            android:src="@drawable/stroke_type_rbtn_draw_checked"/>        "@+id/id_eraser"            style="@style/control_btn"            android:src="@drawable/ic_eraser"/>        "@+id/id_undo"            style="@style/control_btn"            android:src="@drawable/ic_undo"/>        "@+id/id_redo"            style="@style/control_btn"            android:src="@drawable/ic_redo"/>        "@+id/id_pic"            style="@style/control_btn"            android:src="@drawable/ic_photo"/>        "@+id/id_bacground"            style="@style/control_btn"            android:src="@drawable/ic_background"/>        "@+id/id_drag"            style="@style/control_btn"            android:src="@drawable/ic_drag"/>        "@+id/id_save"            style="@style/control_btn"            android:src="@drawable/ic_file"/>        "@+id/id_empty"            style="@style/control_btn"            android:src="@drawable/ic_empty"/>        "@+id/id_screenshot"            style="@style/control_btn"            android:src="@drawable/ic_screenshot"/>    </LinearLayout>RelativeLayout>

popup_pen.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@drawable/bg_popup"    android:orientation="vertical"     android:padding="16dp">        <TextView         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:textSize="20sp"        android:text="画笔类型"/>    <RadioGroup         android:id="@+id/pen_type_radio_group"        android:layout_width="match_parent"        android:layout_height="50dp"        android:gravity="center"        android:orientation="horizontal">        <RadioButton             android:id="@+id/type_pen"            style="@style/control_btn.nobackground"            android:checked="true"            android:button="@drawable/stroke_type_rbtn_draw"/>        <RadioButton             android:id="@+id/type_line"            style="@style/control_btn.nobackground"            android:button="@drawable/stroke_type_rbtn_line"/>        <RadioButton             android:id="@+id/type_circle"            style="@style/control_btn.nobackground"            android:button="@drawable/stroke_type_rbtn_circle"/>        <RadioButton             android:id="@+id/type_rectangle"            style="@style/control_btn.nobackground"            android:button="@drawable/stroke_type_rbtn_rectangle"/>        <RadioButton             android:id="@+id/type_text"            style="@style/control_btn.nobackground"            android:button="@drawable/stroke_type_rbtn_text"/>    RadioGroup>        <TextView         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="画笔大小"        android:textSize="20sp"/>    <LinearLayout         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal"        android:gravity="center_vertical">        <ImageView             android:id="@+id/pen_size_circle"            android:layout_width="wrap_content"            android:layout_height="match_parent"            android:src="@drawable/stroke_color_rbtn_black"/>        <SeekBar             android:id="@+id/pen_size_seek_bar"            android:layout_width="match_parent"            android:layout_height="match_parent"/>    LinearLayout>        <TextView         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:textSize="20sp"        android:text="画笔颜色"/>    <RadioGroup         android:id="@+id/pen_color_radio_group"        android:layout_width="match_parent"        android:layout_height="50dp"        android:gravity="center"        android:orientation="horizontal">        <RadioButton             android:id="@+id/color_black"            style="@style/control_btn.nobackground"            android:checked="true"            android:button="@drawable/stroke_color_rbtn_black"/>        <RadioButton             android:id="@+id/color_red"            style="@style/control_btn.nobackground"            android:button="@drawable/stroke_color_rbtn_red"/>        <RadioButton             android:id="@+id/color_green"            style="@style/control_btn.nobackground"            android:button="@drawable/stroke_color_rbtn_green"/>        <RadioButton             android:id="@+id/color_orange"            style="@style/control_btn.nobackground"            android:button="@drawable/stroke_color_rbtn_orange"/>        <RadioButton             android:id="@+id/color_blue"            style="@style/control_btn.nobackground"            android:button="@drawable/stroke_color_rbtn_blue"/>    RadioGroup>        <TextView         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="画笔透明度"        android:textSize="20sp"/>    <LinearLayout         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal"        android:gravity="center_vertical">        <ImageView             android:id="@+id/pen_alpha_circle"            android:layout_width="wrap_content"            android:layout_height="match_parent"            android:src="@drawable/stroke_color_rbtn_black"/>        <SeekBar             android:id="@+id/pen_alpha_seek_bar"            android:layout_width="match_parent"            android:layout_height="match_parent"/>    LinearLayout>LinearLayout>

popup_eraser.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@drawable/bg_popup"    android:padding="16dp"    android:minWidth="410dp"    android:orientation="vertical" >    <TextView         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="橡皮擦大小"        android:textSize="20sp"/>    <LinearLayout         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="5dp"        android:layout_marginBottom="5dp"        android:orientation="horizontal">        <ImageView             android:id="@+id/eraser_size_circle"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:src="@drawable/circle"/>        <SeekBar             android:id="@+id/eraser_size_seek_bar"            android:layout_width="match_parent"            android:layout_height="match_parent"/>    LinearLayout>LinearLayout>

popup_text.xml

<?xml version="1.0" encoding="utf-8"?><EditText xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/text_pupwindow_et"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:hint="请输入文字"    android:imeOptions="actionDone"    android:maxWidth="200dp"    android:minWidth="120dp"    android:background="@color/gray"    android:selectAllOnFocus="true"    android:textSize="22sp" >EditText>

更多相关文章

  1. Android实现图片缩放与旋转
  2. Android imageView图片按比例缩放
  3. Android控制水平方向字体缩放android:textScaleX
  4. 第三章C++:字符串string、向量vector和数组
  5. 超级炫酷!HTML5 Canvas火焰画笔动画
  6. 使用sklearn轻松实现数据缩放
  7. Html css缩放会将大小改变几个像素
  8. 关于大背景图片随浏览器百分比缩放的问题
  9. Google Maps API v3:如何设置缩放级别和地图中心到用户提交的位置

随机推荐

  1. Android TextView的marquee跑马灯无效的
  2. Android AIDL跨进程通讯
  3. Android LruCache使用分析
  4. Android 处理多线程 UserTask
  5. Android SDK API 13升级指南
  6. Android下的数据储存方式(三)
  7. android学习——EditText的属性
  8. android Activity加载完成后的监听事件
  9. android service 面试
  10. 《Android开发从零开始》――1.Android开