|
|
@@ -0,0 +1,649 @@
|
|
|
+package com.usai.redant.rautils.signature;
|
|
|
+
|
|
|
+import android.app.Activity;
|
|
|
+import android.content.Context;
|
|
|
+import android.graphics.Bitmap;
|
|
|
+import android.graphics.Canvas;
|
|
|
+import android.graphics.Color;
|
|
|
+import android.graphics.Paint;
|
|
|
+import android.graphics.PorterDuff;
|
|
|
+import android.graphics.PorterDuffXfermode;
|
|
|
+import android.graphics.RectF;
|
|
|
+import android.support.annotation.Nullable;
|
|
|
+import android.util.AttributeSet;
|
|
|
+import android.util.DisplayMetrics;
|
|
|
+import android.util.Log;
|
|
|
+import android.view.MotionEvent;
|
|
|
+import android.view.View;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+
|
|
|
+public class SignatureView extends View {
|
|
|
+
|
|
|
+
|
|
|
+ //画笔参数
|
|
|
+ public int penColor = Color.BLACK;//笔的颜色
|
|
|
+ public int penWidth = 25;//设置笔的宽度
|
|
|
+
|
|
|
+ public double DIS_VEL_CAL_FACTOR = 0.01f;//这个控制笔锋的控制值
|
|
|
+
|
|
|
+ //手指在移动的控制笔的变化率 这个值越大,线条的粗细越加明显
|
|
|
+ //float WIDTH_THRES_MAX = 0.6f;
|
|
|
+ float WIDTH_THRES_MAX = 0.6f;
|
|
|
+
|
|
|
+ //计算线宽
|
|
|
+ public double mLastVel;
|
|
|
+
|
|
|
+ public double mLastWidth;
|
|
|
+
|
|
|
+ //线平滑
|
|
|
+ public Bezier mBezier = new Bezier();
|
|
|
+ //绘制计算的次数,数值越小计算的次数越多,需要折中
|
|
|
+ int STEPFACTOR = 10;
|
|
|
+
|
|
|
+ int Touch_Action=-1;
|
|
|
+
|
|
|
+ private Bitmap mBitmap;
|
|
|
+ private Paint mPaint;//画笔
|
|
|
+ private Canvas mbmpCanvas;//画布
|
|
|
+
|
|
|
+ int idx_drawstart=0;
|
|
|
+ int idx_drawend=0;
|
|
|
+// private VelocityTracker mVelocityTracker;
|
|
|
+
|
|
|
+ private ArrayList<ControllerPoint> mMeasurePointList=new ArrayList<>();
|
|
|
+
|
|
|
+ private ArrayList<ControllerPoint> mComputePointList=new ArrayList<>();
|
|
|
+
|
|
|
+ private ControllerPoint mLastPoint = new ControllerPoint(0, 0);
|
|
|
+ private ControllerPoint mCurPoint;
|
|
|
+
|
|
|
+// ControllerPoint last_Point =null;
|
|
|
+ int idx_lastdraw=0;
|
|
|
+ int idx_lastaction = 0;
|
|
|
+ public SignatureView(Context context) {
|
|
|
+ super(context);
|
|
|
+ init(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ public SignatureView(Context context, @Nullable AttributeSet attrs) {
|
|
|
+ super(context, attrs);
|
|
|
+ init(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ public SignatureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
|
|
+ super(context, attrs, defStyleAttr);
|
|
|
+ init(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ public SignatureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
|
+ super(context, attrs, defStyleAttr, defStyleRes);
|
|
|
+ init(context);
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ protected void finalize() throws Throwable
|
|
|
+ {
|
|
|
+// mVelocityTracker.recycle();
|
|
|
+ super.finalize();
|
|
|
+
|
|
|
+ // other finalization code...
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ void init(final Context context)
|
|
|
+ {
|
|
|
+
|
|
|
+ post(new Runnable() {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ Log.d("viewpost", "run: ");
|
|
|
+// mVelocityTracker = VelocityTracker.obtain();
|
|
|
+ initPaint();
|
|
|
+ initCanvas( context);
|
|
|
+ Log.d("viewpost", "init done ");
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initPaint() {
|
|
|
+ mPaint = new Paint();
|
|
|
+ mPaint.setColor(penColor);
|
|
|
+ mPaint.setStrokeWidth(penWidth);
|
|
|
+ mPaint.setStyle(Paint.Style.FILL);
|
|
|
+ mPaint.setStrokeCap(Paint.Cap.ROUND);//结束的笔画为圆心
|
|
|
+ mPaint.setStrokeJoin(Paint.Join.ROUND);//连接处元
|
|
|
+ mPaint.setAlpha(0xFF);
|
|
|
+ mPaint.setAntiAlias(true);
|
|
|
+ mPaint.setStrokeMiter(1.0f);
|
|
|
+// mStokeBrushPen.setPaint(mPaint);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initCanvas(Context context) {
|
|
|
+ DisplayMetrics dm = new DisplayMetrics();
|
|
|
+ ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// mBitmap = Bitmap.createBitmap(dm.widthPixels, dm.heightPixels, Bitmap.Config.ARGB_8888);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ int width = View.MeasureSpec.makeMeasureSpec(0,
|
|
|
+ View.MeasureSpec.UNSPECIFIED);
|
|
|
+ int height = View.MeasureSpec.makeMeasureSpec(0,
|
|
|
+ View.MeasureSpec.UNSPECIFIED);
|
|
|
+ measure(width, height);
|
|
|
+
|
|
|
+ mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
|
|
|
+ mbmpCanvas = new Canvas(mBitmap);
|
|
|
+ //设置画布的颜色的问题
|
|
|
+ mbmpCanvas.drawColor(Color.TRANSPARENT);
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public boolean onTouchEvent(MotionEvent event) {
|
|
|
+
|
|
|
+
|
|
|
+ long startTime = System.currentTimeMillis(); //起始时间
|
|
|
+
|
|
|
+// mVelocityTracker.addMovement(event);
|
|
|
+
|
|
|
+// mIsCanvasDraw = true;
|
|
|
+ MotionEvent event2 = MotionEvent.obtain(event);
|
|
|
+// mStokeBrushPen.onTouchEvent(event2, mCanvas);
|
|
|
+ //event会被下一次事件重用,这里必须生成新的,否则会有问题
|
|
|
+ //getActionMask:触摸的动作,按下,抬起,滑动,多点按下,多点抬起
|
|
|
+ Touch_Action = event2.getActionMasked();
|
|
|
+ switch (Touch_Action) {
|
|
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
+ TouchDown(createMotionElement(event2), mbmpCanvas);
|
|
|
+// if (mGetTimeListner!=null)
|
|
|
+// mGetTimeListner.stopTime();
|
|
|
+ break;
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
+
|
|
|
+ Log.d("move", "onTouchEvent: move "+event.getX()+" , "+event.getY());
|
|
|
+// Bezier bezier = new Bezier(lastPoint, newPoint);
|
|
|
+ TouchMove(createMotionElement(event2), mbmpCanvas);
|
|
|
+// if (mGetTimeListner!=null)
|
|
|
+// mGetTimeListner.stopTime();
|
|
|
+ break;
|
|
|
+ case MotionEvent.ACTION_UP:
|
|
|
+ TouchUp(createMotionElement(event2), mbmpCanvas);
|
|
|
+// long time = System.currentTimeMillis();
|
|
|
+// if (mGetTimeListner!=null)
|
|
|
+// mGetTimeListner.getTime(time);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+
|
|
|
+ Log.i("onTouchEvent", "OTHER ACTION "+Touch_Action);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ long endTime = System.currentTimeMillis(); //结束时间
|
|
|
+ long runTime = endTime - startTime;
|
|
|
+ Log.i("onTouchEvent", String.format("方法使用时间 %d ms", runTime));
|
|
|
+// partialrefresh=true;
|
|
|
+ invalidate();
|
|
|
+
|
|
|
+
|
|
|
+// invalidate((int)event.getX()-50,(int)event.getY()-50,(int)event.getX()+50,(int)event.getY()+50);
|
|
|
+// updateView(vCanvas);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ ControllerPoint getControlPoint(MotionElement element)
|
|
|
+ {
|
|
|
+ return new ControllerPoint(element.x, element.y);
|
|
|
+ }
|
|
|
+ void TouchDown(MotionElement element, Canvas c)
|
|
|
+ {
|
|
|
+ ControllerPoint cp = getControlPoint(element);
|
|
|
+// last_Point = cp;
|
|
|
+ idx_lastaction=mMeasurePointList.size();
|
|
|
+// mMeasurePointList.add(cp);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (mPaint==null){
|
|
|
+ throw new NullPointerException("paint 笔不可能为null哦");
|
|
|
+ }
|
|
|
+// if (getNewPaint(mPaint)!=null){
|
|
|
+// Paint paint=getNewPaint(mPaint);
|
|
|
+// mPaint=paint;
|
|
|
+// //当然了,不要因为担心内存泄漏,在每个变量使用完成后都添加xxx=null,
|
|
|
+// // 对于消除过期引用的最好方法,就是让包含该引用的变量结束生命周期,而不是显示的清空
|
|
|
+// paint=null;
|
|
|
+// System.out.println("shiming 当绘制的时候是否为新的paint"+mPaint+"原来的对象是否销毁了paint=="+paint);
|
|
|
+// }
|
|
|
+ mMeasurePointList.clear();
|
|
|
+ //如果在brush字体这里接受到down的事件,把下面的这个集合清空的话,那么绘制的内容会发生改变
|
|
|
+ //不清空的话,也不可能
|
|
|
+ mComputePointList.clear();
|
|
|
+ //记录down的控制点的信息
|
|
|
+ ControllerPoint curPoint = new ControllerPoint(element.x, element.y);
|
|
|
+ //如果用笔画的画我的屏幕,记录他宽度的和压力值的乘,但是哇,
|
|
|
+ if (element.tooltype == MotionEvent.TOOL_TYPE_STYLUS) {
|
|
|
+ mLastWidth = element.pressure * penWidth;
|
|
|
+ } else {
|
|
|
+ //如果是手指画的,我们取他的0.8
|
|
|
+ mLastWidth = 0.8 * penWidth;
|
|
|
+ }
|
|
|
+ //down下的点的宽度
|
|
|
+ curPoint.width = (float) mLastWidth;
|
|
|
+ mLastVel = 0;
|
|
|
+ mMeasurePointList.add(curPoint);
|
|
|
+ //记录当前的点
|
|
|
+ mLastPoint = curPoint;
|
|
|
+ }
|
|
|
+ void TouchMove(MotionElement element, Canvas c)
|
|
|
+ {
|
|
|
+// ControllerPoint cp = getControlPoint(element);
|
|
|
+// last_Point = cp;
|
|
|
+ idx_lastaction=mMeasurePointList.size();
|
|
|
+// mMeasurePointList.add(cp);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ ControllerPoint curPoint = new ControllerPoint(element.x, element.y);
|
|
|
+
|
|
|
+ if(mMeasurePointList.size()>0)
|
|
|
+ {
|
|
|
+ ControllerPoint lp=mMeasurePointList.get(mMeasurePointList.size()-1);
|
|
|
+ if(lp.x==curPoint.x&& lp.y==curPoint.y)
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ double deltaX = curPoint.x - mLastPoint.x;
|
|
|
+ double deltaY = curPoint.y - mLastPoint.y;
|
|
|
+ //deltaX和deltay平方和的二次方根 想象一个例子 1+1的平方根为1.4 (x²+y²)开根号
|
|
|
+ //同理,当滑动的越快的话,deltaX+deltaY的值越大,这个越大的话,curDis也越大
|
|
|
+ double curDis = Math.hypot(deltaX, deltaY);
|
|
|
+ //我们求出的这个值越小,画的点或者是绘制椭圆形越多,这个值越大的话,绘制的越少,笔就越细,宽度越小
|
|
|
+ double curVel = curDis * DIS_VEL_CAL_FACTOR;
|
|
|
+ double curWidth;
|
|
|
+ //点的集合少,我们得必须改变宽度,每次点击的down的时候,这个事件
|
|
|
+ if (mMeasurePointList.size() < 2) {
|
|
|
+ if (element.tooltype == MotionEvent.TOOL_TYPE_STYLUS) {
|
|
|
+ curWidth = element.pressure * penWidth;
|
|
|
+ } else {
|
|
|
+ curWidth = calcNewWidth(curVel, mLastVel, curDis, 1.5,
|
|
|
+ mLastWidth);
|
|
|
+ }
|
|
|
+ curPoint.width = (float) curWidth;
|
|
|
+ mBezier.init(mLastPoint, curPoint);
|
|
|
+ } else {
|
|
|
+ mLastVel = curVel;
|
|
|
+ if (element.tooltype == MotionEvent.TOOL_TYPE_STYLUS) {
|
|
|
+ curWidth = element.pressure * penWidth;
|
|
|
+ } else {
|
|
|
+ //由于我们手机是触屏的手机,滑动的速度也不慢,所以,一般会走到这里来
|
|
|
+ //阐明一点,当滑动的速度很快的时候,这个值就越小,越慢就越大,依靠着mlastWidth不断的变换
|
|
|
+ curWidth = calcNewWidth(curVel, mLastVel, curDis, 1.5,
|
|
|
+ mLastWidth);
|
|
|
+ }
|
|
|
+ curPoint.width = (float) curWidth;
|
|
|
+ Log.i("onTouchEvent", "move width"+curWidth);
|
|
|
+ mBezier.addNode(curPoint);
|
|
|
+ }
|
|
|
+ //每次移动的话,这里赋值新的值
|
|
|
+ mLastWidth = curWidth;
|
|
|
+ mMeasurePointList.add(curPoint);
|
|
|
+ moveNeetToDo(curDis);
|
|
|
+ mLastPoint = curPoint;
|
|
|
+ }
|
|
|
+ void TouchUp(MotionElement element, Canvas c)
|
|
|
+ {
|
|
|
+ ControllerPoint cp = getControlPoint(element);
|
|
|
+// last_Point = cp;
|
|
|
+ idx_lastaction=mMeasurePointList.size();
|
|
|
+// mMeasurePointList.add(cp);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ mCurPoint = new ControllerPoint(element.x, element.y);
|
|
|
+ double deltaX = mCurPoint.x - mLastPoint.x;
|
|
|
+ double deltaY = mCurPoint.y - mLastPoint.y;
|
|
|
+ double curDis = Math.hypot(deltaX, deltaY);
|
|
|
+ //如果用笔画的画我的屏幕,记录他宽度的和压力值的乘,但是哇,这个是不会变的
|
|
|
+ if (element.tooltype == MotionEvent.TOOL_TYPE_STYLUS) {
|
|
|
+ mCurPoint.width = (float) (element.pressure * penWidth);
|
|
|
+ } else {
|
|
|
+ mCurPoint.width = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ mMeasurePointList.add(mCurPoint);
|
|
|
+
|
|
|
+ mBezier.addNode(mCurPoint);
|
|
|
+
|
|
|
+ int steps = 1 + (int) curDis / STEPFACTOR;
|
|
|
+ double step = 1.0 / steps;
|
|
|
+ for (double t = 0; t < 1.0; t += step) {
|
|
|
+ ControllerPoint point = mBezier.getPoint(t);
|
|
|
+ mComputePointList.add(point);
|
|
|
+ }
|
|
|
+ //
|
|
|
+ mBezier.end();
|
|
|
+ for (double t = 0; t < 1.0; t += step) {
|
|
|
+ ControllerPoint point = mBezier.getPoint(t);
|
|
|
+ mComputePointList.add(point);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 手指up 我画到纸上上
|
|
|
+// draw(canvas);
|
|
|
+ //每次抬起手来,就把集合清空,在水彩笔的那个地方,如果啊,我说如果不清空的话,每次抬起手来,
|
|
|
+ // 在onDown下去的话,最近画的线的透明度有改变,所以这里clear下线的集合
|
|
|
+ clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void clear() {
|
|
|
+ mComputePointList.clear();
|
|
|
+ mMeasurePointList.clear();
|
|
|
+ idx_drawstart=0;
|
|
|
+ idx_drawend=0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清除画布,记得清除点的集合
|
|
|
+ */
|
|
|
+ public void reset() {
|
|
|
+ mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
|
|
+ mbmpCanvas.drawPaint(mPaint);
|
|
|
+ mPaint.setXfermode(null);
|
|
|
+ clear();
|
|
|
+
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * event.getPressure(); //LCD可以感应出用户的手指压力,当然具体的级别由驱动和物理硬件决定的,我的手机上为1
|
|
|
+ * @param motionEvent
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private MotionElement createMotionElement(MotionEvent motionEvent) {
|
|
|
+
|
|
|
+ MotionElement motionElement = new MotionElement(motionEvent.getX(), motionEvent.getY(),
|
|
|
+ motionEvent.getPressure(), motionEvent.getToolType(0));
|
|
|
+ return motionElement;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void onDraw(Canvas canvas) {
|
|
|
+
|
|
|
+
|
|
|
+// Rect r=canvas.getClipBounds();
|
|
|
+// vCanvas = canvas;
|
|
|
+ long startTime = System.currentTimeMillis(); //起始时间
|
|
|
+
|
|
|
+
|
|
|
+ ControllerPoint start = null;
|
|
|
+ ControllerPoint end = null;
|
|
|
+// if(mHWPointList.size()>0)
|
|
|
+// {
|
|
|
+// start = mHWPointList.get(idx_lastdraw);
|
|
|
+//
|
|
|
+// end = mHWPointList.get(idx_lastaction);
|
|
|
+// }
|
|
|
+
|
|
|
+ if(mComputePointList.size()>0)
|
|
|
+ {
|
|
|
+ start=mComputePointList.get(idx_drawstart);
|
|
|
+ end=mComputePointList.get(idx_drawend);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (Touch_Action) {
|
|
|
+ case MotionEvent.ACTION_DOWN:
|
|
|
+ if(end!=null) {
|
|
|
+ idx_lastdraw = idx_lastaction;
|
|
|
+ mbmpCanvas.drawPoint(end.x, end.y, mPaint);
|
|
|
+
|
|
|
+ idx_drawstart = idx_drawend;
|
|
|
+// if (mGetTimeListner!=null)
|
|
|
+// mGetTimeListner.stopTime();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Log.d("ACTION_DOWN", "onDraw: end is null");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MotionEvent.ACTION_MOVE:
|
|
|
+ if(start!=null&& end!=null)
|
|
|
+ {
|
|
|
+ idx_lastdraw=idx_lastaction;
|
|
|
+// mbmpCanvas.drawLine(start.x,start.y,end.x,end.y,mPaint);
|
|
|
+
|
|
|
+
|
|
|
+ for(int i=idx_drawstart;i<idx_drawend;i++)
|
|
|
+// for(int i=0;i<mComputePointList.size()-1;i++)
|
|
|
+ {
|
|
|
+// mbmpCanvas.drawLine(mComputePointList.get(i).x,mComputePointList.get(i).y,mComputePointList.get(i+1).x,mComputePointList.get(i+1).y,mPaint);
|
|
|
+
|
|
|
+ drawLine(mbmpCanvas,mComputePointList.get(i).x,mComputePointList.get(i).y,mComputePointList.get(i).width,mComputePointList.get(i+1).x,mComputePointList.get(i+1).y,mComputePointList.get(i+1).width,mPaint);
|
|
|
+ }
|
|
|
+
|
|
|
+ Log.d("ACTION_MOVE", "onDraw: "+idx_drawstart+"------"+idx_drawend+"....."+mComputePointList.size());
|
|
|
+ idx_drawstart = idx_drawend;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Log.d("ACTION_MOVE", "onDraw: "+(start==null)+(end==null));
|
|
|
+ }
|
|
|
+
|
|
|
+// if (mGetTimeListner!=null)
|
|
|
+// mGetTimeListner.stopTime();
|
|
|
+ break;
|
|
|
+ case MotionEvent.ACTION_UP:
|
|
|
+ idx_lastdraw=idx_lastaction;
|
|
|
+// mbmpCanvas.drawLine(start.x,start.y,end.x,end.y,mPaint);
|
|
|
+// long time = System.currentTimeMillis();
|
|
|
+// if (mGetTimeListner!=null)
|
|
|
+// mGetTimeListner.getTime(time);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ idx_lastdraw=idx_lastaction;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ canvas.drawBitmap(mBitmap, 0, 0, mPaint);
|
|
|
+ super.onDraw(canvas);
|
|
|
+ long endTime = System.currentTimeMillis(); //结束时间
|
|
|
+ long runTime = endTime - startTime;
|
|
|
+ Log.i("绘制", String.format("方法使用时间 %d ms", runTime)+"++++++++++++ ");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //各种计算
|
|
|
+
|
|
|
+ //计算宽度
|
|
|
+ public double calcNewWidth(double curVel, double lastVel, double curDis,
|
|
|
+ double factor, double lastWidth) {
|
|
|
+ double calVel = curVel * 0.6 + lastVel * (1 - 0.6);
|
|
|
+ //返回指定数字的自然对数
|
|
|
+ //手指滑动的越快,这个值越小,为负数
|
|
|
+ double vfac = Math.log(factor * 2.0f) * (-calVel);
|
|
|
+ //此方法返回值e,其中e是自然对数的基数。
|
|
|
+ //Math.exp(vfac) 变化范围为0 到1 当手指没有滑动的时候 这个值为1 当滑动很快的时候无线趋近于0
|
|
|
+ //在次说明下,当手指抬起来,这个值会变大,这也就说明,抬起手太慢的话,笔锋效果不太明显
|
|
|
+ //这就说明为什么笔锋的效果不太明显
|
|
|
+ double calWidth = penWidth * Math.exp(vfac);
|
|
|
+
|
|
|
+ //滑动的速度越快的话,mMoveThres也越大
|
|
|
+ double mMoveThres = curDis * 0.01f;
|
|
|
+ //对之值最大的地方进行控制
|
|
|
+ if (mMoveThres > WIDTH_THRES_MAX) {
|
|
|
+ mMoveThres = WIDTH_THRES_MAX;
|
|
|
+ }
|
|
|
+ // TODO: 2018/2/24 以下的方法 可以删除掉 原因是抽取了一下 ,本来不应该在这里的出现的 不好意思
|
|
|
+// //滑动越慢的情况下,得到的calWidth 和上面的calwidth 相差的值不一样
|
|
|
+//
|
|
|
+// //滑动的越快的话,第一个判断会走
|
|
|
+// if (Math.abs(calWidth - mBaseWidth) / mBaseWidth > mMoveThres) {
|
|
|
+// if (calWidth > mBaseWidth) {
|
|
|
+// calWidth = mBaseWidth * (1 + mMoveThres);
|
|
|
+// } else {
|
|
|
+// calWidth = mBaseWidth * (1 - mMoveThres);
|
|
|
+// }
|
|
|
+// //滑动的越慢的话,第二个判断会走 基本上在屏幕上手指基本上没有走动的时候 ,就会走这个方法
|
|
|
+// } else if (Math.abs(calWidth - lastWidth) / lastWidth > mMoveThres) {
|
|
|
+// if (calWidth > lastWidth) {
|
|
|
+// calWidth = lastWidth * (1 + mMoveThres);
|
|
|
+// } else {
|
|
|
+// calWidth = lastWidth * (1 - mMoveThres);
|
|
|
+// }
|
|
|
+// }
|
|
|
+ return calWidth;
|
|
|
+ }
|
|
|
+// 生成计算点(贝塞尔曲线)
|
|
|
+ protected void moveNeetToDo(double curDis) {
|
|
|
+ int steps = 1 + (int) curDis / STEPFACTOR;
|
|
|
+ Log.d("pen debug", "steps "+steps);
|
|
|
+ if(steps==1)
|
|
|
+ steps=1;
|
|
|
+ double step = 1.0 / steps;
|
|
|
+// idx_drawstart = mComputePointList.size()-1;
|
|
|
+// if(idx_drawstart<0)
|
|
|
+// idx_drawstart = 0;
|
|
|
+ for (double t = 0; t < 1.0; t += step) {
|
|
|
+ ControllerPoint point = mBezier.getPoint(t);
|
|
|
+ //过滤坐标重复的点
|
|
|
+ if(mComputePointList.size()>0)
|
|
|
+ {
|
|
|
+ ControllerPoint last = mComputePointList.get(mComputePointList.size()-1);
|
|
|
+ if(last.x==point.x&&last.y==point.y)
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Log.d("pen debug", "Compute Point width "+point.width);
|
|
|
+ mComputePointList.add(point);
|
|
|
+ }
|
|
|
+
|
|
|
+ idx_drawend = mComputePointList.size()-1;
|
|
|
+
|
|
|
+// Log.d("pen debug", "size() "+mComputePointList.size()+",,,"+(idx_drawend-idx_drawstart));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private void drawLine(Canvas canvas, double x0, double y0, double w0, double x1, double y1, double w1, Paint paint) {
|
|
|
+ //求两个数字的平方根 x的平方+y的平方在开方记得X的平方+y的平方=1,这就是一个园
|
|
|
+ double curDis = Math.hypot(x0 - x1, y0 - y1);
|
|
|
+ int steps = 1;
|
|
|
+ if (paint.getStrokeWidth() < 6) {
|
|
|
+ steps = 1 + (int) (curDis / 2);
|
|
|
+ } else if (paint.getStrokeWidth() > 60) {
|
|
|
+ steps = 1 + (int) (curDis / 4);
|
|
|
+ } else {
|
|
|
+ steps = 1 + (int) (curDis / 3);
|
|
|
+ }
|
|
|
+ double deltaX = (x1 - x0) / steps;
|
|
|
+ double deltaY = (y1 - y0) / steps;
|
|
|
+ double deltaW = (w1 - w0) / steps;
|
|
|
+ double x = x0;
|
|
|
+ double y = y0;
|
|
|
+ double w = w0;
|
|
|
+
|
|
|
+ Log.d("drawLine","line width "+w+","+w0+","+deltaW);
|
|
|
+ for (int i = 0; i < steps; i++) {
|
|
|
+ //都是用于表示坐标系中的一块矩形区域,并可以对其做一些简单操作
|
|
|
+ //精度不一样。Rect是使用int类型作为数值,RectF是使用float类型作为数值。
|
|
|
+ // Rect rect = new Rect();
|
|
|
+ RectF oval = new RectF();
|
|
|
+ oval.set((float) (x - w / 4.0f), (float) (y - w / 2.0f), (float) (x + w / 4.0f), (float) (y + w / 2.0f));
|
|
|
+ // oval.set((float)(x+w/4.0f), (float)(y+w/4.0f), (float)(x-w/4.0f), (float)(y-w/4.0f));
|
|
|
+ //最基本的实现,通过点控制线,绘制椭圆
|
|
|
+ canvas.drawOval(oval, paint);
|
|
|
+ x += deltaX;
|
|
|
+ y += deltaY;
|
|
|
+ w += deltaW;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private int mBackColor = Color.TRANSPARENT;
|
|
|
+ /**
|
|
|
+ * 逐行扫描 清楚边界空白。功能是生成一张bitmap位于正中间,不是位于顶部,此关键的是我们画布需要
|
|
|
+ * 成透明色才能生效
|
|
|
+ * @param blank 边距留多少个像素
|
|
|
+ * @return tks github E-signature
|
|
|
+ */
|
|
|
+ public Bitmap clearBlank(int blank) {
|
|
|
+ if (mBitmap != null) {
|
|
|
+ int HEIGHT = mBitmap.getHeight();//1794
|
|
|
+ int WIDTH = mBitmap.getWidth();//1080
|
|
|
+ int top = 0, left = 0, right = 0, bottom = 0;
|
|
|
+ int[] pixs = new int[WIDTH];
|
|
|
+ boolean isStop;
|
|
|
+ for (int y = 0; y < HEIGHT; y++) {
|
|
|
+ mBitmap.getPixels(pixs, 0, WIDTH, 0, y, WIDTH, 1);
|
|
|
+ isStop = false;
|
|
|
+ for (int pix : pixs) {
|
|
|
+ if (pix != mBackColor) {
|
|
|
+
|
|
|
+ top = y;
|
|
|
+ isStop = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isStop) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (int y = HEIGHT - 1; y >= 0; y--) {
|
|
|
+ mBitmap.getPixels(pixs, 0, WIDTH, 0, y, WIDTH, 1);
|
|
|
+ isStop = false;
|
|
|
+ for (int pix : pixs) {
|
|
|
+ if (pix != mBackColor) {
|
|
|
+ bottom = y;
|
|
|
+ isStop = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isStop) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ pixs = new int[HEIGHT];
|
|
|
+ for (int x = 0; x < WIDTH; x++) {
|
|
|
+ mBitmap.getPixels(pixs, 0, 1, x, 0, 1, HEIGHT);
|
|
|
+ isStop = false;
|
|
|
+ for (int pix : pixs) {
|
|
|
+ if (pix != mBackColor) {
|
|
|
+ left = x;
|
|
|
+ isStop = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isStop) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (int x = WIDTH - 1; x > 0; x--) {
|
|
|
+ mBitmap.getPixels(pixs, 0, 1, x, 0, 1, HEIGHT);
|
|
|
+ isStop = false;
|
|
|
+ for (int pix : pixs) {
|
|
|
+ if (pix != mBackColor) {
|
|
|
+ right = x;
|
|
|
+ isStop = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isStop) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (blank < 0) {
|
|
|
+ blank = 0;
|
|
|
+ }
|
|
|
+ left = left - blank > 0 ? left - blank : 0;
|
|
|
+ top = top - blank > 0 ? top - blank : 0;
|
|
|
+ right = right + blank > WIDTH - 1 ? WIDTH - 1 : right + blank;
|
|
|
+ bottom = bottom + blank > HEIGHT - 1 ? HEIGHT - 1 : bottom + blank;
|
|
|
+ return Bitmap.createBitmap(mBitmap, left, top, right - left, bottom - top);
|
|
|
+ } else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|