上一篇文章
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 布局如下:
<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
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);
}
}
}
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;
}
}
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;
}
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;
}
}
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;
}
}
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;
}
}
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);
}
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;
}
}
}
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;
}
}
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;
}
}
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;
}
}
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());
}
}
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);
}
}
布局文件如下:
"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>
<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>
<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>
<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>