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. Qualcomm defconfig

    xxx_defconfig - for debugging xxx-perf_defconfig - for final live user's version.

  2. Java使用apache的开源数据处理框架commons-dbutils完成查询结果集的各种处理输出(8种方式)

    package demo; /* * QueryRunner数据查询操作: * 调用QueryRunner类方法query(Connection con,String sql,ResultSetHan ...

  3. springBoot api接口

    application/json 请求接口 @RequestMapping(value = "/getBaseData", method = RequestMethod.POST, ...

  4. AC日记——N的倍数 51nod 1103

    1103 N的倍数 思路: 先计算出前缀和: 然后都%n: 因为有n个数,所以如果没有sum[i]%n==0的化,一定有两个取模后的sum相等: 输出两个sum中间的数就好: 来,上代码: #incl ...

  5. AC日记——换教室 洛谷 P1850

    题目描述 对于刚上大学的牛牛来说, 他面临的第一个问题是如何根据实际情况中情合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第 i ( 1≤ i≤n)个时同段上, 两节内容相同的课 ...

  6. 慎用lodash的cloneDeep函数

    lodash的cloneDeep函数能够很方便的拷贝对象,但是一旦拷贝一些很复杂的对象就有可能报错.比如用cloneDeep克隆一个vue实例,就有可能包key.charAt is not a Fun ...

  7. python 安装cx_Oracle模块, MySQLdb模块, Tornado

    一,想访问远程Oracle数据库,本地又不想安装几百兆的Oracle Client(也木有root权限),安装python的cx_Oralce 模块需要依赖Oracle Instant Client ...

  8. 机器学习(4):数据分析的工具-pandas的使用

    前面几节说一些沉闷的概念,你若看了估计已经心生厌倦,我也是.所以,找到了一个理由来说一个有兴趣的话题,就是数据分析.是什么理由呢?就是,机器学习的处理过程中,数据分析是经常出现的操作.就算机器对大量样 ...

  9. python的列表元组字典集合比较

    定义 方法 列表 可以包含不同类型的对象,可以增减元素,可以跟其他的列表结合或者把一个列表拆分,用[]来定义的 eg:aList=[123,'abc',4.56,['inner','list'],7- ...

  10. Python--Day2/Day3/Day4(运算符、数据类型及内建函数)

    一.昨日内容回顾 Python种类:CPython(Python).JPython.IronPython.PyPy 编码: Unicode.UTF-8.GBK while循环 if...elif... ...