美文网首页view
Kevin Learn Android:Android 手签板

Kevin Learn Android:Android 手签板

作者: Kevin_小飞象 | 来源:发表于2021-12-15 12:00 被阅读0次
    每日一图.jpg

    前言

    Android 屏幕手写签名的原理就是把手机屏幕当作画板,把用户手指当作画笔,手指在屏幕上在屏幕上划来划去,屏幕就会显示手指的移动轨迹,就像画笔在画板上写字一样。实现手写签名需要结合绘图的路径工具 Path ,在有按下动作时调用 Path 对象的 moveTo 方法,将路径起始点移动到触摸点;在有移动操作时调用 Path 对象的 quadTo 方法,将记录本次触摸点与上次触摸点之间的路径;在有移动操作与提起动作时调用 Canvas 对象的 drawPath 方法,将本次触摸绘制在画布上。

    效果图

    手写签名.jpg

    功能

    • 空白画板手写
    • 实现笔锋效果
    • 支持橡皮擦,撤回/恢复,清空画布功能
    • 支持画笔颜色大小设置
    • 支持传入初始图片
    • 支持画布大小设置,文字区域裁剪
    • 主题颜色设置
    • 支持传入初始显示图片

    代码

    1. 布局文件
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/rl_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:clipToPadding="true"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
    
        <include
            android:id="@+id/actionbar"
            layout="@layout/sign_actionbar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/sign_actionbar_height"
            android:layout_alignParentTop="true" />
    
        <com.hkt.handwritten.view.PaintView
            android:id="@+id/paint_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/divider"
            android:layout_below="@id/actionbar"
            android:layout_margin="12dp"
            android:background="@drawable/shape_dash_bg"/>
    
    
        <View
            android:id="@+id/divider"
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:layout_above="@+id/setting"
            android:background="@color/sign_border_gray" />
    
        <LinearLayout
            android:id="@+id/setting"
            android:layout_width="match_parent"
            android:layout_height="@dimen/y105"
            android:layout_alignParentBottom="true"
            android:background="@drawable/bottom_bg_shape"
            android:gravity="center"
            android:orientation="horizontal">
    
            <ImageView
                android:id="@+id/btn_hand"
                android:layout_width="@dimen/x38"
                android:layout_height="@dimen/y38"
                android:layout_alignParentLeft="true"
                android:layout_marginRight="@dimen/x40"
                android:scaleType="centerCrop"
                android:layout_weight="1"
                android:background="@drawable/sign_bg_btn_clicked"
                android:src="@mipmap/sign_ic_hand"
                android:visibility="gone" />
    
    
            <ImageView
                android:id="@+id/btn_undo"
                android:layout_width="@dimen/x78"
                android:layout_height="@dimen/y78"
                android:scaleType="centerCrop"
                android:layout_marginRight="@dimen/x40"
                android:layout_toLeftOf="@+id/btn_redo"
                android:padding="@dimen/x12"
                android:background="@drawable/sign_bg_btn_clicked"
                android:src="@mipmap/sign_ic_undo" />
    
            <ImageView
                android:id="@+id/btn_redo"
                android:layout_width="@dimen/x78"
                android:layout_height="@dimen/y78"
                android:scaleType="centerCrop"
                android:layout_marginRight="@dimen/x40"
                android:layout_toLeftOf="@+id/btn_clear"
                android:background="@drawable/sign_bg_btn_clicked"
                android:padding="@dimen/x12"
                android:src="@mipmap/sign_ic_redo" />
    
    
            <ImageView
                android:id="@+id/btn_pen"
                android:layout_width="@dimen/x78"
                android:layout_height="@dimen/y78"
                android:scaleType="centerCrop"
                android:layout_marginRight="@dimen/x40"
                android:layout_toLeftOf="@+id/btn_setting"
                android:padding="@dimen/x12"
                android:background="@drawable/sign_bg_btn_clicked"
                android:src="@mipmap/sign_ic_pen" />
    
            <ImageView
                android:id="@+id/btn_clear"
                android:layout_width="@dimen/x78"
                android:layout_height="@dimen/y78"
                android:layout_marginRight="@dimen/x40"
                android:layout_toLeftOf="@+id/btn_pen"
                android:padding="@dimen/x12"
                android:scaleType="centerCrop"
                android:background="@drawable/sign_bg_btn_clicked"
                android:src="@mipmap/sign_ic_clear" />
    
            <com.hkt.handwritten.view.CircleView
                android:id="@+id/btn_setting"
                android:layout_width="@dimen/x78"
                android:layout_height="@dimen/y78"
                android:layout_alignParentRight="true"
                android:padding="@dimen/x12"
                app:showOutBorder="false"
                app:sizeLevel="2" />
        </LinearLayout>
    </RelativeLayout>
    
    1. 核心代码 PaintView.java
    /**
     * Created on 2021/12/1 11:58
     *
     * @author Gong Youqiang
     */
    public class PaintView extends View {
        public static final int TYPE_PEN = 0;
        public static final int TYPE_ERASER = 1;
    
        private Paint mPaint;
        private Canvas mCanvas;
        private Bitmap mBitmap;
        private int strokeWidth;
        private BasePen mStokeBrushPen;
    
        /**
         * 是否允许写字
         */
        private boolean isFingerEnable = true;
        /**
         * 是否橡皮擦模式
         */
        private boolean isEraser = false;
    
        /**
         * 是否有绘制
         */
        private boolean hasDraw = false;
    
    
        /**
         * 画笔轨迹记录
         */
        private StepOperator mStepOperation;
    
        private StepCallback mCallback;
    
        /**
         * 是否可以撤销
         */
        private boolean mCanUndo;
        /**
         * 是否可以恢复
         */
        private boolean mCanRedo;
    
        private int mWidth;
        private int mHeight;
    
        private boolean isDrawing = false;//是否正在绘制
        private int toolType = 0;  //记录手写笔类型:触控笔/手指
    
        private Eraser eraser;
    
        public PaintView(Context context) {
            this(context, null);
        }
    
        public PaintView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public PaintView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
        }
    
        /**
         * 初始化画板
         *
         * @param width  画板宽度
         * @param height 画板高度
         * @param path   初始图片路径
         */
        public void init(int width, int height, String path) {
            this.mWidth = width;
            this.mHeight = height;
    
            mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444);
            mStokeBrushPen = new SteelPen();
    
            initPaint();
            initCanvas();
    
            mStepOperation = new StepOperator();
            if (!TextUtils.isEmpty(path)) {
                Bitmap bitmap = BitmapFactory.decodeFile(path);
                resize(bitmap, mWidth, mHeight);
            } else {
                mStepOperation.addBitmap(mBitmap);
            }
            //橡皮擦
            eraser = new Eraser(getResources().getDimensionPixelSize(R.dimen.sign_eraser_size));
        }
    
        /**
         * 初始画笔设置
         */
        private void initPaint() {
            strokeWidth = DisplayUtil.dip2px(getContext(), PaintSettingWindow.PEN_SIZES[PenConfig.PAINT_SIZE_LEVEL]);
            mPaint = new Paint();
            mPaint.setColor(PenConfig.PAINT_COLOR);
            mPaint.setStrokeWidth(strokeWidth);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setAlpha(0xFF);
            mPaint.setAntiAlias(true);
            mPaint.setStrokeMiter(1.0f);
            mStokeBrushPen.setPaint(mPaint);
        }
    
        private void initCanvas() {
            mCanvas = new Canvas(mBitmap);
            //设置画布的背景色为透明
            mCanvas.drawColor(Color.TRANSPARENT);
        }
    
    
        @Override
        protected void onDraw(Canvas canvas) {
            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
            if (!isEraser) {
                mStokeBrushPen.draw(canvas);
            }
            super.onDraw(canvas);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            getParent().requestDisallowInterceptTouchEvent(true);
            return super.dispatchTouchEvent(ev);
        }
    
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            toolType = event.getToolType(event.getActionIndex());
            if (!isFingerEnable && toolType != MotionEvent.TOOL_TYPE_STYLUS) {
                return false;
            }
            if (isEraser) {
                eraser.handleEraserEvent(event, mCanvas);
            } else {
                mStokeBrushPen.onTouchEvent(event, mCanvas);
            }
    
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    isDrawing = false;
                    break;
                case MotionEvent.ACTION_MOVE:
                    hasDraw = true;
                    mCanUndo = true;
                    isDrawing = true;
                    break;
                case MotionEvent.ACTION_CANCEL:
                    isDrawing = false;
                    break;
                case MotionEvent.ACTION_UP:
                    if (mStepOperation != null && isDrawing) {
                        mStepOperation.addBitmap(mBitmap);
                    }
                    mCanUndo = !mStepOperation.currentIsFirst();
                    mCanRedo = !mStepOperation.currentIsLast();
                    if (mCallback != null) {
                        mCallback.onOperateStatusChanged();
                    }
                    isDrawing = false;
                    break;
                default:
                    break;
            }
            invalidate();
            return true;
        }
    
        /**
         * @return 判断是否有绘制内容在画布上
         */
        public boolean isEmpty() {
            return !hasDraw;
        }
    
        /**
         * 撤销
         */
        public void undo() {
    
            if (mStepOperation == null || !mCanUndo) {
                return;
            }
            if (!mStepOperation.currentIsFirst()) {
                mCanUndo = true;
                mStepOperation.undo(mBitmap);
                hasDraw = true;
                invalidate();
    
                if (mStepOperation.currentIsFirst()) {
                    mCanUndo = false;
                    hasDraw = false;
                }
            } else {
                mCanUndo = false;
                hasDraw = false;
            }
            if (!mStepOperation.currentIsLast()) {
                mCanRedo = true;
            }
            if (mCallback != null) {
                mCallback.onOperateStatusChanged();
            }
        }
    
        /**
         * 恢复
         */
        public void redo() {
            if (mStepOperation == null || !mCanRedo) {
                return;
            }
            if (!mStepOperation.currentIsLast()) {
                mCanRedo = true;
                mStepOperation.redo(mBitmap);
                hasDraw = true;
                invalidate();
                if (mStepOperation.currentIsLast()) {
                    mCanRedo = false;
                }
            } else {
                mCanRedo = false;
            }
            if (!mStepOperation.currentIsFirst()) {
                mCanUndo = true;
            }
            if (mCallback != null) {
                mCallback.onOperateStatusChanged();
            }
        }
    
        /**
         * 清除画布,记得清除点的集合
         */
        public void reset() {
            mBitmap.eraseColor(Color.TRANSPARENT);
            hasDraw = false;
            mStokeBrushPen.clear();
            if (mStepOperation != null) {
                mStepOperation.reset();
                mStepOperation.addBitmap(mBitmap);
            }
            mCanRedo = false;
            mCanUndo = false;
            if (mCallback != null) {
                mCallback.onOperateStatusChanged();
            }
            invalidate();
        }
    
    
        public void release() {
            destroyDrawingCache();
            if (mBitmap != null) {
                mBitmap.recycle();
                mBitmap = null;
            }
            if (mStepOperation != null) {
                mStepOperation.freeBitmaps();
                mStepOperation = null;
            }
        }
    
        public interface StepCallback {
            /**
             * 操作变更
             */
            void onOperateStatusChanged();
        }
    
        public void setStepCallback(StepCallback callback) {
            this.mCallback = callback;
        }
    
        /**
         * 设置画笔样式
         *
         * @param penType
         */
        public void setPenType(int penType) {
            isEraser = false;
            switch (penType) {
                case TYPE_PEN:
                    mStokeBrushPen = new SteelPen();
                    break;
                case TYPE_ERASER:
                    isEraser = true;
                    break;
            }
            //设置
            if (mStokeBrushPen.isNullPaint()) {
                mStokeBrushPen.setPaint(mPaint);
            }
            invalidate();
        }
    
        /**
         * 设置画笔大小
         *
         * @param width 大小
         */
        public void setPaintWidth(int width) {
            if (mPaint != null) {
                mPaint.setStrokeWidth(DisplayUtil.dip2px(getContext(), width));
    //            eraser.setPaintWidth(DisplayUtil.dip2px(getContext(), width));
                mStokeBrushPen.setPaint(mPaint);
                invalidate();
            }
        }
    
    
        /**
         * 设置画笔颜色
         *
         * @param color 颜色
         */
        public void setPaintColor(int color) {
            if (mPaint != null) {
                mPaint.setColor(color);
                mStokeBrushPen.setPaint(mPaint);
                invalidate();
            }
        }
    
        /**
         * 构建Bitmap
         *
         * @return 所绘制的bitmap
         */
        public Bitmap buildAreaBitmap(boolean isCrop) {
            if (!hasDraw) {
                return null;
            }
            Bitmap result;
            if (isCrop) {
                result = BitmapUtil.clearBlank(mBitmap, 50, Color.TRANSPARENT);
            } else {
                result = mBitmap;
            }
            destroyDrawingCache();
            return result;
        }
    
        public boolean isFingerEnable() {
            return isFingerEnable;
        }
    
        public void setFingerEnable(boolean fingerEnable) {
            isFingerEnable = fingerEnable;
        }
    
        public boolean isEraser() {
            return isEraser;
        }
    
        public boolean canUndo() {
            return mCanUndo;
        }
    
        public boolean canRedo() {
            return mCanRedo;
        }
    
        public Bitmap getBitmap() {
            return mBitmap;
        }
    
        /**
         * 图片大小调整适配画布宽高
         *
         * @param bitmap 源图
         * @param width  新宽度
         * @param height 新高度
         */
        public void resize(Bitmap bitmap, int width, int height) {
    
            if (mBitmap != null) {
                if (width >= this.mWidth) {
                    height = width * mBitmap.getHeight() / mBitmap.getWidth();
                }
                this.mWidth = width;
                this.mHeight = height;
    
                mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
                restoreLastBitmap(bitmap, mBitmap);
                initCanvas();
                if (mStepOperation != null) {
                    mStepOperation.addBitmap(mBitmap);
                }
                invalidate();
            }
        }
    
        /**
         * 恢复最后画的bitmap
         *
         * @param srcBitmap 最后的bitmap
         * @param newBitmap 新bitmap
         */
        private void restoreLastBitmap(Bitmap srcBitmap, Bitmap newBitmap) {
            try {
                if (srcBitmap == null || srcBitmap.isRecycled()) {
                    return;
                }
                srcBitmap = BitmapUtil.zoomImg(srcBitmap, newBitmap.getWidth());
                //缩放后如果还是超出新图宽高,继续缩放
                if (srcBitmap.getWidth() > newBitmap.getWidth() || srcBitmap.getHeight() > newBitmap.getHeight()) {
                    srcBitmap = BitmapUtil.zoomImage(srcBitmap, newBitmap.getWidth(), newBitmap.getHeight());
                }
                //保存所有的像素的数组,图片宽×高
                int[] pixels = new int[srcBitmap.getWidth() * srcBitmap.getHeight()];
                srcBitmap.getPixels(pixels, 0, srcBitmap.getWidth(), 0, 0, srcBitmap.getWidth(), srcBitmap.getHeight());
                newBitmap.setPixels(pixels, 0, srcBitmap.getWidth(), 0, 0,
                        srcBitmap.getWidth(), srcBitmap.getHeight());
            } catch (OutOfMemoryError e) {
            }
    
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int width = onMeasureR(0, widthMeasureSpec);
            int height = onMeasureR(1, heightMeasureSpec);
            setMeasuredDimension(width, height);
        }
    
        /**
         * 计算控件宽高
         */
        public int onMeasureR(int attr, int oldMeasure) {
    
            int newSize = 0;
            int mode = MeasureSpec.getMode(oldMeasure);
            int oldSize = MeasureSpec.getSize(oldMeasure);
    
            switch (mode) {
                case MeasureSpec.EXACTLY:
                    newSize = oldSize;
                    break;
                case MeasureSpec.AT_MOST:
                case MeasureSpec.UNSPECIFIED:
    
                    float value = mWidth;
    
                    if (attr == 0) {
                        if (mBitmap != null) {
                            value = mBitmap.getWidth();
                        }
                        // 控件的宽度
                        newSize = (int) (getPaddingLeft() + value + getPaddingRight());
    
                    } else if (attr == 1) {
                        value = mHeight;
                        if (mBitmap != null) {
                            value = mBitmap.getHeight();
                        }
                        // 控件的高度
                        newSize = (int) (getPaddingTop() + value + getPaddingBottom());
                    }
                    break;
                default:
                    break;
            }
            return newSize;
        }
    
        public Bitmap getLastBitmap() {
            return mBitmap;
        }
    }
    
    1. 核心代码 SteelPen.java
    /**
     * Created on 2021/12/1 11:38
     * 钢笔
     * @author Gong Youqiang
     */
    public class SteelPen extends BasePen {
        @Override
        protected void doPreDraw(Canvas canvas) {
            for (int i = 1; i < mHWPointList.size(); i++) {
                ControllerPoint point = mHWPointList.get(i);
                drawToPoint(canvas, point, mPaint);
                mCurPoint = point;
            }
        }
    
        @Override
        protected void doMove(double curDis) {
            int steps = 1 + (int) curDis / STEP_FACTOR;
            double step = 1.0 / steps;
            for (double t = 0; t < 1.0; t += step) {
                ControllerPoint point = mBezier.getPoint(t);
                mHWPointList.add(point);
            }
        }
    
        @Override
        protected void doDraw(Canvas canvas, ControllerPoint point, Paint paint) {
            drawLine(canvas, mCurPoint.x, mCurPoint.y, mCurPoint.width, point.x,
                    point.y, point.width, paint);
        }
    
        /**
         * 绘制方法,实现笔锋效果
         */
        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;
            //绘制的笔的宽度是多少,绘制多少个椭圆
            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;
    
            for (int i = 0; i < steps; i++) {
                RectF oval = new RectF();
                float top = (float) (y - w / 2.0f);
                float left = (float) (x - w / 4.0f);
                float right = (float) (x + w / 4.0f);
                float bottom = (float) (y + w / 2.0f);
                oval.set(left, top, right, bottom);
                //最基本的实现,通过点控制线,绘制椭圆
                canvas.drawOval(oval, paint);
                x += deltaX;
                y += deltaY;
                w += deltaW;
            }
        }
    }
    
    1. 使用
    public class HandWrittenBoardActivity extends BaseActivity implements View.OnClickListener, PaintView.StepCallback {
        private static final int REQUEST_EXTERNAL_STORAGE = 1;
        private ImageView mHandView; //切换 滚动/手写
        private ImageView mUndoView;
        private ImageView mRedoView;
        private ImageView mPenView;
        private ImageView mClearView;
        private CircleView mSettingView;
    
        private PaintView mPaintView;
    
        private ProgressDialog mSaveProgressDlg;
        private static final int MSG_SAVE_SUCCESS = 1;
        private static final int MSG_SAVE_FAILED = 2;
    
        private String mSavePath;
        private boolean hasSize = false;
    
        private float mWidth;
        private float mHeight;
        private float widthRate = 1.0f;
        private float heightRate = 1.0f;
        private int bgColor;
        private boolean isCrop;
        private String format;
    
        private PaintSettingWindow settingWindow;
    
        private static String[] PERMISSIONS_STORAGE = {
                "android.permission.READ_EXTERNAL_STORAGE",
                "android.permission.WRITE_EXTERNAL_STORAGE" };
    
        @Override
        protected int getLayout() {
            return R.layout.activity_hand_written_board;
        }
    
        @Override
        protected void initView() {
            verifyStoragePermissions(this);
            View mCancelView = findViewById(R.id.tv_cancel);
            View mOkView = findViewById(R.id.tv_ok);
    
            mPaintView = findViewById(R.id.paint_view);
            mHandView = findViewById(R.id.btn_hand);
            mUndoView = findViewById(R.id.btn_undo);
            mRedoView = findViewById(R.id.btn_redo);
            mPenView = findViewById(R.id.btn_pen);
            mClearView = findViewById(R.id.btn_clear);
            mSettingView = findViewById(R.id.btn_setting);
            mUndoView.setOnClickListener(this);
            mRedoView.setOnClickListener(this);
            mPenView.setOnClickListener(this);
            mClearView.setOnClickListener(this);
            mSettingView.setOnClickListener(this);
            mHandView.setOnClickListener(this);
            mCancelView.setOnClickListener(this);
            mOkView.setOnClickListener(this);
    
            mPenView.setSelected(true);
            mUndoView.setEnabled(false);
            mRedoView.setEnabled(false);
            mClearView.setEnabled(!mPaintView.isEmpty());
    
            mPaintView.setStepCallback(this);
    
            PenConfig.PAINT_SIZE_LEVEL = PenConfig.getPaintTextLevel(this);
            PenConfig.PAINT_COLOR = PenConfig.getPaintColor(this);
    
            mSettingView.setPaintColor(PenConfig.PAINT_COLOR);
            mSettingView.setRadiusLevel(PenConfig.PAINT_SIZE_LEVEL);
    
            setThemeColor(PenConfig.THEME_COLOR);
            BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, Color.WHITE);
            BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_pen, Color.WHITE);
            BitmapUtil.setImage(mRedoView, R.mipmap.sign_ic_redo, mPaintView.canRedo() ? Color.WHITE : Color.LTGRAY);
            BitmapUtil.setImage(mUndoView, R.mipmap.sign_ic_undo, mPaintView.canUndo() ? Color.WHITE : Color.LTGRAY);
            BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, !mPaintView.isEmpty() ? Color.WHITE : Color.LTGRAY);
    
        }
    
    
        /**
         * 获取画布默认宽度
         *
         * @return
         */
        private int getResizeWidth() {
            DisplayMetrics dm = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(dm);
            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && dm.widthPixels < dm.heightPixels) {
                return (int) (dm.heightPixels * widthRate);
            }
            return (int) (dm.widthPixels * widthRate);
        }
    
        /**
         * 获取画布默认高度
         *
         * @return
         */
        private int getResizeHeight() {
            int toolBarHeight = getResources().getDimensionPixelSize(R.dimen.sign_grid_toolbar_height);
            int actionbarHeight = getResources().getDimensionPixelSize(R.dimen.sign_actionbar_height);
            int statusBarHeight = StatusBarCompat.getStatusBarHeight(this);
            int otherHeight = toolBarHeight + actionbarHeight + statusBarHeight;
            DisplayMetrics dm = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(dm);
            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && dm.widthPixels < dm.heightPixels) {
                return (int) ((dm.widthPixels - otherHeight) * heightRate);
            }
            return (int) ((dm.heightPixels - otherHeight) * heightRate);
        }
    
        @Override
        protected void initData() {
            isCrop = getIntent().getBooleanExtra("crop", false);
            format = getIntent().getStringExtra("format");
            bgColor = getIntent().getIntExtra("background", Color.TRANSPARENT);
            String mInitPath = getIntent().getStringExtra("image");
            float bitmapWidth = getIntent().getFloatExtra("width", 1.0f);
            float bitmapHeight = getIntent().getFloatExtra("height", 1.0f);
    
            if (bitmapWidth > 0 && bitmapWidth <= 1.0f) {
                widthRate = bitmapWidth;
                mWidth = getResizeWidth();
            } else {
                hasSize = true;
                mWidth = bitmapWidth;
            }
            if (bitmapHeight > 0 && bitmapHeight <= 1.0f) {
                heightRate = bitmapHeight;
                mHeight = getResizeHeight();
            } else {
                hasSize = true;
                mHeight = bitmapHeight;
            }
    
            //初始画板设置
            if (!hasSize && !TextUtils.isEmpty(mInitPath)) {
                Bitmap bitmap = BitmapFactory.decodeFile(mInitPath);
                mWidth = bitmap.getWidth();
                mHeight = bitmap.getHeight();
                hasSize = true;
            }
            mPaintView.init((int) mWidth, (int) mHeight, mInitPath);
            if (bgColor != Color.TRANSPARENT) {
                mPaintView.setBackgroundColor(bgColor);
            }
        }
    
        /**
         * 横竖屏切换
         */
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            if (settingWindow != null) {
                settingWindow.dismiss();
            }
    
            int resizeWidth = getResizeWidth();
            int resizeHeight = getResizeHeight();
            if (mPaintView != null && !hasSize) {
                mPaintView.resize(mPaintView.getLastBitmap(), resizeWidth, resizeHeight);
            }
        }
    
        public static void verifyStoragePermissions(Activity activity) {
            try {
                //检测是否有写的权限
                int permission = ActivityCompat.checkSelfPermission(activity,
                        "android.permission.WRITE_EXTERNAL_STORAGE");
                if (permission != PackageManager.PERMISSION_GRANTED) {
                    // 没有写的权限,去申请写的权限,会弹出对话框
                    ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
        @Override
        public void onClick(View v) {
            int i = v.getId();
            if (i == R.id.btn_setting) {
                showPaintSettingWindow();
    
            } else if (i == R.id.btn_hand) {
                //切换是否允许写字
                mPaintView.setFingerEnable(!mPaintView.isFingerEnable());
                if (mPaintView.isFingerEnable()) {
                    BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_hand, PenConfig.THEME_COLOR);
                } else {
                    BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_drag, PenConfig.THEME_COLOR);
                }
    
            } else if (i == R.id.btn_clear) {
                mPaintView.reset();
    
            } else if (i == R.id.btn_undo) {
                mPaintView.undo();
    
            } else if (i == R.id.btn_redo) {
                mPaintView.redo();
    
            } else if (i == R.id.btn_pen) {
                if (!mPaintView.isEraser()) {
                    mPaintView.setPenType(PaintView.TYPE_ERASER);
                    BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_eraser, Color.WHITE);
                } else {
                    mPaintView.setPenType(PaintView.TYPE_PEN);
                    BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_pen, Color.WHITE);
                }
            } else if (i == R.id.tv_ok) {
                save();
    
            } else if (i == R.id.tv_cancel) {
                if (!mPaintView.isEmpty()) {
                    showQuitTip();
                } else {
                    setResult(RESULT_CANCELED);
                    finish();
                }
            }
        }
    
        @Override
        protected void onDestroy() {
            if (mPaintView != null) {
                mPaintView.release();
            }
            if (mHandler != null) {
                mHandler.removeCallbacksAndMessages(null);
            }
            super.onDestroy();
        }
    
    
        /**
         * 弹出画笔设置
         */
        private void showPaintSettingWindow() {
            settingWindow = new PaintSettingWindow(this);
            settingWindow.setSettingListener(new PaintSettingWindow.OnSettingListener() {
                @Override
                public void onColorSetting(int color) {
                    mPaintView.setPaintColor(color);
                    mSettingView.setPaintColor(color);
                }
    
                @Override
                public void onSizeSetting(int index) {
                    mSettingView.setRadiusLevel(index);
                    mPaintView.setPaintWidth(PaintSettingWindow.PEN_SIZES[index]);
                }
            });
    
            View contentView = settingWindow.getContentView();
            //需要先测量,PopupWindow还未弹出时,宽高为0
            contentView.measure(SystemUtil.makeDropDownMeasureSpec(settingWindow.getWidth()),
                    SystemUtil.makeDropDownMeasureSpec(settingWindow.getHeight()));
    
            int padding = DisplayUtil.dip2px(this, 45);
            settingWindow.popAtTopRight();
            settingWindow.showAsDropDown(mSettingView, -540, -2 * padding - settingWindow.getContentView().getMeasuredHeight());
    
        }
    
    
        private void initSaveProgressDlg() {
            mSaveProgressDlg = new ProgressDialog(this);
            mSaveProgressDlg.setMessage("正在保存,请稍候...");
            mSaveProgressDlg.setCancelable(false);
        }
    
        @SuppressLint("HandlerLeak")
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_SAVE_FAILED:
                        mSaveProgressDlg.dismiss();
                        Toast.makeText(getApplicationContext(), "保存失败", Toast.LENGTH_SHORT).show();
                        break;
                    case MSG_SAVE_SUCCESS:
                        mSaveProgressDlg.dismiss();
                        Intent intent = new Intent();
                        intent.putExtra(PenConfig.SAVE_PATH, mSavePath);
                        setResult(RESULT_OK, intent);
                        break;
                    default:
                        break;
                }
            }
        };
    
        /**
         * 保存
         */
        private void save() {
            if (mPaintView.isEmpty()) {
                Toast.makeText(getApplicationContext(), "没有写入任何文字", Toast.LENGTH_SHORT).show();
                return;
            }
            //先检查是否有存储权限
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(getApplicationContext(), "没有读写存储的权限", Toast.LENGTH_SHORT).show();
                return;
            }
            if (mSaveProgressDlg == null) {
                initSaveProgressDlg();
            }
            mSaveProgressDlg.show();
            new Thread(() -> {
                try {
                    Bitmap result = mPaintView.buildAreaBitmap(isCrop);
                    if (PenConfig.FORMAT_JPG.equals(format) && bgColor == Color.TRANSPARENT) {
                        bgColor = Color.WHITE;
                    }
                    if (bgColor != Color.TRANSPARENT) {
                        result = BitmapUtil.drawBgToBitmap(result, bgColor);
                    }
                    if (result == null) {
                        mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
                        return;
                    }
                    mSavePath = BitmapUtil.saveImage(HandWrittenBoardActivity.this, result, 100, format);
                    if (mSavePath != null) {
                        mHandler.obtainMessage(MSG_SAVE_SUCCESS).sendToTarget();
                    } else {
                        mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
                    }
                } catch (Exception e) {
    
                }
            }).start();
    
        }
    
        /**
         * 画布有操作
         */
        @Override
        public void onOperateStatusChanged() {
            mUndoView.setEnabled(mPaintView.canUndo());
            mRedoView.setEnabled(mPaintView.canRedo());
            mClearView.setEnabled(!mPaintView.isEmpty());
    
            BitmapUtil.setImage(mRedoView, R.mipmap.sign_ic_redo, mPaintView.canRedo() ? Color.WHITE : Color.LTGRAY);
            BitmapUtil.setImage(mUndoView, R.mipmap.sign_ic_undo, mPaintView.canUndo() ? Color.WHITE : Color.LTGRAY);
            BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, !mPaintView.isEmpty() ? Color.WHITE : Color.LTGRAY);
    
        }
    
        @Override
        public void onBackPressed() {
            if (!mPaintView.isEmpty()) {
                showQuitTip();
            } else {
                setResult(RESULT_CANCELED);
                finish();
            }
        }
    
        /**
         * 弹出退出提示
         */
        private void showQuitTip() {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("提示")
                    .setMessage("当前文字未保存,是否退出?")
                    .setNegativeButton("取消", null)
                    .setPositiveButton("确定", (dialog, which) -> {
                        setResult(RESULT_CANCELED);
                        finish();
                    });
            builder.show();
        }
    }
    

    Demo

    相关文章

      网友评论

        本文标题:Kevin Learn Android:Android 手签板

        本文链接:https://www.haomeiwen.com/subject/dzgyxrtx.html