Giter Site home page Giter Site logo

drawingboard's Introduction

Android画板

之前见到一个需求,在视频通话时,希望能将手指在屏幕上绘制的图形实时发送给对方,为了实现这个需求,在github转了一圈,发现一个名叫JustWeTools的开源项目,借鉴这个项目中画板的**,经过一番思考,实现了一个功能比较完善的画板。

功能点:

  1. 画笔粗细颜色可调,橡皮粗细可调。
  2. 可无限undorodo
  3. 能将绘制的图形保存位图片
  4. 可以记录绘画过程,能够以动画形式播放,也能够以文件形式保存。

基本思路:

继承View类,重写onTouch()onDraw()方法。通过监听touch事件在View上绘制轨迹。

	@Override
    public boolean onTouchEvent(MotionEvent event) {
        if(isPlaying) return true;
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN :
                touchDown(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE :
                touchMove(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP :
                touchUp(x, y);
                invalidate();
                break;
        }
        return true;
    }

其中每次都调用invalidate()方法,通知其自身重绘,将记录的轨迹绘制在View中。

接下来看touchDown()touchMove(), touchUp()三个方法:

	private void touchDown(float x, float y) {
        undoNodes.clear();
        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
        recordNode(x, y, MotionEvent.ACTION_DOWN);
    }
	private void touchMove(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= 
            TOUCH_TOLERANCE) {
            mPath.quadTo(mX, mY, (x + mX) / 2,
                         (y + mY) / 2);
            mX = x;
            mY = y;
            recordNode(x, y, MotionEvent.ACTION_MOVE);
        }
    }
	private void touchUp(float x, float y) {
        mPath.lineTo(mX, mY);
        if (isPainting) {
            mCanvas.drawPath(mPath, mPaint);
        } else {
            mCanvas.drawPath(mPath, mEraserPaint);
        }
        mPath.reset();
        recordNode(x, y, MotionEvent.ACTION_UP);
    }

一次完整的touch事件以Action_Down开始,经过一段Action_Move,以Action_Up结束。这里使用一个全局的Path实例mPath来记录touch的轨迹,手指移动时,将轨迹记录在mPath中,以invalidate()被调用后,nDraw()会将这段轨迹绘制在屏幕上。当touch事件结束时,将已经画好的一笔记录在一个缓存的Bitmap中(这里的mCanvas是从一个全局的Bitmap实例即mcanvas创建的),并清空mPath,为下一笔作准备。在之后的onDraw()中,同样会将Bitmap中缓存的图案绘制出来。

	protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //先将已保存在位图中的轨迹绘制到背景
        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
        //再绘制新的轨迹
        if(isPainting) {
            canvas.drawPath(mPath, mPaint);
        } else {
            canvas.drawPath(mPath, mEraserPaint);
        }
    }

此外,所有的轨迹点都经由recordNode()方法记录在了一个全局的列表pathNodes中,undo时,将pathNodes中最新记录的完整的一笔(即自down始,至up终的一系列点)移除,放到另一列表undoNodes中,再绘制pathNode中所有的轨迹。而redo则将undoNodes中的轨迹放回pathNodes。在recordNode()记录轨迹点时,给每一个点打上一个时间戳,此后如果在绘制pathNodes时,在两点绘制之间停顿两点时间戳之差的时间,就能以动画的形式完全的还原整个pathNodes的绘制过程了。

下面是实现播放pathNodes的过程

class PlayerRunnable implements Runnable  {

  private List<Node> nodes;

  public PlayerRunnable(List<Node> nodes) {
    this.nodes = nodes;
  }
  @Override
  public void run() {
    isPlaying = true;
    //保存画板状态
    int originPaintColor = getPaintColor();
    int originPaintWidth = getPaintWidth();
    int originEraserWidth = getEraserWidth();
    boolean originPaintStatus = isPainting();
    long time = 0;
    for(int i = 0; i < nodes.size(); i++) {
      Node node = nodes.get(i);
      if(i < nodes.size() - 1) {
        time = nodes.get(i + 1).timeStamp - 
          node.timeStamp;
        //当两次操作时间间隔太长时将其缩短为一秒
        if(time > 1000) time = 1000;
      }
      //清屏的动作
      if(node.touchEvent == -1) {
        mHandler.sendEmptyMessage(CLEAR);
      }
      if(node.touchEvent == MotionEvent.ACTION_DOWN) {
        setPainting(node.isPainting);
        if(node.isPainting) {
          setPaintColor(node.paintColor);
          setPaintWidth(node.paintWidth);
        } else {
          setEraserWidth(node.paintWidth);
        }
        touchDown(node.x, node.y);
        mHandler.sendEmptyMessage(INVALIDATE);
      }
      if(node.touchEvent == MotionEvent.ACTION_MOVE) {
        touchMove(node.x, node.y);
        mHandler.sendEmptyMessage(INVALIDATE);
      }
      if(node.touchEvent == MotionEvent.ACTION_UP) {
        touchUp(node.x, node.y);
        mHandler.sendEmptyMessage(INVALIDATE);
      }
      try {
        Thread.sleep(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    //还原画板状态
    setPaintColor(originPaintColor);
    setPaintWidth(originPaintWidth);
    setEraserWidth(originEraserWidth);
    setPainting(originPaintStatus);
    isPlaying = false;
  }
}

/**
 * 播放动画
 * @param nodes 轨迹记录
 * @param isAppending 动画内容是否添加在已有的轨迹后面
 */
public void play(List<Node> nodes, boolean isAppending) {
  initBackground();
  if (mBitmap == null) return;
  if(!isAppending) {
    pathNodes.clear();
  }
  executor.execute(new PlayerRunnable(nodes));
}

drawingboard's People

Contributors

yangkx1024 avatar

Stargazers

Jue avatar  avatar  avatar xwhy avatar  avatar  avatar  avatar glumes avatar Frank Neal avatar grace avatar lynn8570 avatar  avatar 有事没事 avatar  avatar  avatar ThomasQin avatar

Watchers

 avatar  avatar

drawingboard's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.