2

私はcanvasアンドロイドで少し混乱していて、誰かが私のためにいくつかのことを明確にすることができるかどうか疑問に思っていました.

現在、View クラス内に次のコードがあります。

 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編集:私の方法も含めると便利かもしれません-

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

3 に答える 3

1

あなたの例では、moveModeにいるとき、最終的なキャンバスには何も描画しません。

すでに描画されているものに変換を適用している場合でも、onDraw()を2回呼び出す間はキャンバスがクリアであるため、再度描画する必要があることに注意してください。

元に戻すやり直し機能に慣れていませんが、キャンバスのビットマップのコピーを特定のポイントに保存して、後で復元できるようにする必要があります

Bitmap.copy()

大量のビットマップを保存することはモバイル環境では非常に重い可能性があるため、UNDOスタックサイズを制限する必要があることに注意してください。

于 2013-02-27T17:09:51.713 に答える
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オブジェクトを再描画する」これを試してください。私はこのコードで作業しています...ありがとう!!!

于 2013-03-01T14:51:40.923 に答える
0

描画メソッドの条件を削除し、if else if元に戻す機能のためにビットマップの配列リストを作成し、ユーザーが何らかのアクションを行うたびにビットマップを保存し、元に戻すときにビットマップを前のものに置き換えてビューを無効にします

于 2013-02-27T17:00:28.807 に答える