你的位置:首页 > 操作系统

[操作系统]Android电子白板实现,Canvas画布


前一阵子做了一个项目,里面有用到画板,在手机上画图,类似一个电子白板,画出曲线,圆,矩形。。   后来查了资料,看了些大神的博客

单独优化出了一个小程序,在原来基础上新增了橡皮檫功能,画出的图形可以是曲线,直线,矩形,正方形,圆,椭圆,也可以筛选相册图片显示到画板,

画图的同时更改画笔的颜色

 

基本思路是在画板上确认两个点,一个起点,一个终点,根据选择的图形样式,拖动的时候有预览图形,松开时图形定下来显示到画板上。

曲线是用贝塞尔曲线,其余基本图形由两个点确定位置大小,canvas提供的方法就能很快画出来。

一.  一个Canvas对象有四大基本要素:

1.一个用来保存像素的Bitmap

2.一个Canvas在Bitmap上进行绘制操作

3.绘制的东西

4.绘制的画笔Paint

提供的方法有:

  canvas.drawArc (扇形)

  canvas.drawCircle(圆)

  canvas.drawOval(椭圆)

  canvas.drawLine(线)

  canvas.drawPoint(点)

  canvas.drawRect(矩形)

  canvas.drawRoundRect(圆角矩形)

  canvas.drawVertices(顶点)

  cnavas.drawPath(路径)

  canvas.drawBitmap (位图)

  canvas.drawPicture (图片)

  canvas.drawText(文本)

 

二.Paint(画笔)类

    要绘制图形,首先得调整画笔,按照自己的开发需要设置画笔的相关属性。Pain类的常用属性设置方法如下:

  setAntiAlias();  设置画笔的锯齿效果

  setColor();   设置画笔的颜色

  setARGB();   设置画笔的A、R、G、B值

  setAlpha();    设置画笔的Alpha值

  setTextSize();    设置字体的尺寸

  setStyle();     设置画笔的风格(空心或实心)

  setStrokeWidth();    设置空心边框的宽度

  getColor();    获取画笔的颜色

  setDither();  获取跟清晰的图像采样

  setStrokeJoin(); 接合处为圆弧

  setStrokeCap(); 画笔样式圆形

三.自定义View的基本实现方法

  首先,我们需要自定义一个类,比如ControlView,继承于View类,也可以继承ViewGroup,因为ViewGroup是继承于View的,然后,复写View类的onDraw()函数。

  最后,在onDraw()函数中使用Paint和Canvas对象绘制我们需要的图形。

  drawBitmap(Bitmap bitmap, float left, float top, Paint paint);

  通过onTouchEvent(MotionEvent)的getAction()方法来获取Touch事件的类型,包括 ACTION_DOWN(按下触摸屏), ACTION_MOVE(按下触摸屏后移动受力点), ACTION_UP(松开触摸屏)

  和ACTION_CANCEL(不会由用户直接触发)。借助对于用户不同操作的判断,结合getRawX()、 getRawY()、getX()和getY()等方法来获取坐标,所有的绘画都在

  MotionEvent.ACTION_MOVE 去做,MotionEvent.ACTION_UP(松开触摸屏)时,画出图形,更新画布

下面列出主要model代码:

  1.继承类IShape,AbsShape

public interface IShape {  public void touchMove(int startX, int startY, int x, int y);  public void drawShape(Canvas canvas);}

  

public abstract class AbsShape implements IShape{  protected ControlView mView;  protected Paint mPaint;  public AbsShape(ControlView view, int model)  {    mView = view;    // 去锯齿    mPaint = new Paint();    // 去锯齿    mPaint.setAntiAlias(true);    // 设置paint的颜色    mPaint.setColor(model == ControlView.MODEL_ERASE ? Constants.BACK_COLOR : view.getPenColor());    // 设置paint的 style 为STROKE:空心    mPaint.setStyle(Paint.Style.STROKE);    // 设置paint的外框宽度    mPaint.setStrokeWidth(model == ControlView.MODEL_ERASE ? view.getEraserWidth() : view.getPenWidth());    // 获取跟清晰的图像采样    mPaint.setDither(true);    // 接合处为圆弧    mPaint.setStrokeJoin(Paint.Join.ROUND);    // 画笔样式圆形    mPaint.setStrokeCap(Paint.Cap.ROUND);  }}

 

  ①曲线

 

public class Path extends AbsShape {  private Rect mInvalidRect;  protected SerializablePath mPath;  private float mStartX = 0;  private float mStartY = 0;  public Path(ControlView view, int model) {    super(view, model);    mPath = new SerializablePath();    mInvalidRect = new Rect();  }  public void touchMove(int startX, int startY, int x, int y) {    // 判断是不是down事件    if (mStartX == 0 && mStartY == 0) {      mStartX = startX;      mStartY = startY;      mPath.moveTo(mStartX, mStartY);    }    int border = (int) mPaint.getStrokeWidth();    mInvalidRect.set((int) mStartX - border, (int) mStartY - border,        (int) mStartX + border, (int) mStartY + border);    float mCurveEndX = (x + mStartX) / 2;    float mCurveEndY = (y + mStartY) / 2;    // 使用贝赛尔曲线    mPath.quadTo(mStartX, mStartY, mCurveEndX, mCurveEndY);    mInvalidRect.union((int) mCurveEndX - border,        (int) mCurveEndY - border, (int) mCurveEndX + border,        (int) mCurveEndY + border);    mStartX = x;    mStartY = y;    mView.invalidate(mInvalidRect);  }  public void drawShape(Canvas canvas) {    canvas.drawPath(mPath, mPaint);  }}

 

  ②直线

  

public class Line extends AbsShape{  private Rect mInvalidRect;  protected SerializablePath mPath;  public Line(ControlView view, int model)  {    super(view, model);    mPath = new SerializablePath();    mInvalidRect = new Rect();  }  public void touchMove(int startX, int startY, int x, int y)  {    mPath.reset();    mPath.moveTo(startX, startY);    mPath.lineTo(x, y);    int border = (int) mPaint.getStrokeWidth() * 2;    mInvalidRect.set((int) startX - border, (int) startY - border, (int) startX + border, (int) startY + border);    mInvalidRect.union((int) x - border, (int) y - border, (int) x + border, (int) y + border);    mView.invalidate(mInvalidRect);  }  public void drawShape(Canvas canvas)  {    canvas.drawPath(mPath, mPaint);  }}

 

 

 

  ③矩形

 

 

public class Rectangle extends AbsShape{  private int mX;  private int mY;  private int mStartX;  private int mStartY;  private Rect mInvalidRect;  public Rectangle(ControlView view, int model)  {    super(view, model);    mInvalidRect = new Rect();  }  public void touchMove(int startX, int startY, int x, int y)  {    mStartX = startX;    mStartY = startY;    mX = x;    mY = y;    int border = (int) mPaint.getStrokeWidth();    mInvalidRect.set(mStartX - border, mStartY - border, mStartX + border, mStartY + border);    mInvalidRect.union(x - border, y - border, x + border, y + border);    mView.invalidate(mInvalidRect);  }  public void drawShape(Canvas canvas)  {    canvas.drawRect(mStartX, mStartY, mX, mY, mPaint);  }}

 

 

 

  ④正方形

  

public class Square extends AbsShape {  private int mStartX;  private int mStartY;  private int mX;  private int mY;  private Rect mInvalidRect;  public Square(ControlView view, int model) {    super(view, model);    mInvalidRect = new Rect();  }  public void touchMove(int startX, int startY, int x, int y) {    mStartX = startX;    mStartY = startY;    mX = x;    mY = y;    int border = (int) mPaint.getStrokeWidth();    mInvalidRect.set(mStartX - border, mStartY - border, mStartX + border,        mStartY + border);    mInvalidRect.union(x - border, y - border, x + border, y + border);    mView.invalidate(mInvalidRect);  }  public void drawShape(Canvas canvas) {    if ((mX > mStartX && mY > mStartY) || (mX < mStartX && mY < mStartY)) {      if (Math.abs(mX - mStartX) > Math.abs(mY - mStartY)) {        canvas.drawRect(mStartX, mStartY, mStartX + mY - mStartY, mY,            mPaint);      } else {        canvas.drawRect(mStartX, mStartY, mX, mStartY + mX - mStartX,            mPaint);      }    } else {      if (Math.abs(mX - mStartX) > Math.abs(mY - mStartY)) {        canvas.drawRect(mStartX, mStartY, mStartX + mStartY - mY, mY,            mPaint);      } else {        canvas.drawRect(mStartX, mStartY, mX, mStartY + mStartX - mX,            mPaint);      }    }  }}

 

 

 

  ⑤椭圆

  

public class Oval extends AbsShape{  private Rect mInvalidRect;  private RectF mDrawRect;  public Oval(ControlView view, int model)  {    super(view, model);    mInvalidRect = new Rect();    mDrawRect = new RectF();  }  public void touchMove(int startX, int startY, int x, int y)  {    int border = (int) mPaint.getStrokeWidth();    mInvalidRect.set(startX - border, startY - border, startX + border, startY + border);    mInvalidRect.union(x - border, y - border, x + border, y + border);    mDrawRect.set(startX, startY, x, y);    mView.invalidate(mInvalidRect);  }  public void drawShape(Canvas canvas)  {    canvas.drawOval(mDrawRect, mPaint);  }}

 

  ⑥圆

  

public class Circle extends AbsShape{  private Rect mInvalidRect;  private int cx;  private int cy;  private int radius;  public Circle(ControlView view, int model)  {    super(view, model);    mInvalidRect = new Rect();  }  public void touchMove(int startX, int startY, int x, int y)  {    cx = (int) ((x + startX) / 2);    cy = (int) ((y + startY) / 2);    radius = (int) Math.sqrt(Math.pow(x - startX, 2) + Math.pow(y - startY, 2)) / 2;    int border = (int) mPaint.getStrokeWidth();    mInvalidRect.set(cx - radius - border, cy - radius - border, cx + radius + border, cy + radius + border);    mView.invalidate(mInvalidRect);  }  public void drawShape(Canvas canvas)  {    canvas.drawCircle(cx, cy, radius, mPaint);  }}

 

  曲线和直线的SerializablePath:

  

public class SerializablePath extends Path implements Serializable{  private static final long serialVersionUID = 1L;  private ArrayList<float[]> pathPoints;  public SerializablePath()  {    super();    pathPoints = new ArrayList<float[]>();  }  public SerializablePath(SerializablePath path)  {    super(path);    pathPoints = path.pathPoints;  }  public void addPathPoints(float[] points)  {    pathPoints.add(points);  }  public void loadPathPointsAsQuadTo()  {    float[] initPoints = pathPoints.remove(0);    moveTo(initPoints[0], initPoints[1]);    for (float[] pointSet : pathPoints)    {      quadTo(pointSet[0], pointSet[1], pointSet[2], pointSet[3]);    }  }}

 

四.核心操作代码

  主界面WhiteBoardActivity.java

  

public class WhiteBoardActivity extends Activity {  private PopupWindow mPopupWindow;  private LayoutInflater mLayoutInflater;  private View mToolbox;  private ControlView drawView;  private ImageView mColorBlack;  private ImageView mColorBlue;  private ImageView mColorGreen;  private ImageView mColorRed;  private ImageView mColorYellow;  private ImageView mColor;  private ImageView mEdit;  private ImageView mEraser;  private ImageView mClear;  private ImageView mSettings;  private ImageView mShape;  View width1;  View width2;  View width3;  // 获取屏幕尺寸  DisplayMetrics dm;  @SuppressWarnings("deprecation")  @Override  public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    // 保持屏幕常亮    getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,        WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);    setContentView(R.layout.activity_ewhiteboard);    dm = new DisplayMetrics();    getWindowManager().getDefaultDisplay().getMetrics(dm);    int screenwWidth = dm.widthPixels; // 屏幕宽(dip,如:720dip)    int screenHeight = dm.heightPixels; // 屏幕宽(dip,如:1280dip)    drawView = new ControlView(this, screenwWidth, screenHeight);    ((ViewGroup) findViewById(R.id.container)).addView(drawView);    drawView.setPenColor(Color.BLACK);    drawView.setPenWidth(3f);    mLayoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);    mPopupWindow = new PopupWindow(new View(this));    mPopupWindow.setFocusable(true);    mPopupWindow.setBackgroundDrawable(new BitmapDrawable());    mToolbox = findViewById(R.id.toolbox);    mSettings = (ImageView) findViewById(R.id.settings);    mColor = (ImageView) findViewById(R.id.color);    mEdit = (ImageView) findViewById(R.id.edit);    mEraser = (ImageView) findViewById(R.id.eraser);    mClear = (ImageView) findViewById(R.id.clear);    mShape = (ImageView) findViewById(R.id.shape);    initToolbox();    initColor();    initPenSize();    initEraserSize();    initShape();  }  private void initToolbox() {    mEdit.setSelected(true);    drawView.setModel(ControlView.MODEL_NORMAL);    mClear.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        new AlertDialog.Builder(WhiteBoardActivity.this)            .setMessage("确定要清除白板吗?")            .setPositiveButton("确定",                new DialogInterface.OnClickListener() {                  public void onClick(DialogInterface dialog,                      int which) {                    drawView.clear();                  }                })            .setNegativeButton("取消",                new DialogInterface.OnClickListener() {                  public void onClick(DialogInterface dialog,                      int which) {                  }                }).create().show();      }    });    mSettings.setSelected(false);    mSettings.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        Intent intent = new Intent();        intent.setClass(WhiteBoardActivity.this, SettingsActivity.class);        startActivity(intent);      }    });  }  private void initShape() {    final int POP_WINDOW_WIDTH = WindowManager.LayoutParams.WRAP_CONTENT;    final int POP_WINDOW_HEIGHT = Utils.dip2px(this, 60);    final View popupView = mLayoutInflater.inflate(        R.layout.view_popup_shape, null);    final View width1 = popupView.findViewById(R.id.pen_width1);    final View width2 = popupView.findViewById(R.id.pen_width2);    final View width3 = popupView.findViewById(R.id.pen_width3);    final View width4 = popupView.findViewById(R.id.pen_width4);    final View width5 = popupView.findViewById(R.id.pen_width5);    final View width6 = popupView.findViewById(R.id.pen_width6);    mShape.setOnClickListener(new View.OnClickListener() {      public void onClick(View paramView) {        mPopupWindow.setContentView(popupView);        mPopupWindow.setWidth(POP_WINDOW_WIDTH);        mPopupWindow.setHeight(POP_WINDOW_HEIGHT);        mPopupWindow.setAnimationStyle(R.style.pop_settings);        mPopupWindow.showAtLocation(            mShape,            Gravity.LEFT | Gravity.TOP,            mToolbox.getRight(),            mToolbox.getTop() + mShape.getTop()                - Utils.dip2px(WhiteBoardActivity.this, 5));        int penSize = drawView.getShap();        if (penSize == ControlView.SHAPE_PATH) {          width1.setSelected(true);          width2.setSelected(false);          width3.setSelected(false);          width4.setSelected(false);          width5.setSelected(false);          width6.setSelected(false);        } else if (penSize == ControlView.SHAPE_LINE) {          width2.setSelected(true);          width1.setSelected(false);          width3.setSelected(false);          width4.setSelected(false);          width5.setSelected(false);          width6.setSelected(false);        } else if (penSize == ControlView.SHAPE_CIRCLE) {          width3.setSelected(true);          width1.setSelected(false);          width2.setSelected(false);          width4.setSelected(false);          width5.setSelected(false);          width6.setSelected(false);        } else if (penSize == ControlView.SHAPE_SQUARE) {          width4.setSelected(true);          width1.setSelected(false);          width2.setSelected(false);          width3.setSelected(false);          width5.setSelected(false);          width6.setSelected(false);        } else if (penSize == ControlView.SHAPE_RECT) {          width5.setSelected(true);          width1.setSelected(false);          width2.setSelected(false);          width3.setSelected(false);          width4.setSelected(false);          width6.setSelected(false);        } else if (penSize == ControlView.SHAPE_OVAL) {          width6.setSelected(true);          width1.setSelected(false);          width2.setSelected(false);          width3.setSelected(false);          width4.setSelected(false);          width5.setSelected(false);        }      }    });    width1.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setShape(ControlView.SHAPE_PATH);        mPopupWindow.dismiss();        mShape.setImageResource(R.drawable.ic_path);      }    });    width2.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setShape(ControlView.SHAPE_LINE);        mPopupWindow.dismiss();        mShape.setImageResource(R.drawable.ic_line);      }    });    width3.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setShape(ControlView.SHAPE_CIRCLE);        mPopupWindow.dismiss();        mShape.setImageResource(R.drawable.ic_circle);      }    });    width4.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setShape(ControlView.SHAPE_SQUARE);        mPopupWindow.dismiss();        mShape.setImageResource(R.drawable.ic_square);      }    });    width5.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setShape(ControlView.SHAPE_RECT);        mPopupWindow.dismiss();        mShape.setImageResource(R.drawable.ic_rect);      }    });    width6.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setShape(ControlView.SHAPE_OVAL);        mPopupWindow.dismiss();        mShape.setImageResource(R.drawable.ic_oval);      }    });  }  private void initColor() {    final int POP_WINDOW_WIDTH = WindowManager.LayoutParams.WRAP_CONTENT;    final int POP_WINDOW_HEIGHT = Utils.dip2px(this, 60);    final View popupView = mLayoutInflater.inflate(        R.layout.view_color_popup, null);    final View colorFrame = findViewById(R.id.color_frame);    colorFrame.setOnClickListener(new View.OnClickListener() {      public void onClick(View paramView) {        mPopupWindow.setContentView(popupView);        mPopupWindow.setWidth(POP_WINDOW_WIDTH);        mPopupWindow.setHeight(POP_WINDOW_HEIGHT);        mPopupWindow.setAnimationStyle(R.style.pop_settings);        mPopupWindow.showAtLocation(            colorFrame,            Gravity.LEFT | Gravity.TOP,            mToolbox.getRight(),            mToolbox.getTop() + colorFrame.getTop()                - Utils.dip2px(WhiteBoardActivity.this, 5));      }    });    mColor.setBackgroundColor(Color.BLACK);    mColorBlack = (ImageView) popupView.findViewById(R.id.color_black);    mColorBlack.setBackgroundColor(Color.BLACK);    mColorBlue = (ImageView) popupView.findViewById(R.id.color_blue);    mColorBlue.setBackgroundColor(Color.BLUE);    mColorGreen = (ImageView) popupView.findViewById(R.id.color_green);    mColorGreen.setBackgroundColor(Color.GREEN);    mColorRed = (ImageView) popupView.findViewById(R.id.color_red);    mColorRed.setBackgroundColor(Color.RED);    mColorYellow = (ImageView) popupView.findViewById(R.id.color_yellow);    mColorYellow.setBackgroundColor(Color.YELLOW);    mColorBlack.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        mColor.setBackgroundColor(Color.BLACK);        drawView.setPenColor(Color.BLACK);        mPopupWindow.dismiss();      }    });    mColorBlue.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        mColor.setBackgroundColor(Color.BLUE);        drawView.setPenColor(Color.BLUE);        mPopupWindow.dismiss();      }    });    mColorGreen.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        mColor.setBackgroundColor(Color.GREEN);        drawView.setPenColor(Color.GREEN);        mPopupWindow.dismiss();      }    });    mColorRed.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        mColor.setBackgroundColor(Color.RED);        drawView.setPenColor(Color.RED);        mPopupWindow.dismiss();      }    });    mColorYellow.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        mColor.setBackgroundColor(Color.YELLOW);        drawView.setPenColor(Color.YELLOW);        mPopupWindow.dismiss();      }    });  }  private void initPenSize() {    final int POP_WINDOW_WIDTH = WindowManager.LayoutParams.WRAP_CONTENT;    final int POP_WINDOW_HEIGHT = Utils.dip2px(this, 60);    final View popupView = mLayoutInflater.inflate(R.layout.view_popup_pen,        null);    final View width1 = popupView.findViewById(R.id.pen_width1);    final View width2 = popupView.findViewById(R.id.pen_width2);    final View width3 = popupView.findViewById(R.id.pen_width3);    mEdit.setOnClickListener(new View.OnClickListener() {      public void onClick(View paramView) {        mEdit.setSelected(true);        mEraser.setSelected(false);        drawView.setModel(ControlView.MODEL_NORMAL);        mPopupWindow.setContentView(popupView);        mPopupWindow.setWidth(POP_WINDOW_WIDTH);        mPopupWindow.setHeight(POP_WINDOW_HEIGHT);        mPopupWindow.setAnimationStyle(R.style.pop_settings);        mPopupWindow.showAtLocation(mEdit, Gravity.LEFT | Gravity.TOP,            mToolbox.getRight(), mToolbox.getTop() + mEdit.getTop()                - Utils.dip2px(WhiteBoardActivity.this, 5));        float penSize = drawView.getPenWidth();        if (penSize == 1) {          width1.setSelected(true);          width2.setSelected(false);          width3.setSelected(false);        } else if (penSize == 3) {          width2.setSelected(true);          width1.setSelected(false);          width3.setSelected(false);        } else if (penSize == 6) {          width3.setSelected(true);          width1.setSelected(false);          width2.setSelected(false);        }      }    });    width1.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setPenWidth(1f);        mPopupWindow.dismiss();      }    });    width2.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setPenWidth(3f);        mPopupWindow.dismiss();      }    });    width3.setOnClickListener(new View.OnClickListener() {      public void onClick(View v) {        drawView.setPenWidth(6f);        mPopupWindow.dismiss();      }    });  }  private void initEraserSize() {    final View popupView = mLayoutInflater.inflate(        R.layout.view_popup_eraser, null);    width1 = popupView.findViewById(R.id.pen_width1);    width2 = popupView.findViewById(R.id.pen_width2);    width3 = popupView.findViewById(R.id.pen_width3);    mEraser.setOnClickListener(new View.OnClickListener() {      public void onClick(View paramView) {        mEdit.setSelected(false);        mEraser.setSelected(true);        drawView.setModel(ControlView.MODEL_ERASE);        // 默认30        drawView.setEraserWidth(30f);      }    });      }  @Override  protected void onResume() {    super.onResume();    SharedPreferences pref = PreferenceManager        .getDefaultSharedPreferences(this);    drawView.setOnlyPenInput(pref.getBoolean("only_pen_input", false));  }  @Override  public void onDestroy() {    super.onDestroy();  }}

 

 

  操作类ControlView.java

  

public class ControlView extends ViewGroup {  public static final int MODEL_NORMAL = 1;  public static final int MODEL_ERASE = 2;  public static String pointmsg;  protected int mModel = MODEL_NORMAL;  public static final int SHAPE_PATH = 1;  public static final int SHAPE_LINE = 2;  public static final int SHAPE_CIRCLE = 3;  public static final int SHAPE_SQUARE = 4;  public static final int SHAPE_RECT = 5;  public static final int SHAPE_OVAL = 6;  public static final int IMAGE = 8;  private int mShapeType = SHAPE_PATH;  public static final float scale = 210f / 297f;  private Context mContext;  public Hardware mHardware;  public boolean mOnlyPenInput;  protected Canvas mCanvas;  protected Bitmap mBitmap;  protected float mPenWidth = 3f;  protected float mEraserWidth = 10f;  protected int mColor = Color.BLACK;  private Paint mBitmapPaint;  private IShape mEraser;  private IShape mShape;  private int mStartX = 0;  private int mStartY = 0;  private int x = 0, y = 0;  protected boolean mIsCanvasMoving;  private boolean mIsTouchUp;  int index = 1;  int width = 0, height = 0; // 屏幕的宽高  public ControlView(Context context, int width, int height) {    super(context);    setWillNotDraw(false);    mContext = context;    mHardware = Hardware.getInstance(mContext);    this.width = width;    this.height = height;    mHardware.addPenButtonEvent(this);    mBitmapPaint = new Paint(Paint.DITHER_FLAG);    mBitmap = MainApplication.getBitmap();    mCanvas = new Canvas(mBitmap);  }  @Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {    for (int i = 0; i < getChildCount(); i++) {      getChildAt(i).layout(l, t, r, b);    }  }  @Override  protected void onDraw(Canvas canvas) {    // 在View上绘制    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);    if (mShape != null && !mIsTouchUp) {      mShape.drawShape(canvas);    }    }  @Override  public boolean onTouchEvent(MotionEvent event) {    if (mOnlyPenInput) {      if (mHardware.hasPenDigitizer() && mHardware.isPenEvent(event)) {        return touchEvent(event);      } else {        return false;      }    } else {      return touchEvent(event);    }  }  private boolean touchEvent(MotionEvent event) {    if ((mHardware.hasPenDigitizer() && mHardware.isPenEvent(event))        || !mOnlyPenInput) {      // 笔的按钮只有在move才能检测到,所以有可能在move时改变形状      // 所以所有的绘画都在move中去做      if (mModel == MODEL_ERASE || mHardware.isPenButtonPressed(event)) {        mShape = mEraser;      }      switch (event.getAction()) {      case MotionEvent.ACTION_DOWN:        mStartX = (int) event.getX();        mStartY = (int) event.getY();        // 创建一个橡皮        mEraser = new Path(this, MODEL_ERASE);        // 根据选择创建相应图形        switch (mShapeType) {        case SHAPE_PATH: // 随意曲线          mShape = new Path(this, MODEL_NORMAL);          break;        case SHAPE_LINE:// 直线          mShape = new Line(this, MODEL_NORMAL);          break;        case SHAPE_CIRCLE:// 圆          mShape = new Circle(this, MODEL_NORMAL);          break;        case SHAPE_SQUARE:// 正方形          mShape = new Square(this, MODEL_NORMAL);          break;        case SHAPE_RECT:// 矩形          mShape = new Rectangle(this, MODEL_NORMAL);          break;        case SHAPE_OVAL:// 椭圆形          mShape = new Oval(this, MODEL_NORMAL);          break;        default:          break;        }        return true;      case MotionEvent.ACTION_MOVE:        x = (int) event.getX();        y = (int) event.getY();        // 进入拖动模式        if (event.getPointerCount() >= 2) {          mIsCanvasMoving = true;          resetView();          return false;        }        // 虽然只有一个触摸点,但是没恢复绘画模式        if (!mIsCanvasMoving) {          // 是否结束绘画          mIsTouchUp = false;          mShape.touchMove(mStartX, mStartY, x, y);        }        return true;      case MotionEvent.ACTION_UP:        if (!mIsCanvasMoving) {          mIsTouchUp = true;          mShape.drawShape(mCanvas);          // 更新画布          invalidate();        } else {          // 恢复绘画模式          mIsCanvasMoving = false;        }        return true;      }    }    return false;  }  @Override  public boolean onKeyDown(int keyCode, KeyEvent event) {    if (mHardware.onKeyDown(keyCode, event)) {      return true;    }    return super.onKeyDown(keyCode, event);  }  @Override  public boolean onKeyUp(int keyCode, KeyEvent event) {    if (mHardware.onKeyUp(keyCode, event)) {      return true;    }    return super.onKeyUp(keyCode, event);  }  public void resetView() {    mIsTouchUp = true;    invalidate();  }  public void clear() {    restoreBitmap();    invalidate();  }  public void setPenWidth(float penWidth) {    mPenWidth = penWidth;  }  public float getPenWidth() {    return mPenWidth;  }  public void setEraserWidth(float penWidth) {    mEraserWidth = penWidth;  }  public float getEraserWidth() {    return mEraserWidth;  }  public void setPenColor(int color) {    mColor = color;  }  public int getPenColor() {    return mColor;  }  public void setModel(int model) {    mModel = model;  }  public void setShape(int shape) {    mShapeType = shape;  }  public int getShap() {    return mShapeType;  }  public boolean useForWriting(MotionEvent event) {    return !mOnlyPenInput || mHardware.isPenEvent(event);  }  public boolean useForTouch(MotionEvent event) {    return !mOnlyPenInput        || (mOnlyPenInput && !mHardware.isPenEvent(event));  }  private void restoreBitmap() {    mBitmap.eraseColor(Constants.BACK_COLOR);    mCanvas = new Canvas(mBitmap);  }  public void setOnlyPenInput(boolean b) {    mOnlyPenInput = b;  }}

 

 

  MainApplication类

 

  

public class MainApplication extends Application {  public static final float scale = 210f / 297f;  public static int CANVAS_WIDTH;  public static int CANVAS_HEIGHT;  public static int SCREEN_WIDTH;  public static int SCREEN_HEIGHT;  private static Bitmap curBitmap;  @SuppressWarnings("deprecation")  @Override  public void onCreate() {    super.onCreate();    Display display = ((WindowManager) getSystemService("window"))        .getDefaultDisplay();    if (display.getWidth() < display.getHeight()) {      SCREEN_HEIGHT = display.getWidth();      SCREEN_WIDTH = display.getHeight();    } else {      SCREEN_WIDTH = display.getWidth();      SCREEN_HEIGHT = display.getHeight();    }    CANVAS_WIDTH = MainApplication.SCREEN_WIDTH;    CANVAS_HEIGHT = (int) (MainApplication.SCREEN_WIDTH / scale);    createPage();  }  private static Bitmap createBitmap() {    Bitmap bitmap = Bitmap.createBitmap(CANVAS_WIDTH, CANVAS_HEIGHT,        Bitmap.Config.ARGB_8888);    bitmap.eraseColor(Constants.BACK_COLOR);    return bitmap;  }  public static Bitmap getBitmap() {    return curBitmap;  }  public static void createPage() {    curBitmap = createBitmap();  }  @Override  public void onTerminate() {    super.onTerminate();    curBitmap.recycle();    curBitmap = null;  }}

  

  下面是效果图:

 

 

 

 

 

  好了,以上就是这个白板的主要功能和核心代码,有任何好的建议或者不明白的可以在下方评论。

  源码就不上传了,其余就是一些布局和图片。