10

FingerPaintAndroid SDK Demos の例に似たテスト プロジェクトに取り組んでいます。プロジェクトに元に戻す/やり直し機能を実装しようとしましたが、試したことが期待どおりに機能しませんでした。インターネットとここでこれに似た質問をいくつか見つけましたが、役に立たなかったので、新しい質問をしています。

ここに私が実際にやっていることのいくつかのアイデアがあります:

    public class MyView extends View {

    //private static final float MINP = 0.25f;
    //private static final float MAXP = 0.75f;



    private Path    mPath;
    private Paint   mBitmapPaint;

    public MyView(Context c) {
        super(c);

        mPath = new Path();
        mBitmapPaint = new Paint(Paint.DITHER_FLAG);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        mCanvas = new Canvas(mBitmap);
        mCanvas.drawColor(Color.WHITE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

        canvas.drawPath(mPath, mPaint);
    }

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    private void touch_start(float x, float y) {
        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
    }
    private void touch_move(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;
        }
    }
    private void touch_up() {

        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        mCanvas.drawPath(mPath, mPaint);
        // kill this so we don't double draw
        mPath.reset();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }
}

私のプロジェクトでこの種の機能を実装するための最良の方法である提案/アイデア/例はありますか?

4

5 に答える 5

13

これがあなたが念頭に置いていたものかどうかはわかりませんが、それが私がやっている方法です. 1 つのパスだけに保存​​する代わりに、すべてのパスを含む配列を保存します。このように、ユーザーは多くの線を引くことができ、少し変更を加えるだけでマルチタッチも追加できます。

取り消しとやり直しを行うには、paths変数から最後のパス path を削除または追加し、それらを新しい配列に格納します。何かのようなもの:

public void onClickUndo () { 
    if (paths.size()>0) { 
       undonePaths.add(paths.remove(paths.size()-1))
       invalidate();
     }
    else
     //toast the user 
}

public void onClickRedo (){
   if (undonePaths.size()>0) { 
       paths.add(undonePaths.remove(undonePaths.size()-1)) 
       invalidate();
   } 
   else 
     //toast the user 
}

これが私の変更されたパネルです。今は試すことができませんが、上記の方法は機能するはずです! それが役に立てば幸い!(それらを削除するだけの余分な変数はほとんどありません:)

private ArrayList<Path> undonePaths = new ArrayList<Path>(); 
public class DrawingPanel extends View implements OnTouchListener {

private Canvas  mCanvas;
private Path    mPath;
private Paint   mPaint,circlePaint,outercirclePaint;   
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>(); 
private float xleft,xright,xtop,xbottom;

public DrawingPanel(Context context) {
    super(context);
    setFocusable(true);
    setFocusableInTouchMode(true);

    this.setOnTouchListener(this);


    circlePaint = new Paint();
    mPaint = new Paint();
    outercirclePaint = new Paint();
    outercirclePaint.setAntiAlias(true);
    circlePaint.setAntiAlias(true);
    mPaint.setAntiAlias(true);        
    mPaint.setColor(0xFFFFFFFF);
    outercirclePaint.setColor(0x44FFFFFF);
    circlePaint.setColor(0xAADD5522);
    outercirclePaint.setStyle(Paint.Style.STROKE);
    circlePaint.setStyle(Paint.Style.FILL);        
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(6);
    outercirclePaint.setStrokeWidth(6);        
    mCanvas = new Canvas();
    mPath = new Path();
    paths.add(mPath);             


    cx = 400*DrawActivity.scale;
    cy = 30*DrawActivity.scale;
    circleRadius = 20*DrawActivity.scale;
    xleft = cx-10*DrawActivity.scale;
    xright = cx+10*DrawActivity.scale;
    xtop = cy-10*DrawActivity.scale;
    xbottom = cy+10*DrawActivity.scale;

}


public void colorChanged(int color) {
    mPaint.setColor(color);
}


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {            

        for (Path p : paths){
            canvas.drawPath(p, mPaint);
        }

    }

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 0;

    private void touch_start(float x, float y) {
        mPath.reset();
        mPath.moveTo(x, y);
        mX = x;
        mY = y;
    }
    private void touch_move(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;
        }
    }
    private void touch_up() {
        mPath.lineTo(mX, mY);
        // commit the path to our offscreen
        mCanvas.drawPath(mPath, mPaint);
        // kill this so we don't double draw            
        mPath = new Path();
        paths.add(mPath);
    }



@Override
public boolean onTouch(View arg0, MotionEvent event) {
      float x = event.getX();
      float y = event.getY();

      switch (event.getAction()) {
          case MotionEvent.ACTION_DOWN:
              if (x <= cx+circleRadius+5 && x>= cx-circleRadius-5) {
                  if (y<= cy+circleRadius+5 && cy>= cy-circleRadius-5){
                      paths.clear();
                      return true;
                      }
              }
              touch_start(x, y);
              invalidate();
              break;
          case MotionEvent.ACTION_MOVE:
              touch_move(x, y);
              invalidate();
              break;
          case MotionEvent.ACTION_UP:
              touch_up();
              invalidate();
              break;
      }
      return true;
}




}
于 2012-05-11T19:55:55.817 に答える
4

最善の解決策は、独自のUndo/Redoエンジンを実装することです。

  1. アレイで実行するすべてのアクションをアレイに保存します(つまり、位置x1の[0]円、y1、x2、y2からx3、y3への[1]行など)

  2. 描く

  3. 元に戻す必要がある場合は、キャンバスをクリアして、[0]から[n-1]までのすべてのn-1アクションを再描画します。

  4. さらに元に戻す場合は、[0]から[n-2]などにペイントする必要があります

それがあなたにヒントを与えることを願っています

乾杯!

于 2012-05-07T18:15:23.000 に答える
4

do/redo 機能を実装する 1 つの方法は、メソッド呼び出しと呼び出しに必要なすべての情報をオブジェクトにカプセル化して、それを格納して後で呼び出すことができるようにすることです (コマンド パターン) 。

このパターンでは、各アクションには独自のオブジェクトがあります: DrawCircleCommand、DrawPathCommand、FillColorCommand など。コマンド。元に戻すには、undo() メソッドを呼び出してオブジェクトを反復処理します。

各コマンド オブジェクトはインターフェイスを実装します

public interface IDrawCommand {  
  public void draw(Canvas canvas);  
  public void undo();  
}  

オブジェクトは次のようになります。

public class DrawPathCommand implements IDrawCommand{  
  public Path path;  
  public Paint paint;  

  public void setPath(path){
    this.path = path
  }

  public void draw(Canvas canvas) {  
    canvas.drawPath( path, paint );  
  }  

  public void undo() {  
   //some  action to remove the path
 }  

}

コマンドが CommandManager に追加されます。

mCommandManager.addCommand(IDrawCommand command)

コマンドを元に戻すには、次のように呼び出します。

mCommandManager.undo();

CommandManager は、コマンド オブジェクトを反復処理できるようにするリストなどのデータ構造にコマンドを格納します。

Android で Canvas を描画するための do/undo を使用して Command Pattern を実装する方法については、こちらの完全なチュートリアルを参照してください。

これは、Java でコマンドパターンを実装する方法に関する別のチュートリアルです。

于 2012-05-11T23:22:11.163 に答える
0

この場合、2つのキャンバスを使用できると思います。ユーザーがいつ描画を開始し、いつ終了するかがわかります。したがって、touch_start現在のキャンバスのコピーを作成できます。ユーザーが元に戻すをクリックすると、現在のキャンバスを以前に保存したものに置き換えます。

これにより、以前の状態の画像が得られることが保証されますが、パフォーマンスについてはわかりません。

于 2012-04-15T21:31:16.593 に答える