2011-06-20 20 views
5

Tôi đang sử dụng phiên bản tùy chỉnh FingerPaint cho Android với một số tính năng khác, như chèn hình ảnh và di chuyển chúng. Tôi quyết định triển khai Hoàn tác & Làm lại, vì nó sẽ giúp cuộc sống dễ dàng hơn. Để thực hiện nó, cuối cùng tôi quyết định sử dụng một Stack nơi tôi đẩy Cache vẽ của khung nhìn, và từ đó tôi đẩy nội dung mỗi lần tôi muốn quay lại trạng thái trước đó. Vì vậy, sử dụng FingerPaint làm cơ sở, tôi có những điều sau đây:Hoàn tác và làm lại trong Canvas cho Android

private void touch_up() { 
    mPath.lineTo(mX, mY); 
    // commit the path to our offscreen 
    mCanvas.drawPath(mPath, mPaint); 
    // I enable the set drawing cache...  
    myView.setDrawingCacheEnabled(true); 
    // ... and I add the cache to the stack 
    undoStack.add(myView.getDrawingCache()); 
    indexOfUndoRedo++; 
    // kill this so we don't double draw 
    mPath.reset(); 
} 

Ngăn xếp chỉ được cập nhật sau khi chạm vào lúc này vì tôi vẫn đang tìm cách giải quyết vấn đề này. Khi tôi muốn áp dụng lại, tôi thực hiện như sau:

private void undo() { 
    myView = new MyView(getActivity()); 
    myView.setBackgroundDrawable(new BitmapDrawable(undoStack.get(indexOfUndoRedo))); 
    indexOfUndoRedo--; 
    myView.invalidate(); 
} 

Cho đến nay, ứng dụng hiển thị trạng thái ban đầu của màn hình mà không thay đổi. Tôi cũng cố vẽ nó với nền trắng để đặt lại, nhưng cách tiếp cận này cũng không hoạt động.

Bất kỳ ý tưởng hoặc đề xuất nào về cách sửa lỗi này? Tôi sẽ thực sự biết ơn :)

Trân

+0

Làm cách nào để 'indexOfUndoRedo' được khởi tạo? – Rom1

+0

Tôi khởi tạo nó thành -1 (vì vậy, khi tôi thêm phần tử đầu tiên, giá trị sẽ là 0). Nhưng vẫn không làm việc – kikoso

Trả lời

9

Hãy thử mã dưới đây Draw Xem:

package com.draw; 
import java.util.ArrayList; 
import android.content.Context; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Canvas; 
import android.graphics.Paint; 
import android.graphics.Path; 

import android.view.MotionEvent; 
import android.view.View; 
import android.view.View.OnTouchListener; 
import android.widget.ImageView; 

public class DrawView extends View implements OnTouchListener { 
    private Canvas mCanvas; 
    private Path mPath; 
    private Paint  mPaint; 
    private ArrayList<Path> paths = new ArrayList<Path>(); 
    private ArrayList<Path> undonePaths = new ArrayList<Path>(); 

    private Bitmap im; 
    public DrawView(Context context) 
    { 
     super(context); 
     setFocusable(true); 
     setFocusableInTouchMode(true);  
     this.setOnTouchListener(this); 
     mPaint = new Paint(); 
     mPaint.setAntiAlias(true); 
     mPaint.setDither(true); 
     mPaint.setColor(0xFFFFFFFF); 
     mPaint.setStyle(Paint.Style.STROKE); 
     mPaint.setStrokeJoin(Paint.Join.ROUND); 
     mPaint.setStrokeCap(Paint.Cap.ROUND); 
     mPaint.setStrokeWidth(6); 
     mCanvas = new Canvas(); 
     mPath = new Path(); 
     paths.add(mPath); 

     im=BitmapFactory.decodeResource(context.getResources(),R.drawable.ic_launcher); 


    }    
     @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 = 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 = new Path(); 
      paths.add(mPath); 
     } 

     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 
     } 

    @Override 
    public boolean onTouch(View arg0, 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; 
    } 
} 

và Vẽ Hoạt động mã bố trí dưới đây:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 
<FrameLayout android:id="@+id/main_frame" 
    android:layout_width="fill_parent" android:layout_height="250dp"> 

</FrameLayout> 
     <Button 
     android:id="@+id/button2" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="Redo" /> 

    <Button 
     android:id="@+id/button1" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="Undo" /> 

</LinearLayout> 

và Vẽ Hoạt động lớp bên dưới mã:

package com.draw; 

import android.app.Activity; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.FrameLayout; 
import android.widget.ImageButton; 
import android.widget.ImageView; 
import android.widget.LinearLayout; 
import android.widget.Toast; 

public class Draw extends Activity { 
    ImageView iv1; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     final DrawView drawView = new DrawView(this); 
     setContentView(R.layout.main); 
     FrameLayout frm_layout=(FrameLayout) findViewById(R.id.main_frame); 
     frm_layout.addView(drawView); 
     Button btn_undo=(Button) findViewById(R.id.button1); 
     btn_undo.setOnClickListener(new OnClickListener() { 

      @Override 
      public void onClick(View v) { 
       // TODO Auto-generated method stub 
       drawView.onClickUndo(); 
      } 
     }); 

     Button btn_redo=(Button) findViewById(R.id.button2); 
     btn_redo.setOnClickListener(new OnClickListener() { 

      @Override 
      public void onClick(View v) { 
       // TODO Auto-generated method stub 
       drawView.onClickRedo(); 
      } 
     }); 
    } 

} 

Đây là ứng dụng vẽ mẫu với các hoạt động hoàn tác và làm lại trong Android, nó hoạt động hoàn hảo cho tôi!

+0

+1 tốt đẹp một chỉ thay đổi dòng này 'mPaint.setColor (0xFFFFFFFF);' to 'mPaint.setColor (0xff00ff00);' làm việc tốt đẹp bởi vì '0xFFFFFFFF' là màu trắng đã có màu nền là màu trắng đó là lý do tại sao không hiển thị trong đường dẫn vẽ ..... – NagarjunaReddy

+1

Nó không hoạt động hoàn hảo bởi vì ở lần nhấp đầu tiên trên nút hoàn tác, nó không hoạt động, và nhấp chuột thứ hai nó hoạt động. – anddev

+1

@anddev thấy bài đăng này hoạt động bình thường http://stackoverflow.com/questions/11114625/android-canvas-redo-and-undo-operation – Dinesh

2

Đây là mã hoạt động. Tôi thử nghiệm nó trên ứng dụng của riêng tôi và nó đang hoạt động rất tốt, có thể nó hữu ích cho người khác. Hãy bình luận về nó.

public class Main extends Activity implements OnColorChangedListener { 
    // public static int selectedColor = Color.BLACK; 
    public static ArrayList<Path> mMaindialog; 
    // private ArrayList<Path> undonePaths; 
    // public int selectedcolor; 
    private static final String COLOR_PREFERENCE_KEY = "color"; 
    private FrameLayout relativelayout; 
    static String sdpath, location; 
    Boolean i; 
    // Instance variables 
    private Bitmap mBitmap = null; 
    Bitmap bitmap; 
    private Paint mPaint, mBitmapPaint, mPaint1; 
    private MyView mView; 
    ImageView idd; 
    // private Path mPath; 
    int slll = Color.BLACK; 
    Bitmap map = ListView5.bMap; 
    private Button ClearPaint, Colorpaint; 
    Ghostdatabase gost; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 
     idd = (ImageView) findViewById(R.id.imageView1); 
     relativelayout = (FrameLayout) findViewById(R.id.frameLayout); 

     DisplayMetrics metrics = getBaseContext().getResources() 
       .getDisplayMetrics(); 
     int w = metrics.widthPixels; 
     int h = metrics.heightPixels; 

     System.out.println(" width " + w); 
     System.out.println(" height " + h); 

     mView = new MyView(this, w, h); 
     mView.setDrawingCacheEnabled(true); 

     mPaint = new Paint(); 
     mPaint.setAntiAlias(true); 
     mPaint.setDither(true); 
     mPaint.setColor(Color.BLACK); 
     mPaint.setStyle(Paint.Style.STROKE); 
     mPaint.setStrokeJoin(Paint.Join.ROUND); 
     mPaint.setStrokeCap(Paint.Cap.ROUND); 
     mPaint.setStrokeWidth(4); 

     ClearPaint = (Button) findViewById(R.id.ne); 
     ClearPaint.setOnClickListener(new OnClickListener() { 

      public void onClick(View v) { 
       // mBitmap.eraseColor(Color.TRANSPARENT); 
       // mPath.reset(); 
       // mView.invalidate(); 

       mView.onClickUndo(); 

      } 
     }); 
     Button save22 = (Button) findViewById(R.id.save); 
     save22.setOnClickListener(new OnClickListener() { 

      @Override 
      public void onClick(View v) { 
       // TODO Auto-generated method stub 
       File cacheDir; 
       Toast.makeText(Main.this, "Photo", 500).show(); 
       Bitmap icon; 
       relativelayout.setDrawingCacheEnabled(true); 

       icon = Bitmap.createBitmap(relativelayout.getDrawingCache()); 
       Bitmap bitmap = icon; 
       relativelayout.setDrawingCacheEnabled(false); 
       // File mFile1 = Environment.getExternalStorageDirectory(); 
       Date d = new Date(); 
       String fileName = d.getTime() + "mg1.jpg"; 

       File storagePath = (Environment.getExternalStorageDirectory()); 
       File dest = new File(storagePath + "/CityAppImages"); 

       if (!dest.exists()) { 
        dest.mkdirs(); 

       } 

       File mFile2 = new File(dest, fileName); 
       sdpath = mFile2.getAbsolutePath(); 

       Log.d("qqqqqqqqqqqqqqqqqqqqqqq", "zzzzzzzz" + sdpath); 
       try { 
        FileOutputStream outStream; 

        outStream = new FileOutputStream(mFile2); 

        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream); 

        outStream.flush(); 

        outStream.close(); 
        Toast.makeText(Main.this, "Photo Saved Sucessfully", 500) 
          .show(); 
       } catch (FileNotFoundException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } catch (IOException e) { 

        // TODO Auto-generated catch block 
        e.printStackTrace(); 
        Toast.makeText(Main.this, "Photo Not Saved Sucessfully", 
          500).show(); 
       } 

       gost = new Ghostdatabase(Main.this); 
       gost.open(); 

       gost.insertTitle(sdpath); 
      } 
     }); 

     Button view = (Button) findViewById(R.id.listtt); 
     view.setOnClickListener(new OnClickListener() { 

      public void onClick(View v) { 

       Intent ii = new Intent(Main.this, ListView5.class); 
       startActivity(ii); 

      } 
     }); 

     Button Colorpaint = (Button) findViewById(R.id.Color); 
     Colorpaint.setOnClickListener(new OnClickListener() { 

      public void onClick(View v) { 
       int color = PreferenceManager.getDefaultSharedPreferences(
         Main.this).getInt(COLOR_PREFERENCE_KEY, Color.WHITE); 
       // int _color = R.color.red; 
       new ColorPickerDialog(v.getContext(), 
         new OnColorChangedListener() { 

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

           slll = color; 

           Log.i("TAG", "mpaint one" + mPaint); 
          } 
         }, mPaint.getColor()).show(); 
       Log.i("TAG", "mpaint two" + mPaint); 
      } 
     }); 
     relativelayout.addView(mView); 
    } 

    // //////////******************* Pinting view 
    // *******************/////////////////// 

    public class MyView extends View implements OnTouchListener { 
     private Map<Path, Integer> colorsMap = new HashMap<Path, Integer>(); 
     private ArrayList<Path> mMaindialog = new ArrayList<Path>(); 
     private ArrayList<Path> undonePaths = new ArrayList<Path>(); 
     int colorPicked = slll; 
     // Paint mPaint1; 

     private Canvas mCanvas; 
     private Path mPath; 

     public MyView(Context c, int w, int h) { 
      super(c); 
      if (GlobalVariable.impath == 1) { 
       Log.d("", "111111" + GlobalVariable.impath); 
       System.out.println(GlobalVariable.impath); 
       Intent ii = getIntent(); 
       location = ii.getStringExtra("IMAGE"); 
       // bitmap.recycle(); 
       Log.d("", "location" + location); 
       bitmap = BitmapFactory.decodeFile(location); 
       mBitmap = Bitmap.createScaledBitmap(bitmap, 300, 300, false); 
       Log.d("hhhhhhhhhhhhhhhssssssss", "mBitmap" + mBitmap); 
       // mBitmap = Bitmap.createBitmap(100, 100, 
       // Bitmap.Config.ARGB_8888); 
       // idd.setImageBitmap(mBitmap); 
       Log.d("hhhhhhhhhhhhhhhssssssss", "GlobalVariable.impath" 
         + GlobalVariable.impath); 
      } else if (GlobalVariable.impath == 2) { 
       // bitmap.recycle(); 
       Log.d("", "22222222222222222222222" + GlobalVariable.impath); 
       bitmap = BitmapFactory.decodeResource(getResources(), 
         R.drawable.base); 
       mBitmap = Bitmap.createScaledBitmap(bitmap, 100, 100, false); 
       // mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 
       Log.d("hhhhhhhhhhhhhhhssssssss1111111", "mBitmap" + mBitmap); 
      } 

      // 
      mCanvas = new Canvas(mBitmap); 
      mPath = new Path(); 

     } 

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

     } 

     @Override 
     protected void onDraw(Canvas canvas) { 

      canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); 

      for (Path p : mMaindialog) { 
       mPaint.setColor(colorsMap.get(p)); 
       canvas.drawPath(p, mPaint); 
      } 
      mPaint.setColor(slll); 
      canvas.drawPath(mPath, mPaint); 
     } 

     // //////************touching evants for painting**************/////// 
     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; 
      undonePaths.clear(); 
     } 

     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(); 
      mPath.reset(); 
      mMaindialog.add(mPath); 
     } 

     @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(); 
       undonePaths.clear(); 
       mPath.reset(); 
       mPath.moveTo(x, y); 
       mX = x; 
       mY = y; 
       invalidate(); 
       break; 
      case MotionEvent.ACTION_MOVE: 
       // touch_move(x, y); 
       // invalidate(); 
       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; 
       } 
       invalidate(); 
       break; 
      case MotionEvent.ACTION_UP: 
       // touch_up(); 
       // invalidate(); 
       mPath.lineTo(mX, mY); 
       mMaindialog.add(mPath); 
       colorsMap.put(mPath, slll); 
       mPath = new Path(); 
       mPath.reset(); 
       invalidate(); 
       break; 
      } 
      return true; 
     } // end of touch events for image 

     private Paint createPen(int colorPicked) { 
      // TODO Auto-generated method stub 
      mPaint1 = new Paint(); 
      mPaint1.setColor(colorPicked); 
      mPaint1.setAntiAlias(true); 
      mPaint1.setDither(true); 
      mPaint1.setStyle(Paint.Style.STROKE); 
      mPaint1.setStrokeJoin(Paint.Join.ROUND); 
      mPaint1.setStrokeCap(Paint.Cap.ROUND); 
      // mPaint1.setStrokeWidth(3); 
      return mPaint1; 
     } 

     public void onClickRedo() { 
      if (undonePaths.size() > 0) { 
       mMaindialog.add(undonePaths.remove(undonePaths.size() - 1)); 
       mView.invalidate(); 

      } else { 

      } 
      // toast the user 
     } 

     public void onClickUndo() { 
      if (mMaindialog.size() > 0) { 
       undonePaths.add(mView.mMaindialog.remove(mView.mMaindialog 
         .size() - 1)); 
       mView.invalidate(); 
      } 

      else { 

      } 
     } 

     @Override 
     public boolean onTouch(View arg0, MotionEvent arg1) { 
      // TODO Auto-generated method stub 
      return false; 
     } 
    }// end MyView 

    @Override 
    public void colorChanged(int color) { 
     // TODO Auto-generated method stub 
     PreferenceManager.getDefaultSharedPreferences(this).edit() 
       .putInt(COLOR_PREFERENCE_KEY, color).commit(); 
     slll = color; 
    } 

}