2012-04-15 8 views
8

Ich arbeite an einem Testprojekt, das ähnlich wie FingerPaint Beispiel in Android SDK Demos ist. Ich habe versucht, Rückgängig/Redo-Funktionalität in meinem Projekt zu implementieren, aber die Dinge, die ich versuchte, funktionierte nicht wie erwartet. Ich finde ähnliche Fragen über das Internet und hier, aber sie haben mir nicht geholfen, deshalb stelle ich eine neue Frage. HierAndroid FingerPaint Rückgängig/Wiederholen Implementierung

ist eine Idee, was ich tue eigentlich:

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

Irgendwelche Vorschläge/Ideen/Beispiele, welche die beste Art und Weise ist diese Art von Funktionalität auf mein Projekt zu implementieren?

+0

Welche Lösungen haben Sie schon versucht? – marwinXXII

+0

dieser: http://stackoverflow.com/questions/7633282/android-add-undo-feature-to-finger-paint-example-in-api-demo –

Antwort

11

Ich weiß nicht, ob dies das war, was Sie im Sinn hatten, aber es ist, wie ich es mache. Anstatt es in nur einem Pfad zu speichern, speichern Sie ein Array mit allen Pfaden, so dass der Benutzer viele Zeilen zeichnen kann, mit einer kleinen Änderung können Sie auch Multitouch hinzufügen.

Um das Rückgängigmachen und Wiederherstellen zu ermöglichen, entfernen oder fügen Sie einfach den letzten Pfad aus der paths Variablen hinzu und speichern Sie sie in einem neuen Array. Etwas wie:

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 
} 

Hier ist mein modifiziertes Panel, ich kann es jetzt nicht ausprobieren, aber die oben genannten Methoden sollten funktionieren! Ich hoffe es hilft! (Es gibt nur wenige zusätzliche Variablen sie einfach entfernen :)

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




} 
+1

Danke, Alter! Diese Implementierung funktioniert wie ein Zauber !!!! Sie müssen nur 'invalidate();' in 'onClickUndo()' und 'onClickRedo();' hinzufügen, damit Sie das Ergebnis gleich nach dem Klicken auf die Schaltfläche erhalten. –

+0

Gut zu helfen :) Ich habe es zur Antwort hinzugefügt! Prost! – caiocpricci2

+0

Ich habe gerade ein Problem gesehen. Bei Verwendung von 'mPaint.setXfermod (neuer PorterDuffXfermode (PorterDuff.Mode.CLEAR));' mit Ihrem Code ist das Ergebnis schwarze Linien, es verhält sich nicht wie ein Pinsel. Und die andere Sache ist, wenn ich versuche, einen Effekt auf den Strich zu setzen, ändert es auch alle alten Linien. –

0

Ich denke, dass Sie in diesem Fall zwei Leinwände verwenden können. Sie wissen, wann der Benutzer mit dem Zeichnen beginnt und wann er endet. So können Sie unter touch_start eine Kopie Ihres aktuellen Canvas erstellen. Wenn der Nutzer auf "Rückgängig" klickt, ersetzt er die aktuelle Arbeitsfläche durch die zuvor gespeicherte Arbeitsfläche.

Dies sollte garantieren, dass Sie vorherigen Zustand des Bildes haben, aber ich bin mir nicht sicher über die Leistung.

4

Die beste Lösung ist, dass Sie Ihre eigene Undo/Redo-Engine implementieren.

  1. Speicher in ein Array jede einzelne Aktion, die Sie in einem Array durchführen (dh. [0] Kreis in Position x1, y1, [1] Zeile von x2, y2 x3, y3 usw.)

  2. ziehen, wenn Sie rückgängig machen müssen, die Leinwand löschen und alle n neu streichen - 1 Aktionen von [0] bis [n - 1]

  3. , wenn Sie mehr nur rückgängig gemacht werden, um zu malen, haben aus [0] bis [n - 2] usw.

Ich hoffe, es gibt Ihnen einen Hinweis

Prost!

+1

Eigentlich war das meine Idee, aber ich möchte wissen, ob das ist das Beste, was ich tun kann, um dies zu tun. –

4

Eine Möglichkeit, eine do/Redo-Funktionalität zu implementieren einem Methodenaufruf kapseln und alle Informationen für den Anruf in einem Objekt benötigt, damit Sie sie speichern und rufen Sie es später - die Command Pattern.

In diesem Muster hat jede Aktion ihr eigenes Objekt: DrawCircleCommand, DrawPathCommand, FillColorCommand usw. In jedem Objekt ist die draw() -Methode auf eine einzigartige Weise implementiert, wird aber immer als draw (Canvas Canvas) bezeichnet CommandManager, um die Befehle zu durchlaufen.Um den Vorgang rückgängig zu machen, durchlaufen Sie die Objekte, die die Methode undo() aufrufen;

Jeder Befehl Objekt implementiert eine Schnittstelle

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

Ein Objekt würde wie folgt aussehen:

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 
} 

}

Ihre Befehle werden dem Befehlsmanager:

mCommandManager.addCommand(IDrawCommand command) 

und einen Befehl rückgängig zu machen, den Sie gerade aufrufen:

Der CommandManager speichert die Befehle in einer Datenstruktur, z. Liste, die es iterativ über die Befehlsobjekte ermöglicht.

Sie finden ein komplettes Tutorial here zum Implementieren der Befehlsmuster mit Do/Rückgängig für Canvas Zeichnung auf Android.

Here ist ein weiteres Tutorial zur Implementierung des Befehlsmusters in Java;

0

Zum Glück habe ich das heute gelöst. Ich finde einen Weg, dies zu tun.Etwas wie:

private ArrayList<Path> paths  = new ArrayList<>(); 
private ArrayList<Path> undonePaths = new ArrayList<>(); 

public void undo() { 
    if (paths.size() > 0) { 
     LogUtils.d("undo " + paths.size()); 
     clearDraw(); 
     undonePaths.add(paths.remove(paths.size() - 1)); 
     invalidate(); 
    } 
} 

public void redo() { 
    if (undonePaths.size() > 0) { 
     LogUtils.d("redo " + undonePaths.size()); 
     clearDraw(); 
     paths.add(undonePaths.remove(undonePaths.size() - 1)); 
     invalidate(); 
    } 
} 


public void clearDraw() { 
    mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); 
    mCanvas.setBitmap(mBitmap); 
    invalidate(); 
} 

public void clear() { 
    paths.clear(); 
    undonePaths.clear(); 
    invalidate(); 
} 

in Aktivität.

private DrawView   mDrawView; 

if (v == mIvUndo) { 
     mDrawView.undo(); 

    } else if (v == mIvRedo) { 
     mDrawView.redo(); 

in xml

<com.cinread.note.view.DrawView 
     android:id="@+id/paintView" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 

Wenn Sie eine bessere Lösung mich kontaktieren.

[hier ist mein Blog, dass ich in

schreiben

http://blog.csdn.net/sky_pjf/article/details/51086901]

Verwandte Themen