{"id":22018770,"url":"https://github.com/rhcad/androiddrawing","last_synced_at":"2025-10-18T00:47:51.852Z","repository":{"id":29737423,"uuid":"33280825","full_name":"rhcad/AndroidDrawing","owner":"rhcad","description":"基于vgandroid构建Android绘图应用","archived":false,"fork":false,"pushed_at":"2015-09-07T10:06:08.000Z","size":2368,"stargazers_count":37,"open_issues_count":0,"forks_count":15,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-07T03:34:45.849Z","etag":null,"topics":["android","touchvg"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rhcad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-02T00:44:20.000Z","updated_at":"2024-06-20T03:18:35.000Z","dependencies_parsed_at":"2022-09-06T18:10:57.602Z","dependency_job_id":null,"html_url":"https://github.com/rhcad/AndroidDrawing","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rhcad/AndroidDrawing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhcad%2FAndroidDrawing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhcad%2FAndroidDrawing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhcad%2FAndroidDrawing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhcad%2FAndroidDrawing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rhcad","download_url":"https://codeload.github.com/rhcad/AndroidDrawing/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhcad%2FAndroidDrawing/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262565866,"owners_count":23329653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","touchvg"],"created_at":"2024-11-30T05:14:03.113Z","updated_at":"2025-10-18T00:47:46.801Z","avatar_url":"https://github.com/rhcad.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AndroidDrawing\n\n基于 [TouchVG][vgandroid] 构建矢量绘图应用样例。\n\n## 目录\n\n- [1.超简单的涂鸦App](#练习1超简单的涂鸦app)\n- [2.添加绘图按钮](#练习2添加绘图按钮)\n- [3.增加自动保存和恢复功能](#练习3增加自动保存和恢复功能)\n- [4.增加线宽动态修改和更新功能](#练习4增加线宽动态修改和更新功能)\n- [5.增加颜色选择框](#练习5增加颜色选择框)\n- [6.增加Undo/Redo功能](#练习6增加undoredo功能)\n- [7.截屏](#练习7绘图区截屏)\n\n练习1：超简单的涂鸦App\n-------------------\n\n准备工作：安装 ADT Bundle 开发环境（我用的是v23，官方下载被墙，可从[这][ADT]下载）。\n\n1. 新建 Android 程序项目。\n\n   - SDK 最小版本选 API 16 以上（避免自动创建的 appcompat_v7 项目出现资源缺失错误），完成后可改回低版本（使用 TouchVG 要求最低 API 12）。\n   - 在创建 Activity 页面选择默认的简单布局 Blank Activity。\n\n2. 在主页面布局中添加一个 FrameLayout，将用作绘图区的容器。\n\n   - 指定 ID 为 `container`，下面就可通过 `findViewById(R.id.container)`找到此布局。\n   - 使用 FrameLayout 而不是其他布局类型做绘图视图容器，是避免触摸绘图引起其他相邻视图联锁刷新。\n\n3. 添加 TouchVG 引用。\n\n   - 下载[预编译的TouchVG包][prebuilt]，将 touchvg.jar 和 libtouchvg.so 复制到程序项目的 libs 下。\n   \n   - 如需调试进入 TouchVG 或快速查看 [IViewHelper][IViewHelper] 接口注释，则不能复制touchvg.jar，可将 [TouchVG项目][vgandroid] 下载到上级目录并导入TouchVG工程，在程序项目的 project.properties 中加入引用：\n   `android.library.reference.1=../../vgandroid/TouchVG`\n\n4. 在 MainActivity.java 中创建绘图视图。\n\n   - 定义 [IViewHelper][IViewHelper] 对象，在 onCreate 中创建绘图视图。\n\n     ```java\n    public class MainActivity extends Activity {\n         private IViewHelper mHelper = ViewFactory.createHelper();\n\n         @Override\n         protected void onCreate(Bundle savedInstanceState) {\n             super.onCreate(savedInstanceState);\n             setContentView(R.layout.activity_main);\n\n             mHelper.createGraphView(this, (ViewGroup) this.findViewById(R.id.container));\n             mHelper.setCommand(\"splines\");\n         }\n     ```\n   - 在 `createGraphView` 下一行的 `setCommand` 激活随手画命令。`splines` 是命令名，更多命令名见[在线文档][cmdnames]。\n   \n   - 将 `createGraphView` 换为 `createSurfaceView` 可基于 SurfaceView 创建绘图视图，适合大量图形或页面上有较多控件的情况。在普通 View 上绘图时占用主线程显示，刷新时可能引起页面其他视图被动刷新。在 SurfaceView 上可异步绘图，避免连锁刷新问题。\n\n5. 运行程序，动手画图吧。\n\n   ![step1](Screenshot/step1.png)\n\n练习2：添加绘图按钮\n----------------\n\n1. 添加按钮布局和按钮图片。\n\n   在 res/drawable 中添加五个按钮图片，在 res/layout 中添加按钮布局 button_bar.xml：\n\n     ```xml\n    \u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n    \u003cLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"fill_parent\"\n        android:orientation=\"vertical\" \u003e\n\n        \u003cImageButton\n            android:id=\"@+id/line_btn\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable/line\" /\u003e\n\n        \u003cImageButton\n            android:id=\"@+id/select_btn\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable/select\" /\u003e\n\n    \u003c/LinearLayout\u003e\n     ```\n\n   主界面布局 activity_main.xml：\n\n     ```xml\n    \u003cLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:tools=\"http://schemas.android.com/tools\"\n        android:id=\"@+id/horzLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"horizontal\"\n        tools:context=\"com.example.drawing1.MainActivity\" \u003e\n\n        \u003cinclude\n            android:id=\"@+id/buttons_bar\"\n            layout=\"@layout/buttons_bar\" /\u003e\n\n        \u003cFrameLayout\n            android:id=\"@+id/container\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" \u003e\n        \u003c/FrameLayout\u003e\n\n    \u003c/LinearLayout\u003e\n     ```\n   \n2. 添加按钮响应，激活相应绘图命令。\n\n     ```java\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        .....\n        initButtons();\n    }\n\n    private void initButtons() {\n        findViewById(R.id.line_btn).setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                mHelper.setCommand(\"line\");\n            }\n        });\n        findViewById(R.id.select_btn).setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                mHelper.setCommand(\"select\");\n            }\n        });\n    }\n     ```\n\n3. 再次运行程序，点按钮可绘制多种图形。但选择图形出来几个空的按钮。\n\n   ![step2](Screenshot/step2.png)\n\n4. 添加上下文按钮资源、本地化串资源。从[预编译的TouchVG包][prebuilt]中，将 res/drawable-hdpi、res/drawable-mdpi、res/values 合并到程序中。其中 res/values/strings.xml 不能直接复制文件，需要合并文字内容。\n\n   ![step3](Screenshot/step3.png)\n\n练习3：增加自动保存和恢复功能\n------------------------\n\n目前重启程序或按Home键，返回程序后所绘图形会丢失。可以增加自动保存和恢复功能。\n\n1. 在 AndroidManifest.xml 中增加 MOUNT_UNMOUNT_FILESYSTEMS 和 WRITE_EXTERNAL_STORAGE 权限，以便读写外部存储器。\n\n2. 在 MainActivity 中实现 onDestroy、onPause、onSaveInstanceState、onRestoreInstanceState，分别调用 IViewHelper 中相似名称的函数。在创建绘图视图时传入 savedInstanceState，返回 Activity 时自动恢复图形：`mHelper.createGraphView(this, layout, savedInstanceState);`。\n\n练习4：增加线宽动态修改和更新功能\n---------------------------\n\n选中一个图形，可动态（所见即所得）修改其线宽等属性。没有选中图形时设置的图形属性将应用到新画的图形上。\n\n1. 在 Activity 布局中增加一个滑块控件，ID为lineWidthBar，最大值为 20，即最大20像素宽。\n\n2. 在 MainActivity 的 onCreate 中设置滑动响应：\n\n     ```java\n    mLineWidthBar = (SeekBar) findViewById(R.id.lineWidthBar);\n    mLineWidthBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {\n        @Override\n        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n            mHelper.setStrokeWidth(progress);\n        }\n        @Override\n        public void onStartTrackingTouch(SeekBar seekBar) {\n            mHelper.setContextEditing(true);\n        }\n        @Override\n        public void onStopTrackingTouch(SeekBar seekBar) {\n            mHelper.setContextEditing(false);\n        }\n    });\n     ```\n\n    其中，调用 setContextEditing 是避免在拖动滑块过程中多次提交改动，产生多次Undo步骤（下面会实现Undo）。\n\n3. 为了在选中不同的图形后更新线宽滑块值，需要增加[选择改变观察者][IGraphView]：\n\n    ```java\n   mHelper.getGraphView().setOnSelectionChangedListener(new OnSelectionChangedListener() {\n        @Override\n        public void onSelectionChanged(IGraphView view) {\n            mLineWidthBar.setProgress(mHelper.getStrokeWidth());\n        }\n  });\n    ```\n\n练习5：增加颜色选择框\n-----------------\n\n1. 在工程中导入[Android-Color-Picker][ColorPicker]库。这里就直接添加源码(com.chiralcode.colorpicker)了。也可以换为其他颜色选取框项目，例如 [HoloColorPicker][HoloColorPicker]。\n\n2. 增加一个按钮，点击时显示颜色选择对话框：\n\n     ```java\n   findViewById(R.id.colorpicker_btn).setOnClickListener(new OnClickListener() {\n       @Override\n       public void onClick(View v) {\n           new ColorPickerDialog(MainActivity.this, mHelper.getLineColor(),\n               new OnColorSelectedListener() {\n                   @Override\n                   public void onColorSelected(int color) {\n                       mHelper.setLineColor(color);\n                   }\n           }).show();\n       }\n   });\n     ```\n\n练习6：增加Undo/Redo功能\n----------------------\n\n1. 在页面布局中增加两个按钮，ID为 undo_btn 和 redo_btn。\n2. 在按钮点击响应中执行Undo/Redo操作，并准备录制Undo信息：\n\n     ```java\n     findViewById(R.id.undo_btn).setOnClickListener(new OnClickListener() {\n         @Override\n         public void onClick(View v) {\n             mHelper.undo();\n         }\n     });\n     findViewById(R.id.redo_btn).setOnClickListener(new OnClickListener() {\n         @Override\n         public void onClick(View v) {\n             mHelper.redo();\n         }\n     });\n     mHelper.startUndoRecord(PATH + \"undo\");\n     ```\n\n3. 增加图形内容改变的观察者，在图形改变时更新按钮状态：\n\n     ```java\n   mHelper.getGraphView().setOnContentChangedListener(new OnContentChangedListener() {\n       @Override\n       public void onContentChanged(IGraphView view) {\n           findViewById(R.id.undo_btn).setEnabled(mHelper.canUndo());\n           findViewById(R.id.redo_btn).setEnabled(mHelper.canRedo());\n       }\n   });\n     ```\n\n练习7：绘图区截屏\n---------------\n\n添加一个按钮，按钮响应函数如下：\n\n    findViewById(R.id.snapshot_btn).setOnClickListener(new OnClickListener() {\n        @Override\n        public void onClick(View v) {\n            mHelper.savePNG(mHelper.extentSnapshot(4, true), PATH + \"snapshot.png\");\n        }\n    });\n\n\n[vgandroid]: https://github.com/rhcad/vgandroid\n[prebuilt]: https://github.com/rhcad/vgandroid/archive/prebuilt.zip\n[IViewHelper]: https://github.com/rhcad/vgandroid/blob/develop/TouchVG/src/rhcad/touchvg/IViewHelper.java\n[IGraphView]: https://github.com/rhcad/vgandroid/blob/develop/TouchVG/src/rhcad/touchvg/IGraphView.java\n[cmdnames]: http://touchvg.github.io/pages/Commands.html\n[ADT]: http://tools.android-studio.org\n[ColorPicker]: https://github.com/chiralcode/Android-Color-Picker\n[HoloColorPicker]: https://github.com/LarsWerkman/HoloColorPicker\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frhcad%2Fandroiddrawing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frhcad%2Fandroiddrawing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frhcad%2Fandroiddrawing/lists"}