Android 版 Canvas 中的撤消和重做

2024-01-05

我正在使用定制版本指甲油 http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/FingerPaint.html适用于 Android 并具有其他一些功能,例如插入图像和移动图像。我决定实施撤消和重做,因为它会让生活变得更轻松。为了实现它,我最终决定使用一个堆栈,在其中推送视图的绘图缓存,并在每次想要返回到之前的状态时从其中推送内容。因此,使用 FingerPaint 作为基础,我有以下内容:

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

目前,堆栈仅在修复后更新,因为我仍在研究如何解决此问题。当我想应用重做时,我会执行以下操作:

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

到目前为止,应用程序显示屏幕的原始状态没有任何变化。我还尝试将其绘制为白色背景以重置它,但这种方法也不起作用。

关于如何解决这个问题有什么想法或建议吗?我真的很感激:)

Regards


尝试下面的代码绘制视图:

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

和绘制 Activity 布局代码如下:

<?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>

并绘制活动类如下代码:

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

}

这是 Android 中具有撤消和重做操作的示例绘画应用程序,它非常适合我!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 版 Canvas 中的撤消和重做 的相关文章

  • 在视图上按下按键时不会调用 onKeyDown

    我有一个包含两个视图的活动 一个视图重写 onDraw 并正确处理 onTouchEvent 但是当我尝试检索第二个视图的 onKeyDown 时 它没有给我任何结果 相反 当我按下后退按钮或任何其他键盘按钮时 会调用 onKeyDown
  • 按下后退按钮时停止 Fragments 中的 AsyncTask

    我有一个托管片段的活动 按下按钮会通过 FragmentTransaction 从片段 A 转到片段 B 并将其添加到返回堆栈中 现在片段 B 有一个 AsyncTask 实现 它从 sdcard 加载图像并在加载图像时将其发布 如果我按
  • 将 LinkedHashset 内容复制到新的 ArrayList?

    我有一个最初包含一些内容的 listView 如果它得到相同的内容 我通过删除重复linkedhashset 现在 我想复制linkedhashset内容 即没有重复的内容到新的ArrayList 我尝试复制通过 p addAll 0 lh
  • 什么时候使用弱引用? [复制]

    这个问题在这里已经有答案了 我了解什么是 Java WeakReference 我想知道的是它通常用于解决哪种具体问题 有没有包含它们的模式 WeakReference and SoftReference当您想保留某些东西以备再次需要时使用
  • 谷歌gson LinkedTreeMap类转换为myclass

    我知道这个问题以前已经被问过 由于我对java和android的新手技能 我一个多星期都无法解决这个问题 我和我的一位朋友正在开发一个 Android 项目 其中有一些类似的事情 最奇怪的部分是 只有当我从 Google Play 商店下载
  • 序列化/反序列化 LinkedHashMap (android) java

    所以我想将 LinkedHashMap 传递给意图 SEND THE MAP Intent singlechannel new Intent getBaseContext singlechannel class singlechannel
  • 字符串包含相同的字符但仍然不同[重复]

    这个问题在这里已经有答案了 我正在尝试读取一个 txt 文件并使用每个句子作为团队的名称 同时使用该名称查找另一个 txt 文件以获取其内容 所有 txt 文件都位于我的资产文件夹的根目录中 第一个 txt 文件工作正常 我使用assetm
  • 消费者关闭了输入通道或发生错误。事件=0x8

    D AndroidRuntime 11752 D AndroidRuntime 11752 gt gt gt gt gt gt AndroidRuntime START com android internal os RuntimeInit
  • API 27 中 startActivityForResult 后崩溃

    更新到 API 27 和支持库 27 0 2 后 我突然在 Crashlytics 中得到了很多这样的堆栈跟踪 Fatal Exception java lang IllegalArgumentException at android os
  • 如何反序列化数组 google-gson 内的数组

    我有这样的 JSON Answers Locale Ru Name Name1 Locale En Name Name2 Locale Ru Name Name3 Locale En Name Name4 正如你所看到的 我的数组里面有数组
  • FileNotFoundException(系统找不到指定的路径)

    我得到这个例外 java io FileNotFoundException C filename xml The system cannot find the path specified 使用此代码 FileWriter fileWrit
  • 如何启用 Genymotion 模拟器使用主机正在使用的 WIFI 互联网

    我在 Genymotion 模拟器上运行的应用程序需要互联网 互联网似乎无法在 Genymotion 模拟器上运行 我试图通过打开浏览器来确认这一点 这就是我得到的 我在我的笔记本电脑上运行 Windows 7 并使用 Wifi 互联网 我
  • 如何处理MaxUploadSizeExceededException

    MaxUploadSizeExceededException当我上传的文件大小超过允许的最大值时 会出现异常 我想在出现此异常时显示错误消息 如验证错误消息 我该如何处理这个异常 以便在 Spring 3 中执行类似的操作 Thanks 这
  • 返回数据集的 kSoap 和 .Net Web 服务

    我知道使用数据集是一个很大的罪恶 但由于该服务不在我的控制之下 并且创建代理服务的前景是不可能的 我想看看是否有人创建了可以使用 kSoap 序列化器反序列化的类结构 或者我是否吸错了东西 同时要启动它 看看是否可行 以下是预期的结果数据
  • 缓冲区溢出(与)缓冲区溢出(与)堆栈溢出[重复]

    这个问题在这里已经有答案了 可能的重复 堆栈溢出和缓冲区溢出有什么区别 https stackoverflow com questions 1120575 what is the difference between a stack ove
  • 使用 Java 8 时间将时间从一个时区转换为另一时区

    我正在尝试将日期转换为GMT 5 30 to EST与java 8ZonedDateTime String inputDate 2015 04 30 13 00 DateTimeFormatter sourceFormatter DateT
  • Robotium 和系统对话框

    当我尝试与蓝牙设备配对时 会出现带有 PIN 码的系统确认对话框 有 取消 和 确定 按钮 但我无法使用 Robotium 单击它们 如何在 Robotium 中使用 Android 操作系统对话框 谢谢 这对我有用 solo clickO
  • 安卓应用安全

    我想开发一个用户数据非常敏感的应用程序 我是开发新手 所以不确定以下内容 技术对于安全或高效来说是必要的 请留下您的评论 提前致谢 为了额外的安全性 我们可以避开市场 游戏商店 并将应用程序安装在个人设备上 它会让它更安全吗 我必须在设备上
  • 如何防止LRU缓存android中的内存不足错误

    我在我的 Android 应用程序中使用内存 LRU 缓存来缓存位图 但是在将某些位图加载到 LRU 映射中后 应用程序强制关闭并提示内存不足异常 我花了一整天的时间 但还没有找到解决方案 请任何人都可以帮助我 我严重陷入这个问题 提前致谢
  • Oracle 的商业 Hotspot JVM 相对于 OpenJDK 有哪些性能优势?

    正如这个问题中所描述的 OpenJDK 与 Java HotspotVM https stackoverflow com q 44335605 1593077 Oracle 的商业 Hotspot JVM 本质上是 OpenJDK 加上一些

随机推荐