package com.example.drawboard;

import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View; public class DrawBoardView extends View { //定義防止線條有鋸齒的常數
private static final boolean GESTURE_RENDERING_ANTIALIAS = true;
private static final boolean DITHER_FLAG = true; // 底圖 bitmap
private Bitmap mBackgroundBitmap = null;
// View 的整個長寬範圍
private Rect mWholeRect; private Bitmap mBitmap = null; // 圖層繪圖
private Canvas mCanvas = null; // 圖層畫布
private Paint mDefaultPaint = new Paint(); // 空白畫筆 private boolean mCapturing = true; // 是否擷取狀態 //定義繪圖的基本參數:線的 width, color
private float mPaintWidth = 5f;
private int mPaintColor = Color.RED; private int mPenMode = 1; //1:為畫筆, 0:為板擦
private Paint mPaint; private Path mPath; private List<Path> mDrawList = new ArrayList<Path>();
private List<Paint> mPaintsList = new ArrayList<Paint>();
private List<Rect> mRectsList = new ArrayList<Rect>();
private int mTotalAction = 0; // 記錄動作的次數 private float mCurveStartX, mCurveStartY; // 按下的點
private float mX, mY; // 移動的點
private float mCurveEndX, mCurveEndY; // 放開的點 private final Rect mInvalidRect = new Rect();
private int mInvalidateExtraBorder = 10; // 建構子
public DrawBoardView(Context context) {
super(context);
init(context);
} // 建構子
public DrawBoardView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
} // 建構子
public DrawBoardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
} // 初始化
private void init(Context context) {
setWillNotDraw(false);
} private void setPaint(Paint thePaint) {
if (mPenMode != 0) {
thePaint.setStrokeWidth(mPaintWidth);
thePaint.setColor(mPaintColor);
thePaint.setStyle(Paint.Style.STROKE);
thePaint.setStrokeJoin(Paint.Join.ROUND);
thePaint.setStrokeCap(Paint.Cap.ROUND);
thePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
thePaint.setDither(DITHER_FLAG);
thePaint.setXfermode(null);
} else { // 橡皮檫模式,先以黑色顯示範圍
thePaint.setStrokeWidth(mPaintWidth);
thePaint.setColor(Color.BLACK);
thePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
thePaint.setDither(DITHER_FLAG);
thePaint.setXfermode(null);
}
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
this.mWholeRect = new Rect(0, 0, w, h); mBitmap = null;
mCanvas = null;
mBitmap = Bitmap.createBitmap(this.mWholeRect.width(), this.mWholeRect.height(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap); super.onSizeChanged(w, h, oldw, oldh);
} @Override
protected void onDraw(Canvas canvas) {
if (mBackgroundBitmap != null) mCanvas.drawBitmap(mBackgroundBitmap, null, this.mWholeRect, null); for (int i = 0; i < mTotalAction; i++) {
mCanvas.drawPath(mDrawList.get(i), mPaintsList.get(i));
} if (mPath != null && mPaint != null) mCanvas.drawPath(mPath, mPaint); canvas.drawBitmap(mBitmap, 0, 0, mDefaultPaint);
} @Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mCapturing = true;
mPaint = new Paint();
setPaint(mPaint); mPath = new Path(); touchDown(event);
invalidate(); return true; case MotionEvent.ACTION_MOVE:
if (mCapturing) {
Rect rect = touchMove(event);
if (rect != null) {
invalidate(rect);
}
} return true; case MotionEvent.ACTION_UP:
touchUp(event);
invalidate(); mCapturing = false; // 先清除已復原的動作
int totalAction = mDrawList.size();
while (totalAction > mTotalAction) {
totalAction -= 1;
mPaintsList.remove(totalAction);
mRectsList.remove(totalAction);
mDrawList.remove(totalAction);
} // 再加入新的動作
mTotalAction += 1; if (mPenMode == 0) {
mBitmap = null;
mCanvas = null;
mBitmap = Bitmap.createBitmap(this.mWholeRect.width(), this.mWholeRect.height(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap); // 橡皮擦屬性
mPaint.setColor(Color.TRANSPARENT);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
mPaintsList.add(mPaint); mDrawList.add(mPath); mPaint = null;
mPath = null; return true;
} return false;
} private void touchDown(MotionEvent event) {
float x = event.getX();
float y = event.getY(); mCurveStartX = x;
mCurveStartY = y;
mX = x;
mY = y;
mCurveEndX = x;
mCurveEndY = y; mPath.moveTo(x, y); final int border = mInvalidateExtraBorder;
mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border);
} private Rect touchMove(MotionEvent event) {
Rect areaToRefresh = null; final float x = event.getX();
final float y = event.getY(); final float previousX = mX;
final float previousY = mY; areaToRefresh = mInvalidRect; // start with the curve end
final int border = mInvalidateExtraBorder;
areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border,
(int) mCurveEndX + border, (int) mCurveEndY + border); float cX = mCurveEndX = (x + previousX) / 2;
float cY = mCurveEndY = (y + previousY) / 2; mPath.quadTo(previousX, previousY, cX, cY); // union with the control point of the new curve
areaToRefresh.union((int) previousX - border, (int) previousY - border,
(int) previousX + border, (int) previousY + border); // union with the end point of the new curve
areaToRefresh.union((int) cX - border, (int) cY - border, (int) cX
+ border, (int) cY + border); mX = x;
mY = y; return areaToRefresh;
} private void touchUp(MotionEvent event) {
float x = event.getX();
float y = event.getY(); mCurveEndX = x;
mCurveEndY = y;
} /**
* 復原上一動作
* return: false 表不能再undo; true 表能繼續undo
*/
public boolean undo() {
int totalAction = mDrawList.size();
if (mTotalAction > 0) {
mTotalAction -= 1; mBitmap = null;
mCanvas = null;
mBitmap = Bitmap.createBitmap(this.mWholeRect.width(), this.mWholeRect.height(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap); invalidate();
} if (totalAction == 0 || mTotalAction <= 0) {
return false;
} else {
return true;
}
} /**
* 取消復原動作
* return: false 表不能再 reUndo; true 表能繼續 reUndo
*/
public boolean reUndo() {
int totalAction = mDrawList.size();
if (mTotalAction < totalAction) {
mTotalAction += 1; mBitmap = null;
mCanvas = null;
mBitmap = Bitmap.createBitmap(this.mWholeRect.width(), this.mWholeRect.height(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap); invalidate();
} if (totalAction == 0 || mTotalAction >= totalAction) {
return false;
} else {
return true;
}
} /**
* 檢查可復原動作的狀態
* return: 0 表不能再 undo,也不能再 reUndo;1 表不能再 undo,但可再 reUndo;2 表可再 undo,但不能再 reUndo;3 表可再 undo,也可再 reUndo;
*/
public int checkUndoStatus() {
int totalAction = mDrawList.size();
int result = 0; if (totalAction == 0) { //沒有任何動作資訊
result = 0;
} else if (mTotalAction <= 0) { //有動作資訊,但已「復原」至最前端
result = 1;
} else if (mTotalAction >= totalAction) { //有動作資訊,但已「取消復原」至最後端
result = 2;
} else {
result = 3;
} return result;
} /**
* 清除整個 View 的畫面
*/
public void clear() {
mBackgroundBitmap = null; mDrawList.removeAll(mDrawList);
mPaintsList.removeAll(mPaintsList);
mRectsList.removeAll(mRectsList);
mTotalAction = 0; mBitmap = null;
mCanvas = null;
mBitmap = Bitmap.createBitmap(this.mWholeRect.width(), this.mWholeRect.height(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap); invalidate();
} /**
* 設定整個 View 的畫面圖示
*/
public void setWholeViewBitmap(Bitmap bitmap) {
mBackgroundBitmap = bitmap;
invalidate();
} /**
* 設定為畫筆或板擦
* @param penMode: 1 為畫筆, 0 為板擦
*/
public void setPenMode(int penMode) {
if (penMode == 0) {
this.mPenMode = 0;
} else {
this.mPenMode = 1;
}
} /**
* 取得是畫筆或板擦
* @return: 1 為畫筆, 0 為板擦
*/
public int getPenMode() {
return this.mPenMode;
} /**
* 設定畫筆或板擦的寬度
* @param width
*/
public void setPaintStrokeWidth(float width) {
this.mPaintWidth = width;
} /**
* 取得畫筆或板擦的寬度
*/
public float getPaintStrokeWidth() {
return this.mPaintWidth;
} /**
* 設定畫筆的顏色
* @param width
*/
public void setPaintColor(int color) {
this.mPaintColor = color;
} /**
* 取得畫筆的顏色
*/
public int getPaintColor() {
return this.mPaintColor;
} }

Android 的繪圖白板元件(View)。 可當作一般的 View 元件使用。 setWholeViewBitmap(Bitmap bitmap):設定整個 View 的畫面圖示 setPenMode(int penMode):設定為畫筆或板擦 setPaintStrokeWidth(float width):設定畫筆或板擦的寬度 setPaintColor(int color):設定畫筆的顏色,话不多说自己看吧~可以直接复制代码就可以用。

Android 繪圖白板元件,有畫筆和板擦的功能 (转)的更多相关文章

  1. [R] 繪圖 Par 函数

    本篇內文主引用 https://zhuanlan.zhihu.com/p/21394945 之內容再稍加整理並參照下方有用資源 [rdocumentation] https://www.rdocume ...

  2. Android消息推送(二)--基于MQTT协议实现的推送功能

    国内的Android设备,不能稳定的使用Google GCM(Google Cloud Messageing)消息推送服务. 1. 国内的Android设备,基本上从操作系统底层开始就去掉了Googl ...

  3. Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View

    Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...

  4. [Android中级]使用Commons-net-ftp来实现FTP上传、下载的功能

    本文属于学习分享,如有雷同纯属巧合 利用业余时间.学习一些实用的东西,假设手又有点贱的话.最好还是自己也跟着敲起来. 在android上能够通过自带的ftp组件来完毕各种功能.这次是由于项目中看到用了 ...

  5. Android中脱离WebView使用WebSocket实现群聊和推送功能

    WebSocket是Web2.0时代的新产物,用于弥补HTTP协议的某些不足,不过他们之间真实的关系是兄弟关系,都是对socket的进一步封装,其目前最直观的表现就是服务器推送和聊天功能.更多知识参考 ...

  6. Android应用如何监听自己是否被卸载及卸载反馈功能的实现

    一个应用被用户卸载肯定是有理由的,而开发者却未必能得知这一重要的理由,毕竟用户很少会主动反馈建议,多半就是用得不爽就卸,如果能在被卸载后获取到用户的一些反馈,那对开发者进一步改进应用是非常有利的.目前 ...

  7. android EditText长按屏蔽ActionMode context菜单但保留选择工具功能

    最近项目要求屏蔽EditText 长按出来的ActionMode菜单,但是要保留选择文本功能.这个屏蔽百度会出现各种方法,这里说一下我的思路: 1.屏蔽百度可知setCustomSelectionAc ...

  8. Android 高仿UC浏览器监控剪切板弹出悬浮窗功能

    UC浏览器应该是android手机里 最流行的浏览器之一了,他们有一个功能 相信大家都体验过,就是如果你复制了什么文字,(在其他app中 复制也有这个效果!,所以能猜到肯定是监控了剪切板),就会弹出一 ...

  9. Android网络:开发浏览器(一)——基本的浏览网页功能开发

    我们定义这个版本为1.0版本. 首先,因为要制作一个浏览器,那么就不能通过调用内置浏览器来实现网页的浏览功能,但是可以使用WebView组件来进行. 在此之前,我们可以来看看两种网页显示方式:     ...

随机推荐

  1. 浅析CSS里的 BFC 和 IFC

    前端日刊 登录 浅析CSS里的 BFC 和 IFC 2018-01-29 阅读 1794 收藏 3 原链:segmentfault.com 分享到:   前端必备图书<Web安全开发指南 掌握白 ...

  2. TCP/IP和HTTP协议与Socket的区别联系

    参考资料: http://www.cnblogs.com/goodcandle/archive/2005/12/10/socket.html http://www.2cto.com/net/20121 ...

  3. layui 的单选框

    <div class="layui-form-item"> <label class="layui-form-label">是否通过&l ...

  4. linux创建和查看用户命令

    1.创建一个叫做hadoop的用户,用户的目录是/home/hadoop useradd -d /home/hadoop hadoop 2.输入密码 passwd hadoop 3.删除用户 user ...

  5. 腾讯云使用liveRoom开启直播时,报“房间已存在”错误?

    利用腾讯云roomService服务,移动直播,创建房间api,CreateRoom时有时报“房间已存在”错误. 分析流程发现,CreateRoom会传入roomId到roomService后台,后台 ...

  6. Codeforces 631B Print Check (思维)

    题目链接 Print Check 注意到行数加列数最大值只有几千,那么有效的操作数只有几千,那么把这些有效的操作求出来依次模拟就可以了. #include <bits/stdc++.h> ...

  7. 网站安全测试工具GoLismero

    网站安全测试工具GoLismero GoLismero是一款开源的安全测试框架.目前,它的测试目标主要为网站.该框架采用插件模式,实现用户所需要的功能.GoLismero默认自带了导入.侦测.扫描.攻 ...

  8. Python: Write UTF-8 characters to csv file

    To use codecs, we can write UTF-8 characters into csv file import codecs with open('ExcelUtf8.csv', ...

  9. ASIHTTPRequest实现断点续传

    http://blog.csdn.net/daiyelang/article/category/1377418 ASIHTTPRequest可以实现断点续传.网上有一些介绍类似使用:   [reque ...

  10. eos智能合约执行流程

    eos智能合约执行 1. 执行流程 controller::push_transaction()  // 事务 -> transaction_context::exec()  // 事务 -&g ...