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;
    }

}

  

  下面是效果图:

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

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

时间: 2024-10-12 23:20:16

Android电子白板实现,Canvas画布的相关文章

Android自定义万能Canvas画布

一.需求: 1.在自定义的画布中实现可缩放手势,摇一摇可对控件进行整理排序: 2.画布中可以添加位置设定的控件,控件可以响应点击.长按.拖动事件: 3.控件A长按事件会隐藏画布中的控件除了A之外,显示另一个控件B:当A在在底层画布中拖动,拖动结束之后回到原画布:当A移动B的位置范围响应操作(可以添加另方面功能). 二.实现思想: 1.画布的的手势缩放.控件的添加,在我的上一篇关于画布文章中已经实现了这个功能,这里不再赘述: 2.要实现上述的几个功能只需要屏幕上添加两层画布,一层画布用于添加控件在

【读书笔记《Android游戏编程之从零开始》】12.游戏开发基础(Canvas 画布)

1.Canvas 画布 画布类 Canvas 封装了图形和图片绘制等内容,此类常用的函数说明如下: drawColor(int color) 作用:绘制颜色覆盖画布,常用于刷屏 参数:颜色值,也可用十六进制形式表示(ARGB) drawText(String text,float x,float y,Paint paint) 作用:绘制文本字符 第一个参数:文本内容 第二.三个参数:文本的X.Y坐标 第四个参数:画笔实例 drawPoint(float x,float y,Paint paint

第六章 一张白纸好作画—Canvas画布(2)

6.2 Canvas常用绘制方法 前面一节我们了解到如果创建一个画布,接下来我们就将要在这个画布上进行绘制.Android SDK的Canvas类中包含了一系列用于绘制的方法,方法分为3种类型,下面简单介绍这些常用的绘制方法. 1)Canvas类的几何图形(Geometry)方面的方法用于绘制点.绘制线.绘制矩形.绘制圆弧等. 其中一些主要的方法如表6-1所示: 方法 返回值 说明 drawARGB(int a, int r, int g, int b) void 将整体填充为某种颜色 draw

Android 中的Canvas画图

Android中有一个Canvas类,Canvas类就是表示一块画布,你可以在上面画你想画的东西.当然,你还可以设置画布的属性,如画布的颜色/尺寸等.Canvas提供了如下一些方法: Canvas():创建一个空的画布,可以使用setBitmap()方法来设置绘制的具体画布: Canvas(Bitmapbitmap):以bitmap对象创建一个画布,则将内容都绘制在bitmap上,bitmap不得为null; Canvas(GLgl):在绘制3D效果时使用,与OpenGL有关: drawColo

android 绘图之Canvas,Paint类

Canvas,Paint 1.在android 绘图但中经常要用到Canvas和Paint类,Canvas好比是一张画布,上面已经有你想绘制图画的轮廓了,而Paint就好比是画笔,就要给Canvas进行添色等操作. 这两个类通常都是在onDraw(Canvas canvas)方法中用的. 2.Bitmap:代表一张位图,BitmapDrawable里封装的突变就是一个Bitmao对象 3.Canvas里面有一些例如: drawArc(参数) 绘制弧 drawBitmao(Bitmap bitma

Android 绘图(二) Canvas

上篇文章,我们讲述了Paint(画笔)类的.如果你还未了解,那么可以先看看这篇文章,Android 绘图(一) Paint.今天这篇文章,我们来看看Canvas.Canvas 是画布,来响应绘画(Draw)的调用(并将其写入Btmap). 我们先看看官方文档对Canvas的描述: The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hol

第六章 一张白纸好作画—Canvas画布(1)

第六章 一张白纸好作画-Canvas画布 前面的相关章节,我们详细说明过Android UI组件的使用.通过前面章节的学习,开发者已经可以开发出令人满意的UI效果了.但是有的时候,我们需要实现更加漂亮的UI效果,此时可能就无法直接使用UI组件,而是需要自己画出各种UI效果了. 在Android中,Canvas就是一个画布,开发者可以在画布上绘制想要的任何东西.在本章中,我们将介绍Canvas及相关的技术. 6.1 Canvas画布介绍 6.1.1View Canvas-使用普通View的Canv

将Canvas画布内容保存为jpg格式图片

/// <summary>        /// 功能:将Canvas画布内容保存为jpg格式图片        /// 作者:GYS | 日期:2013年9月9日        /// </summary>        /// <param name="filePath"></param>        /// <param name="pixBox"></param>        pri

如何在html5的canvas画布中绘制gif动态图片

我们都知道如何在html5的canvas画布上绘制静态图片(jpeg, png等),直接用canvas中的drawImage方法即可,那么如何绘制动态图片(gif)? 相信大家都知道动态图片之所以动态,是因为它是由很多图片按一定的帧数顺序播放而成的,因此我们是否也可以模拟这样的帧数,每隔一定的时间重新 绘制图片,就能画出动态图片呢?答案当然是YES.下面就是我自己测试的一个例子,代码如下: 1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 &l

用canvas画布画一个画板

前段时间,在对H5的回顾中突然对canvas有了感觉,闲来无事便对其进行了一些捯饬.这不,上周我还做了一个好玩的画板呢,废话不多说,直接上代码(PS:翠花,上代码~): HTML部分: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Canvas 制作画板</title> <style> h1