Android實(shí)現(xiàn)手寫板功能
本文實(shí)例為大家分享了Android實(shí)現(xiàn)手寫板功能的具體代碼,供大家參考,具體內(nèi)容如下
自定義個(gè)一個(gè)手寫板的重點(diǎn):
筆畫為一次down-move-up的集合
撤銷筆畫并非一次path的動(dòng)作撤銷 應(yīng)該也是一次down-move -up的撤銷
為了更好的筆畫需要使用貝塞爾曲線來完成
效果如下:
截圖中清楚 的意思是清除 !
具體代碼如下:
package com.kyli.base.view; 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.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.List; /** ?* 繪制畫板 ?*/ public class SignBoradView extends View { ? ? /*4個(gè)像素點(diǎn)*/ ? ? private int beierThreshold = 4; ? ? private float x = 0; ? ? private float y = 0; ? ? /*畫筆*/ ? ? private Paint mPaint; ? ? /*寬度*/ ? ? private int strokeWidth = 10; ? ? /*yanbse*/ ? ? private int color = Color.BLACK; ? ? /*當(dāng)前筆畫*/ ? ? private Path path; ? ? private int state = State.CLEAR; ? ? private interface State { ? ? ? ? /*畫板可以使用了*/ ? ? ? ? int START = 0; ? ? ? ? /*停止使用畫板*/ ? ? ? ? int STOP = 1; ? ? ? ? /*清空畫板*/ ? ? ? ? int CLEAR = 2; ? ? } ? ? private List<EveryPenPath> everyPenPaths = new ArrayList<>(); ? ? /*每一個(gè)筆畫*/ ? ? private static class EveryPenPath { ? ? ? ? public Path path; ? ? } ? ? public SignBoradView(Context context) { ? ? ? ? super(context); ? ? } ? ? public SignBoradView(Context context, @Nullable AttributeSet attrs) { ? ? ? ? super(context, attrs); ? ? } ? ? public SignBoradView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { ? ? ? ? super(context, attrs, defStyleAttr); ? ? } ? ? private void initPaint() { ? ? ? ? if (mPaint == null) { ? ? ? ? ? ? mPaint = new Paint(); ? ? ? ? ? ? mPaint.setStrokeWidth(strokeWidth); ? ? ? ? ? ? mPaint.setColor(color); ? ? ? ? ? ? mPaint.setStyle(Paint.Style.STROKE); ? ? ? ? ? ? mPaint.setAntiAlias(true); ? ? ? ? ? ? mPaint.setFlags(Paint.ANTI_ALIAS_FLAG); ? ? ? ? } ? ? } ? ? public void start() { ? ? ? ? state = State.START; ? ? ? ? initPaint(); ? ? } ? ? /*停止使用*/ ? ? public void stop() { ? ? ? ? state = State.STOP; ? ? } ? ? /*清空畫板*/ ? ? public void clear() { ? ? ? ? state = State.CLEAR; ? ? ? ? for (int i = everyPenPaths.size() - 1; i >= 0; i--) { ? ? ? ? ? ? EveryPenPath everyPenPath = everyPenPaths.get(i); ? ? ? ? ? ? everyPenPath.path.reset(); ? ? ? ? ? ? everyPenPath.path.close(); ? ? ? ? ? ? everyPenPath.path = null; ? ? ? ? } ? ? ? ? everyPenPaths.clear(); ? ? ? ? invalidate(); ? ? } ? ? public void back() { ? ? ? ? int count = everyPenPaths.size(); ? ? ? ? if (count < 1) ? ? ? ? ? ? return; ? ? ? ? EveryPenPath everyPenPath = everyPenPaths.get(count - 1); ? ? ? ? everyPenPath.path.reset(); ? ? ? ? everyPenPath.path.close(); ? ? ? ? everyPenPath.path = null; ? ? ? ? everyPenPaths.remove(count - 1); ? ? ? ? invalidate(); ? ? } ? ? @Override ? ? protected void onDraw(Canvas canvas) { ? ? ? ? super.onDraw(canvas); ? ? ? ? if (state == State.START) { ? ? ? ? ? ? /*先繪制完整筆畫*/ ? ? ? ? ? ? for (EveryPenPath e : everyPenPaths) { ? ? ? ? ? ? ? ? canvas.drawPath(e.path, mPaint); ? ? ? ? ? ? } ? ? ? ? ? ? //當(dāng)前進(jìn)行中的 ?path!=null ? ? ? ? ? ? if (path != null) { ? ? ? ? ? ? ? ? canvas.drawPath(path, mPaint); ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? @Override ? ? public boolean onTouchEvent(MotionEvent event) { ? ? ? ? if (state == State.START) { ? ? ? ? ? ? if (event.getAction() == MotionEvent.ACTION_UP) { ? ? ? ? ? ? ? ? actionUp(event); ? ? ? ? ? ? ? ? invalidate(); ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? ? ? if (event.getAction() == MotionEvent.ACTION_MOVE) { ? ? ? ? ? ? ? ? actionMove(event); ? ? ? ? ? ? ? ? invalidate(); ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? ? ? if (event.getAction() == MotionEvent.ACTION_DOWN) { ? ? ? ? ? ? ? ? actionDown(event); ? ? ? ? ? ? ? ? invalidate(); ? ? ? ? ? ? ? ? return true; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return super.onTouchEvent(event); ? ? } ? ? private void actionUp(MotionEvent event) { ? ? ? ? actionMove(event); ? ? ? ? /*構(gòu)成一個(gè)筆畫*/ ? ? ? ? EveryPenPath everyPenPath = new EveryPenPath(); ? ? ? ? everyPenPath.path = path; ? ? ? ? everyPenPaths.add(everyPenPath); ? ? ? ? //將當(dāng)前畫筆置位null; ? ? ? ? path = null; ? ? } ? ? /**/ ? ? private void actionMove(MotionEvent event) { ? ? ? ? /*每次移動(dòng)去繪制貝塞爾曲線*/ ? ? ? ? float cX = event.getX(); ? ? ? ? float cY = event.getY(); ? ? ? ? float dX = Math.abs(cX - x);//變化量 ? ? ? ? float dY = Math.abs(cY - y); ? ? ? ? if (dX >= beierThreshold || dY >= beierThreshold) { ? ? ? ? ? ? float rX = x + (cX - x) / 2; ? ? ? ? ? ? float rY = y + (cY - y) / 2; ? ? ? ? ? ? path.quadTo(rX, rY, cX, cY); ? ? ? ? ? ? //下次的x 域y 將重新計(jì)算 ? ? ? ? ? ? x = cX; ? ? ? ? ? ? y = cY; ? ? ? ? } ? ? } ? ? /*開始時(shí)*/ ? ? private void actionDown(MotionEvent event) { ? ? ? ? path = new Path(); ? ? ? ? x = event.getX(); ? ? ? ? y = event.getY(); ? ? ? ? path.moveTo(x, y); ? ? } ? ? public void setBeierThreshold(int beierThreshold) { ? ? ? ? this.beierThreshold = beierThreshold; ? ? } ? ? public void setStrokeWidth(int strokeWidth) { ? ? ? ? this.strokeWidth = strokeWidth; ? ? } ? ? public void setColor(int color) { ? ? ? ? this.color = color; ? ? } ? ? public Bitmap getResult(int bgColor) { ? ? ? ? if (everyPenPaths.size() == 0) ? ? ? ? ? ? return null; ? ? ? ? Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); ? ? ? ? Canvas canvas = new Canvas(bitmap); ? ? ? ? canvas.drawColor(bgColor); ? ? ? ? for (int i = 0; i < everyPenPaths.size(); i++) { ? ? ? ? ? ? if (mPaint == null) { ? ? ? ? ? ? ? ? initPaint(); ? ? ? ? ? ? } ? ? ? ? ? ? canvas.drawPath(everyPenPaths.get(i).path, mPaint); ? ? ? ? } ? ? ? ? return bitmap; ? ? } ? ? public Bitmap getResult() { ? ? ? ? return getResult(Color.WHITE); ? ? } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android中實(shí)現(xiàn)長(zhǎng)按修改ListView對(duì)象的內(nèi)容
這篇文章主要給大家介紹了在Android中實(shí)現(xiàn)長(zhǎng)按修改ListView對(duì)象內(nèi)容的相關(guān)資料,文中給出了完整的示例代碼,相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。2017-02-02詳解 android 光線傳感器 light sensor的使用
這篇文章主要介紹了詳解 android 光線傳感器 light sensor的使用的相關(guān)資料,需要的朋友可以參考下2017-06-06Android冷啟動(dòng)優(yōu)化的3個(gè)小案例分享
為了提高App的冷啟動(dòng)耗時(shí),除了在常規(guī)的業(yè)務(wù)側(cè)進(jìn)行耗時(shí)代碼優(yōu)化之外,為了進(jìn)一步縮短啟動(dòng)耗時(shí),需要在純技術(shù)測(cè)做一些優(yōu)化探索,本期我們從類預(yù)加載、Retrofit 、ARouter方面進(jìn)行了進(jìn)一步的優(yōu)化,感興趣的同學(xué)跟著小編一起來看看吧2023-07-07Android實(shí)現(xiàn)面包屑功能的代碼(支持Fragment聯(lián)動(dòng))
這篇文章主要介紹了Android實(shí)現(xiàn)面包屑功能的代碼(支持Fragment聯(lián)動(dòng)),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05詳解Flutter如何在單個(gè)屏幕上實(shí)現(xiàn)多個(gè)列表
這篇文章主要為大家詳細(xì)介紹了Flutter如何在單個(gè)屏幕上實(shí)現(xiàn)多個(gè)列表,這些列表可以水平排列、網(wǎng)格格式、垂直排列,甚至是這些常用布局的組合,感興趣的小伙伴可以了解下2023-11-11Android startActivityForResult實(shí)例詳解
這篇文章主要介紹了Android startActivityForResult實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05Android實(shí)現(xiàn)自定義圓角對(duì)話框Dialog的示例代碼
項(xiàng)目中多處用到對(duì)話框,本篇文章主要介紹了Android實(shí)現(xiàn)圓角對(duì)話框Dialog的示例代碼,有興趣的可以了解一下。2017-03-03