【问题标题】:android canvas remove previous path being drawnandroid canvas删除上一个正在绘制的路径
【发布时间】:2013-01-28 16:26:16
【问题描述】:

我正在制作一个绘图应用程序,并想实现一个撤消功能来删除前一个绘制的路径。

编码:

private HashMap<Integer, Path> pathMap; // current Paths being drawn
private HashMap<Integer, Point> previousPointMap; // current Points
private Bitmap bitmap; // drawing area for display or saving
private Canvas bitmapCanvas; // used to draw on bitmap
private Paint paintScreen; // use to draw bitmap onto screen
private Paint paintLine; // used to draw lines onto bitmap

public DrawView(Context context, AttributeSet attrs) 
{
     super(context, attrs); // pass context to View's constructor
     this.context_new=context;

      paintScreen = new Paint(); // used to display bitmap onto screen

      // set the initial display settings for the painted line
      paintLine = new Paint();
      paintLine.setAntiAlias(true); // smooth edges of drawn line
      paintLine.setColor(Color.BLACK); // default color is black
      paintLine.setStyle(Paint.Style.STROKE); // solid line
      paintLine.setStrokeWidth(5); // set the default line width
      paintLine.setStrokeCap(Paint.Cap.ROUND); // rounded line ends
      pathMap = new HashMap<Integer, Path>();
      previousPointMap = new HashMap<Integer, Point>();
} // end DrawView constructor

@Override
protected void onDraw(Canvas canvas)  
{
    canvas.drawBitmap(bitmap, 0, 0, paintScreen); 
    for (Integer key : pathMap.keySet()) 
    canvas.drawPath(pathMap.get(key), paintLine);
} 

// called when the user finishes a touch    
   private void touchEnded(int lineID)   
   {
      Path path = pathMap.get(lineID); // get the corresponding Path
      bitmapCanvas.drawPath(path, paintLine); // draw to bitmapCanvas
      path.reset(); // reset the Path
      rememberLineId = lineID;
   } // end method touch_ended

//undo       
   private void undo()
   {
      Path path = pathMap.get(rememberLineId); // get the corresponding Path
      pathMap.remove(rememberLineId);
      bitmapCanvas.clearPath(path, paintLine); 
      path.reset(); // reset the Path
   } 

问题:

不过,好像没有bitmapCanvas.clearPath这个方法?如果是那怎么修改呢?

代码修改:

声明:

private Bitmap bitmap; // drawing area for display or saving
private Canvas bitmapCanvas; // used to draw on bitmap
private Paint paintScreen; // use to draw bitmap onto screen
private Paint paintLine; // used to draw lines onto bitmap
private HashMap<Integer, Path> pathMap; // current Paths being drawn
private HashMap<Integer, Point> previousPointMap; // current Points

private Bitmap bitmapBackup; 

OnSizeChanged

@Override
public void onSizeChanged(int w, int h, int oldW, int oldH)
{ 
   super.onSizeChanged(w, h, oldW, oldH);
   DoodlzViewWidth = w;    
   DoodlzViewHeight = h;

   bitmapBackup = Bitmap.createBitmap(getWidth(), DoodlzViewHeight, Bitmap.Config.ARGB_8888);     
   bitmap =       Bitmap.createBitmap(getWidth(), DoodlzViewHeight, Bitmap.Config.ARGB_8888);

   bitmapCanvas = new Canvas(bitmap);
   bitmap      .eraseColor(Color.WHITE); // erase the BitMap with white 
   bitmapBackup.eraseColor(Color.WHITE); 
} 

FirsttoBackup 方法,会在下面的 TouchedStart 执行时调用

public void firsttobackup()
{ 
   bitmapBackup=bitmap;
       Toast message = Toast.makeText(getContext(), "backuped 123", Toast.LENGTH_SHORT);
   message.show(); //THIS TOAST CAN BE SUCESSFULLY PRESENTED when touching screen starting to draw
} 

OnDraw

@Override
protected void onDraw(Canvas canvas) 
{
canvas.drawBitmap(bitmap, 0, 0, paintScreen); 
    for (Integer key : pathMap.keySet()) 
     canvas.drawPath(pathMap.get(key), paintLine); 

}

OnTouchEvent

@Override
public boolean onTouchEvent(MotionEvent event) 
{        
  int action = event.getActionMasked(); // event type 
  int actionIndex = event.getActionIndex(); // pointer (i.e., finger)

  if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) 
  {
      firsttobackup(); //TOAST CAN SHOW "BACKUP 123"

      touchStarted(event.getX(actionIndex), event.getY(actionIndex), 
        event.getPointerId(actionIndex));
  }

撤消:用户按下撤消按钮将调用此操作

public void undo()
{
  bitmap = bitmapBackup.copy(Bitmap.Config.ARGB_8888, true);
  bitmapCanvas = new Canvas(bitmap);        
}  

问题已修改:

现在使用firsttobackup() 方法,这样在执行OnTouchEvent touchStarted 时bitmapBackup 将设置= bitmap。我在里面放了一个吐司,当用户按下屏幕并开始绘图时,它成功呈现“备份 123”。

当用户点击撤消按钮时,它会调用undo方法,但是现在按下撤消按钮,看不到任何动作...为什么?

【问题讨论】:

  • 没有明确的,因为唯一被画出来的,就是你画的。请显示onDraw。
  • onDraw 部分已添加到问题中...谢谢!

标签: android canvas bitmap


【解决方案1】:

这是一篇旧帖子,但我也在寻找该问题的答案。我对该帖子选择的答案不满意,之后我自己找到了一个。 我实际上认为将完整的位图作为备份在内存方面并不是很好,并且会限制我们可以拥有的撤消步骤的数量。

我相信更好的解决方案是:

在你的类中有一堆路径

private Stack<Path> m_pathHistory = new Stack<Path>();

在你的画布和画笔旁边(跳过初始化):

private Canvas m_drawingCanvas;
private Paint m_paint;

然后每次完成一个笔划(在修饰事件上),添加一个“克隆”路径到撤消历史记录:

m_pathHistory.Push(new Path(currentPath));

这里是撤销功能:

public void Undo()
{
    if(m_pathHistory.Count > 0)
    {
        m_pathHistory.Pop(); // Remove the last path from the history

        m_drawingCanvas.DrawColor(Color.Transparent, PorterDuff.Mode.Clear); // Clear the canvas with a transparent color

        // Draw the paths which are still in the history
        foreach (Path p in m_pathHistory)
        {
            m_drawingCanvas.DrawPath(p, m_paint);
        }
     }
}

存储在内存中的路径比完整的位图小得多,因此我们可以拥有更大的历史记录。

【讨论】:

    【解决方案2】:
    use path.reset()in the MotionEvent.ACTION_DOWN event of OnTouchEvent() method.
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPath.reset();
                invalidate();
                break;
        }
        return true;
    
    }
    

    【讨论】:

    • path.reset() 是我要找的。​​span>
    【解决方案3】:

    如果您使用PorterDuffXfermode,请将视图保存到您的bitmapBackup, 而不是以前的位图

    public void undo (){
        bitmap.eraseColor(getDrawingCacheBackgroundColor());
        mCanvas.drawBitmap(bitmapBackup, 0, 0, null);
        invalidate();
        mPath.reset();
        undofresh=true;
    }
    
    
    private void touch_start(float x, float y) {
        View v1 = this;
        v1.setDrawingCacheEnabled(true);
        this.bitmapBackup = Bitmap.createBitmap(v1.getDrawingCache());
        v1.setDrawingCacheEnabled(false);
    }
    

    【讨论】:

      【解决方案4】:

      我认为最简单的方法是使用 2 个位图(1 个额外的备份位图用于恢复以前的状态)。

      在开始新绘图之前,您需要保存位图的先前状态。

      以下是我将如何修改您的代码:

        private HashMap<Integer, Path> pathMap; // current Paths being drawn
        private HashMap<Integer, Point> previousPointMap; // current Points
        private Bitmap bitmap; // drawing area for display or saving
        private Bitmap bitmapBackup; 
        private Canvas bitmapCanvas; // used to draw on bitmap
        private Canvas bitmapBackupCanvas; 
      
      
        // remember last bitmap before new drawings...    
           private void touchStarted()   
           {
              bitmapBackupCanvas.drawBitmap(bitmap, 0, 0, null);
           } 
        // called when the user finishes a touch    
           private void touchEnded(int lineID)   
           {
              Path path = pathMap.get(lineID); // get the corresponding Path
              bitmapCanvas.drawPath(path, paintLine); // draw to bitmapCanvas
              path.reset(); // reset the Path
              rememberLineId = lineID;
           } // end method touch_ended
      
        //undo       
           private void undo()
           {
              Path path = pathMap.get(rememberLineId); // get the corresponding Path
              pathMap.remove(rememberLineId);
              bitmapCanvas.drawBitmap(bitmapBackup, 0, 0, null); // restore from backup
              path.reset(); // reset the Path
           } 
      

      【讨论】:

      • 是的,我也想过这种方式。这无疑是最简单最清楚的:一个一个地保存。但只是好奇,我真的不知道如果我是否允许用户撤消更多次,比如 5 个操作(并保存 5 个以前的位图),它会弹出内存不足错误。这是因为我已经在 S3 上测试了导入一些不太大的照片来绘制它,但它会在直接导入时崩溃(但不会因为 S2 内存不足!!)...总而言之,让我先尝试在不同的实际情况下运行设备并回复给您 =) 感谢您的绝妙想法,我认为这是解决此问题的最后手段
      • 我使用上面的代码进行了测试,但崩溃了。 logcat 位于 dalvik.system.NativeStart.main(Native Method) 的 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747) 关闭 VM threadid=1:线程以未捕获的异常退出致命异常:main java .lang.NullPointerException at com.pear.draw.DrawView.touchStarted(DrawView.java:584) at com.pear.draw.DrawView.onTouchEvent(DrawView.java:560) at android.view.View.dispatchTouchEvent(View.java :5717) 在 android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1689) 的 android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1962)
      • 是的,你有NullPointerException。您是否正确初始化了所有内容?我的代码只是一个没有适当初始化例程的伪代码。我假设你知道如何处理它
      • 现在没有更多的 nullpointerexception 但是当用户按下撤消按钮时看不到任何动作。你知道为什么吗?代码修改如上~
      【解决方案5】:

      乍一看我看到以下问题:

      通过将空的Path 添加到paths,只要你做到了,你就可以 撤消后就会出现问题:您正在弹出那个空的 Path 首先,使第一个撤消似乎不起作用。那么如果你画 到 Path 中,它没有添加到 paths。解决方案是添加 在创建新路径之前,已完成 Pathtouch_up() 中的路径。

      即移除

      paths.add(mPath);
      

      从构造函数,并在touch_up(),更改

      mPath = new Path();
      paths.add(mPath);
      

      paths.add(mPath);
      mPath = new Path();
      

      您还需要添加

      canvas.drawPath(mPath, mPaint);
      

      在您的for 循环之后在onDraw() 中绘制正在进行的Path

      当用户再次开始绘图时,您并没有清空undonePaths

      【讨论】:

        【解决方案6】:

        【讨论】:

        • 感谢 NagarjunaReddy.P 的参考!我已经阅读了这两个链接,但在两个博客中,清除油漆/路径的方法是完全擦除所有油漆,而不是前一个路径。你的方式是 public void onClick(View v) {mBitmap.eraseColor(Color.TRANSPARENT);mPath.reset();mView.invalidate();} --> 如果允许用户点击撤消一个就更完美了清除以前的操作。顺便说一句,我很感兴趣,你如何看待我的另一个 Q 将导入的位图放在扩展视图的中间@stackoverflow.com/questions/14675008/…
        猜你喜欢
        • 1970-01-01
        • 2012-03-16
        • 1970-01-01
        • 2014-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多