android 手写 流畅,安卓手写板,以及提高Android应用手写流畅度
优化方式
增加触摸点个数
减少每次刷新的区域(每次只刷新一个小矩形区域)
不直接说了,直接上代码。
public class PaletteView extends View {
private Paint mPaint;
private Path mPath;
private float mLastX;
private float mLastY;
private Bitmap mBufferBitmap;
private Canvas mBufferCanvas;
private static final int MAX_CACHE_STEP = 20;
private List mDrawingList;
private List mRemovedList;
private Xfermode mXferModeClear;
private Xfermode mXferModeDraw;
private int mDrawSize;
private int mEraserSize;
private int mPenAlpha = 255;
private boolean mCanEraser;
private Callback mCallback;
public enum Mode {
DRAW,
ERASER
}
private Mode mMode = Mode.DRAW;
public PaletteView(Context context) {
super(context);
init();
}
public PaletteView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public PaletteView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public interface Callback {
void onUndoRedoStatusChanged();
}
public void setCallback(Callback callback) {
mCallback = callback;
}
private void init() {
setDrawingCacheEnabled(true);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setFilterBitmap(true);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mDrawSize = DimenUtils.dp2pxInt(3);
mEraserSize = DimenUtils.dp2pxInt(30);
mPaint.setStrokeWidth(mDrawSize);
mPaint.setColor(Color.BLACK);
mXferModeDraw = new PorterDuffXfermode(PorterDuff.Mode.SRC);
mXferModeClear = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
mPaint.setXfermode(mXferModeDraw);
}
private void initBuffer() {
mBufferBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
mBufferCanvas = new Canvas(mBufferBitmap);
}
private abstract static class DrawingInfo {
Paint paint;
abstract void draw(Canvas canvas);
}
private static class PathDrawingInfo extends DrawingInfo {
Path path;
@Override
void draw(Canvas canvas) {
canvas.drawPath(path, paint);
}
}
public Mode getMode() {
return mMode;
}
public void setMode(Mode mode) {
if (mode != mMode) {
mMode = mode;
if (mMode == Mode.DRAW) {
mPaint.setXfermode(mXferModeDraw);
mPaint.setStrokeWidth(mDrawSize);
} else {
mPaint.setXfermode(mXferModeClear);
mPaint.setStrokeWidth(mEraserSize);
}
}
}
public void setEraserSize(int size) {
mEraserSize = size;
}
public void setPenRawSize(int size) {
mDrawSize = size;
if (mMode == Mode.DRAW) {
mPaint.setStrokeWidth(mDrawSize);
}
}
public void setPenColor(int color) {
mPaint.setColor(color);
}
private void reDraw() {
if (mDrawingList != null) {
mBufferBitmap.eraseColor(Color.TRANSPARENT);
for (DrawingInfo drawingInfo : mDrawingList) {
drawingInfo.draw(mBufferCanvas);
}
invalidate();
}
}
public int getPenColor() {
return mPaint.getColor();
}
public int getPenSize() {
return mDrawSize;
}
public int getEraserSize() {
return mEraserSize;
}
public void setPenAlpha(int alpha) {
mPenAlpha = alpha;
if (mMode == Mode.DRAW) {
mPaint.setAlpha(alpha);
}
}
public int getPenAlpha() {
return mPenAlpha;
}
public boolean canRedo() {
return mRemovedList != null && mRemovedList.size() > 0;
}
public boolean canUndo() {
return mDrawingList != null && mDrawingList.size() > 0;
}
public void redo() {
int size = mRemovedList == null ? 0 : mRemovedList.size();
if (size > 0) {
DrawingInfo info = mRemovedList.remove(size - 1);
mDrawingList.add(info);
mCanEraser = true;
reDraw();
if (mCallback != null) {
mCallback.onUndoRedoStatusChanged();
}
}
}
public void undo() {
int size = mDrawingList == null ? 0 : mDrawingList.size();
if (size > 0) {
DrawingInfo info = mDrawingList.remove(size - 1);
if (mRemovedList == null) {
mRemovedList = new ArrayList<>(MAX_CACHE_STEP);
}
if (size == 1) {
mCanEraser = false;
}
mRemovedList.add(info);
reDraw();
if (mCallback != null) {
mCallback.onUndoRedoStatusChanged();
}
}
}
public void clear() {
if (mBufferBitmap != null) {
if (mDrawingList != null) {
mDrawingList.clear();
}
if (mRemovedList != null) {
mRemovedList.clear();
}
mCanEraser = false;
mBufferBitmap.eraseColor(Color.TRANSPARENT);
invalidate();
if (mCallback != null) {
mCallback.onUndoRedoStatusChanged();
}
}
}
public Bitmap buildBitmap() {
Bitmap bm = getDrawingCache();
Bitmap result = Bitmap.createBitmap(bm);
destroyDrawingCache();
return result;
}
private void saveDrawingPath() {
if (mDrawingList == null) {
mDrawingList = new ArrayList<>(MAX_CACHE_STEP);
} else if (mDrawingList.size() == MAX_CACHE_STEP) {
mDrawingList.remove(0);
}
Path cachePath = new Path(mPath);
Paint cachePaint = new Paint(mPaint);
PathDrawingInfo info = new PathDrawingInfo();
info.path = cachePath;
info.paint = cachePaint;
mDrawingList.add(info);
mCanEraser = true;
if (mCallback != null) {
mCallback.onUndoRedoStatusChanged();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBufferBitmap != null) {
canvas.drawBitmap(mBufferBitmap, 0, 0, null);
}
}
@SuppressWarnings("all")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
final int action = event.getAction() & MotionEvent.ACTION_MASK;
final float x = event.getX();
final float y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
if (mPath == null) {
mPath = new Path();
}
mPath.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
if (mMode == Mode.DRAW) {
//橡皮擦模式,绘图模式
resetDirtyRect(x, y);
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
float historicalX = event.getHistoricalX(i);
float historicalY = event.getHistoricalY(i);
getDirtyRect(historicalX, historicalY);
mPath.lineTo(historicalX, historicalY);
}
mPath.lineTo(x, y);
if (mBufferBitmap == null) {
initBuffer();
}
mBufferCanvas.drawPath(mPath, mPaint);
invalidate((int) (mDirtyRect.left - HALF_STROKE_WIDTH),
(int) (mDirtyRect.top - HALF_STROKE_WIDTH),
(int) (mDirtyRect.right + HALF_STROKE_WIDTH),
(int) (mDirtyRect.bottom + HALF_STROKE_WIDTH));
} else {
//橡皮擦模式
//这里终点设为两点的中心点的目的在于使绘制的曲线更平滑,如果终点直接设置为x,y,效果和lineto是一样的,实际是折线效果
mPath.quadTo(mLastX, mLastY, (x + mLastX) / 2, (y + mLastY) / 2);
if (mBufferBitmap == null) {
initBuffer();
}
if (mMode == Mode.ERASER && !mCanEraser) {
break;
}
mBufferCanvas.drawPath(mPath, mPaint);
invalidate();
}
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_UP:
if (mMode == Mode.DRAW || mCanEraser) {
saveDrawingPath();
}
mPath.reset();
break;
}
return true;
}
private void resetDirtyRect(float eventX, float eventY) {
mDirtyRect.left = Math.min(mLastX, eventX);
mDirtyRect.right = Math.max(mLastX, eventX);
mDirtyRect.top = Math.min(mLastY, eventY);
mDirtyRect.bottom = Math.max(mLastY, eventY);
}
private void getDirtyRect(float historicalX, float historicalY) {
if (historicalX < mDirtyRect.left) {
mDirtyRect.left = historicalX;
} else if (historicalX > mDirtyRect.right) {
mDirtyRect.right = historicalX;
}
if (historicalY < mDirtyRect.top) {
mDirtyRect.top = historicalY;
} else if (historicalY > mDirtyRect.bottom) {
mDirtyRect.bottom = historicalY;
}
}
private static final float STROKE_WIDTH = 5f;
private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
private final RectF mDirtyRect = new RectF();
}
使用方式
public class HandWritingFragment extends BaseFragment implements View.OnClickListener, PaletteView.Callback {
@BindView(R.id.mPaletteView)
PaletteView paletteView;
private ImageView mPenView, mClearView, mEraserView, mSaveView, mQuitView;
private ImageView blackPen, redPen, yellowPen, greenPen, pinkPen, purplePen;
private ProgressDialog progressDialog;
private UpLoadImagePresenter upLoadImagePresenter;
private HandWritePrestener handWritePrestener;
@Override
protected int getLayoutId() {
return R.layout.fragment_hand_writing;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
upLoadImagePresenter = new UpLoadImagePresenter(getActivity(), handWriteView);
handWritePrestener = new HandWritePrestener(getActivity(), handWriteView);
View containerView = getActivity().getWindow().getDecorView().findViewById(R.id.task_info_container);
Bitmap bitmap = Bitmap.createBitmap(containerView.getWidth(), containerView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
containerView.draw(canvas);
// paletteView.setBackgroundColor(Color.WHITE);
paletteView.setBackground(new BitmapDrawable(bitmap));
}
@Override
protected void initialized() {
blackPen = view.findViewById(R.id.black);
redPen = view.findViewById(R.id.red);
yellowPen = view.findViewById(R.id.yellow);
greenPen = view.findViewById(R.id.green);
pinkPen = view.findViewById(R.id.pink);
purplePen = view.findViewById(R.id.purple);
mPenView = view.findViewById(R.id.pen);
mClearView = view.findViewById(R.id.clear);
mEraserView = view.findViewById(R.id.eraser);
mSaveView = view.findViewById(R.id.save);
mQuitView = view.findViewById(R.id.quit);
initProgressDialog();
}
@Override
protected void initListener() {
// paletteView.setCallback(this);
mSaveView.setOnClickListener(this);
mQuitView.setOnClickListener(this);
mPenView.setOnClickListener(this);
mClearView.setOnClickListener(this);
mEraserView.setOnClickListener(this);
redPen.setOnClickListener(this);
yellowPen.setOnClickListener(this);
pinkPen.setOnClickListener(this);
greenPen.setOnClickListener(this);
purplePen.setOnClickListener(this);
blackPen.setOnClickListener(this);
mPenView.setSelected(true);
blackPen.setSelected(true);
}
public static HandWritingFragment newsInstance() {
return new HandWritingFragment();
}
@Override
public void widgetClick(View view) {
switch (view.getId()) {
case R.id.pen:
paletteView.setMode(PaletteView.Mode.DRAW);
paletteView.setPenColor(Color.BLACK);
mPenView.setSelected(true);
mEraserView.setSelected(false);
blackPen.setSelected(true);
greenPen.setSelected(false);
pinkPen.setSelected(false);
yellowPen.setSelected(false);
redPen.setSelected(false);
purplePen.setSelected(false);
break;
case R.id.eraser:
paletteView.setMode(PaletteView.Mode.ERASER);
mEraserView.setSelected(true);
mPenView.setSelected(false);
redPen.setSelected(false);
greenPen.setSelected(false);
pinkPen.setSelected(false);
yellowPen.setSelected(false);
purplePen.setSelected(false);
blackPen.setSelected(false);
break;
case R.id.clear:
paletteView.clear();
break;
case R.id.save:
progressDialog.show();
Bitmap bitmap = paletteView.buildBitmap();
handWritePrestener.saveImageToLocal(bitmap);
break;
case R.id.quit:
popOutBackStack();
break;
case R.id.black:
paletteView.setPenColor(Color.BLACK);
blackPen.setSelected(true);
greenPen.setSelected(false);
pinkPen.setSelected(false);
yellowPen.setSelected(false);
purplePen.setSelected(false);
redPen.setSelected(false);
break;
case R.id.red:
paletteView.setPenColor(Color.RED);
redPen.setSelected(true);
greenPen.setSelected(false);
pinkPen.setSelected(false);
yellowPen.setSelected(false);
purplePen.setSelected(false);
blackPen.setSelected(false);
break;
case R.id.green:
paletteView.setPenColor(Color.GREEN);
greenPen.setSelected(true);
pinkPen.setSelected(false);
yellowPen.setSelected(false);
purplePen.setSelected(false);
redPen.setSelected(false);
blackPen.setSelected(false);
break;
case R.id.yellow:
paletteView.setPenColor(Color.YELLOW);
yellowPen.setSelected(true);
redPen.setSelected(false);
greenPen.setSelected(false);
pinkPen.setSelected(false);
purplePen.setSelected(false);
blackPen.setSelected(false);
break;
case R.id.pink:
paletteView.setPenColor(Color.parseColor("#E771F6"));
pinkPen.setSelected(true);
redPen.setSelected(false);
greenPen.setSelected(false);
yellowPen.setSelected(false);
purplePen.setSelected(false);
blackPen.setSelected(false);
break;
case R.id.purple:
paletteView.setPenColor(Color.parseColor("#6376EB"));
purplePen.setSelected(true);
redPen.setSelected(false);
greenPen.setSelected(false);
yellowPen.setSelected(false);
pinkPen.setSelected(false);
blackPen.setSelected(false);
break;
}
}
private void initProgressDialog() {
progressDialog = new ProgressDialog(getActivity());
progressDialog.setMessage("正在保存,请稍候.....");
progressDialog.setCancelable(false);
}
@Override
public void onUndoRedoStatusChanged() {
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().post(new HandWriteExitEvent());
handWritePrestener.onDestory();
upLoadImagePresenter.onDestory();
}
HandWriteView handWriteView = new HandWriteView() {
@Override
public void onError(String msg) {
if (progressDialog.isShowing())
progressDialog.dismiss();
ToastUtil.showToast(getContext(), msg);
}
@Override
public void onFileUploadSuccess(String fileid, String imgURL) {//上传成功
String teacherId = SPStoreUtil.getString(SystemConstant.sharedName, SystemConstant.teacherID);
String skjlID = SPStoreUtil.getString(SystemConstant.sharedName, SystemConstant.SKJLID);
SXBRequestBean sxbRequestBean = new SXBRequestBean();
sxbRequestBean.setSczid(teacherId);
sxbRequestBean.setSkjlid(skjlID);
sxbRequestBean.setFileid(fileid);
sxbRequestBean.setTplj(imgURL);
handWritePrestener.saveSXB(sxbRequestBean);
}
@Override
public void onSXBSave() {//保存成功
if (progressDialog.isShowing())
progressDialog.dismiss();
ToastUtil.showToast(getActivity(), "图片保存成功");
}
@Override
public void onLocalSaveImageSuccess(HandWriteSaveBean fileBean) {
upLoadImagePresenter.updateFile(
fileBean.getAppDir() + "/" + fileBean.getFileName()
, fileBean.getFileName());
Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
scanIntent.setData(Uri.fromFile(new File(fileBean.getAbsoluteFilePath())));
getContext().sendBroadcast(scanIntent);
}
};
}
效果还是比之前流畅多了。
android 手写 流畅,安卓手写板,以及提高Android应用手写流畅度相关推荐
- android培训讲师介绍,安卓培训讲师浅析android项目重点知识
原标题:安卓培训讲师浅析android项目重点知识 在应用程序之前,必须创建一个相应的Android项目.如果你使用Eclipse开发的话,那就是创建一个Eclipse项目.这个项目用来保存所有源代码 ...
- android 平板 吃鸡,安卓平板哪个吃鸡好 | 手游网游页游攻略大全
发布时间:2015-11-13 炉石传说安卓平板版本在加拿大.新西兰及澳大利亚这三个地区上架,官方版不支持国服和中文,需要玩家自行修改成中文.下面小编为大家介绍下暴雪官方公布的炉石传说安卓版最低硬件配 ...
- 目前最流畅的android手机,最流畅安卓手机排名!第一堪比iOS 跟手度极佳
日前,鲁大师发布2020年度手机报告.在流畅度排行榜中,小米10至尊纪念版以202.72分的成绩排名第一,成为2020年度"最流畅手机". Redmi K30S至尊纪念版以201. ...
- android 多界面开发,安卓开发教程(Android多界面应用程序开发)
安卓开发教程(Android多界面应用程序开发) 开篇 本文阅读需10分钟,简单易上手,属于安卓开发教程的基础部分. 建议精读,深刻理解大意.多做实践.多写代码. 本文章由做全栈攻城狮原创首发. 同名 ...
- unity android 播放器,Unity3D 安卓视频播放插件 WRP Android Video Player Pro
通过这个安卓视频播放插件,你可以在你的Unity3D 项目中针对很容的播放视频. Easily play videos in your Unity Android Projects with this ...
- android 启动模式_安卓学习笔记之Android中Activity的4种启动模式
根据Activity在任务栈中的调用方式不同,Activity的启动模式分为4种,分别是Standard.SingleTop.SingleTask以及SingleInstance.可以在Android ...
- 安卓编译android.mk,详解安卓系统中的Android.mk文件
概述 Android.mk文件用来向编译系统描述如何编译你的源代码.更确切地说,该文件其实就是一个小型的Makefile.由于该文件会被NDK的编译工具解析多次,因此应该尽量减少源码中声明变量, ...
- 支持3d android 模拟器,五款常用安卓模拟器哪个玩3D大型手游流畅好用不卡顿?...
随着现在手游包体越来越大,使用3D引擎之后有更炫酷的动画,这个就对安卓模拟器提出了越来越高的要求.很多用安卓模拟器玩手游的用户都会遇到某款游戏大作卡顿或者直接都玩不了的情况. 其实很多用户并不知道市场 ...
- android11电视,适用于Android TV的安卓11更新:提高性能与隐私
你知道智能电视也有适配的安卓11吗?在前几日,手机上的安卓11终于更新,紧接着,适用于Android TV的安卓11也发布了更新推送,更多信息和XDA小编一起往下看吧! 昨日,谷歌Pixel5有了最新 ...
最新文章
- 做好自己,一切都是最好的安排
- linux环境调用gsoap,Linux下gSOAP的使用 (c++)
- python调用adb传输电脑文件到手机_使用adb在电脑和手机间传文件
- Python 在字符串中处理html 和xml
- 【测试基础】测试用例的设计方法
- 7z制作自解压安装包
- tomcat整合apache
- html垂直线性渐变,html5线性渐变
- 字典dictionary
- ajax的typeAMDAt,ajax的运用
- Boss直聘上面HR是不是很恶心?
- 电商扣减库存_竞争激烈的电商市场,小型仓储外包服务解决了中小电商的后顾之忧...
- bzoj1355——2016——3——15
- 怎么批量修改文件后缀名?
- WS2811B驱动使用及使用说明
- 邮件中的抄送和密送的区别
- win10服务器只显示4g内存,64位win10识别到了4G内存,却只用了3.1G,为什么?
- 云计算是什么,云计算发展现状是什么?
- Photoshop(PS)2021安装教程【64位】
- Java Test Fore
热门文章
- hmacsha256 java实现_java实现HMACSHA256(md5私钥key)加密签名
- 计算机游戏配机方案,主流装机配置方案 3500元i5-7500配GTX1050游戏电脑配置清单推荐...
- form表单如何加css框架,如何写好CSS系列之表单(form)
- Java 三种集合的遍历方式
- php用chrome打不开,chrome打不开任何网页怎么回事_一招解决chrome打不开任何网页的方法-系统城...
- 6个小技巧,32个经典字谜,让你轻松答出98%的字谜题!
- jQuery日历记事插件SimpleCalendar(附源代码)
- cfa可以用计算机吗,CFA考试
- 服务器+维修清单+戴尔,惠东戴尔服务器主板维修
- LeetCode-621. 任务调度器