2013-02-27 122 views
2

我在Android中与canvas有点混淆,并想知道是否有人可以为我澄清一些事情。Android - 画布混乱(绘图和滚动)

目前,我有这个代码我的视图类:

class HomerView extends View { // the custom View for drawing on 
    // set up Bitmap, canvas, path and paint 
    private Bitmap myBitmap; // the initial image we turn into our canvas 
    private Canvas myCanvas; // the canvas we are drawing on 
    private Rect myRect; // the mathematical path of the lines we draw 
    private Paint myBitmapPaint; // the paint we use to draw the bitmap 

    // get the width of the entire tablet screen 
    private int screenWidth = getContext().getResources() 
      .getDisplayMetrics().widthPixels; 
    // get the height of the entire tablet screen 
    private int screenHeight = getContext().getResources() 
      .getDisplayMetrics().heightPixels; 

    private int mX, mY, iX, iY; // current x,y and initial x,y 
    private static final float TOUCH_TOLERANCE = 4; 
    private static final int INVALID_POINTER_ID = -1; 
    private float mPosX; 
    private float mPosY; 
    private BitmapDrawable mImage; 
    private float mLastTouchX; 
    private float mLastTouchY; 
    private int mActivePointerId = INVALID_POINTER_ID; 
    private ScaleGestureDetector mScaleDetector; 
    private float mScaleFactor = 1.f; 

    public HomerView(Context context) { // constructor of HomerView 
     super(context); 
     myBitmap = Bitmap.createBitmap(screenWidth, screenHeight, 
       Bitmap.Config.ARGB_8888); // set our drawable space - the bitmap which becomes the canvas we draw on 
     myCanvas = new Canvas(myBitmap); // set our canvas to our bitmap which we just set up 
     myRect = new Rect(); // make a new rect 
     myBitmapPaint = new Paint(Paint.DITHER_FLAG); // set dither to ON in our saved drawing - gives better color interaction 
     setHorizontalScrollBarEnabled(true); 
     setVerticalScrollBarEnabled(true); 
     TypedArray a = context.obtainStyledAttributes(R.styleable.View); 
     initializeScrollbars(a); 
     a.recycle(); 
     computeVerticalScrollRange(); 
     computeHorizontalScrollRange(); 
     mImage = new BitmapDrawable(getResources(), myBitmap); 
     mScaleDetector = new ScaleGestureDetector(context, 
       new ScaleListener()); 
    } 

    public HomerView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
     mScaleDetector = new ScaleGestureDetector(context, 
       new ScaleListener()); 
    } 

    public HomerView(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
    } 

    protected void onDraw(Canvas canvas) { // method used when we want to draw something to our canvas 
     super.onDraw(canvas); 
     if (addObjectMode == true) { 
      canvas.drawColor(Color.TRANSPARENT); // sets canvas colour 
      canvas.drawBitmap(myBitmap, 0, 0, myBitmapPaint); // save the canvas to bitmap - the numbers are the x, y coords we are drawing from 
      canvas.drawRect(myRect, myPaint); // draw the rectangle that the user has drawn using the paint we set up 
     } else if (moveMode == true) { 
      canvas.save(); 
      System.out.println("X: " + mPosX + " Y: " + mPosY); 
      canvas.translate(mPosX, mPosY); 
      canvas.scale(mScaleFactor, mScaleFactor); 
      mImage.draw(canvas); 
      canvas.restore(); 
     } 

这是种2种不同的方法来绘制到屏幕我所理解的合并的。我的理解是,我有myBitmap(这是用户图纸绘制的),并且这适用于“addObjectMode”。但是,“moveMode”是我希望用户能够在他们绘制的房屋计划周围捏缩放滚动等。

目前,我可以很好地绘制物体,但是当我按下按钮启用moveMode时,绘图在我触摸或手势时消失。我知道这可能是因为onDraw()中的代码;但画布对我来说仍然有点神秘。

最后,我想要一个基本的撤销/重做功能,以及保存canvas/bitmap以后打开。任何人都可以提供任何建议或链接到任何全面的画布教程?

编辑:这可能是有用的,包括我的onTouchEvent方法too-

public boolean onTouchEvent(MotionEvent event) { // on any touch event 
     if (addObjectMode == true) { 

      float x = event.getX(); // get current X 
      float y = event.getY(); // get current Y 

      switch (event.getAction()) { // what action is the user performing? 
      case MotionEvent.ACTION_DOWN: // if user is touching down 
       touch_Start(x, y); 
       invalidate(); 
       break; 
      case MotionEvent.ACTION_MOVE: // if user is moving finger while touched down 
       touch_Move(x, y); 
       invalidate(); 
       break; 
      case MotionEvent.ACTION_UP: // if user has released finger 
       touch_Up(); 
       invalidate(); 
       break; 
      } 
      return true; 
     } else if (moveMode == true) { 
      mScaleDetector.onTouchEvent(event); 
      final int action = event.getAction(); 
      switch (action & MotionEvent.ACTION_MASK) { 
      case MotionEvent.ACTION_DOWN: { 
       final float x = event.getX(); 
       final float y = event.getY(); 
       mLastTouchX = x; 
       mLastTouchY = y; 
       mActivePointerId = event.getPointerId(0); 
       break; 
      } 

      case MotionEvent.ACTION_MOVE: { 
       final int pointerIndex = event 
         .findPointerIndex(mActivePointerId); 
       final float x = event.getX(pointerIndex); 
       final float y = event.getY(pointerIndex); 

       // Only move if the ScaleGestureDetector isn't processing a gesture. 
       if (!mScaleDetector.isInProgress()) { 
        final float dx = x - mLastTouchX; 
        final float dy = y - mLastTouchY; 
        mPosX += dx; 
        mPosY += dy; 
        invalidate(); 
       } 
       mLastTouchX = x; 
       mLastTouchY = y; 
       break; 
      } 

      case MotionEvent.ACTION_UP: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 

      case MotionEvent.ACTION_CANCEL: { 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
      } 

      case MotionEvent.ACTION_POINTER_UP: { 
       final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
       final int pointerId = event.getPointerId(pointerIndex); 
       if (pointerId == mActivePointerId) { 
        // This was our active pointer going up. Choose a new 
        // active pointer and adjust accordingly. 
        final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 
        mLastTouchX = event.getX(newPointerIndex); 
        mLastTouchY = event.getY(newPointerIndex); 
        mActivePointerId = event.getPointerId(newPointerIndex); 
       } 
       break; 
      } 
      } 
      return true; 
     } else { 
      return false; 
     } 
    } 

回答

1

在您的示例中,当您在moveMode中时,不会在最终画布上绘制任何东西。

即使您正在对已绘制的东西进行转换,请注意两次onDraw()调用之间的画布已清除,因此您必须再次绘制它。

我不熟悉的撤销重做功能,但你应该能够存储在画布上的位图的副本在给定的点以后恢复它

Bitmap.copy()

要小心的是存储大量的位图对于移动环境来说可能会非常沉重,因此您必须限制撤消堆栈大小。

+0

嗯......不行'mImage.draw(canvas);'draw to canvas?我做了什么,而不是添加'\t \t \t \t canvas.drawBitmap(MYBITMAP,0,0,myBitmapPaint);'canvas.restore前();虽然当我捏缩放并切换到“绘图模式”时,它立即恢复到初始缩放级别,就好像没有发生滚动一样。任何想法为什么? – 2013-02-27 17:28:55

+0

是,以同样的方式您的绘图并没有适用于您的movemode,你的翻译转换没有被应用到您的drawMode这里;) – Guian 2013-02-27 17:31:32

+0

我建议你分开移动/从绘制方法来绘制mecanism出来,设法绘制在用户事件的唯一输出位图。那么onDraw方法只绘制输出位图。 – Guian 2013-02-27 17:34:12

0

去除平局方法if else if条件,并撤消功能做位图的ArrayList中,并保存您的位图每次用户做一些动作,然后撤消将位图替换为先前并使视图无效

+0

我希望用户有2个“触摸模式” - 绘图和移动。如果有其他人在那里执行相关的代码。如果没有'if if if',我可以绘制,但是在moveMode中,什么都不会发生。 – 2013-02-27 17:04:40

+0

如果您处于移动模式,为什么要跳过绘图模式?首先绘制位图和缩放比例 – 2013-02-27 17:06:42

+0

绘图模式=用户绘制矩形,移动模式=用户可捏缩放和滚动矩形。 – 2013-02-27 17:10:41

0

为了实现Undo \ Redo,您需要维护路径列表...并且您必须相应地将这些路径存储在路径列表中。我附上了我的代码,看看它,然后你会清楚地知道撤消重做功能。

 public static LinkedList<Path> rrPathList=new LinkedList<Path>(); 
    public static LinkedList<Path> mPathList=new LinkedList<Path>(); 
    protected void onDraw(Canvas canvas) { 

      for(i = 0; i < mPathList.size() && i < sPaint.size(); i++) 
      { 
       canvas.drawPath(mPathList.get(i),sPaint.get(i)); 

      } 
      } 
    // In your touch up method store the paths into mPathList 
     private void touch_up() {  
      mCanvas.drawPath(mPath, mPaint); 
      mPathList.add(mPath); 
     } 
    // Undo function 
    public void undo() 
    { 
     try 
     { 
      if(mPathList.size()>0) 
      { 
       rPathList.add(mPathList.getLast()); 
       mPathList.removeLast(); 

       invalidate(); 
      } 
     }catch(ArrayIndexOutOfBoundsException arr) 
     { 
      //Print error; 
     } 
    } 
    // Redo function 
    public void redo() 
    { 
     try 
     { 
      if(rPathList.size()>0) 
      { 
       mPathList.add(rPathList.getLast()); 
       rPathList.removeLast(); 
      invalidate(); 
      } 
     }catch(ArrayIndexOutOfBoundsException arr) 
     { 
      //Handle error 
     } 
    } 

注意:请不要忘记调用“invalidate()”。 调用无效结果“调用画布并重新绘制mPathList对象” 试试这个。我正在使用此代码... 谢谢!