【Android】手写签名(87/100)

在这里插入图片描述
自定义View:

package top.lc951.myandroid.views;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

import top.lc951.myandroid.R;

/**
 * 自定义签名View
 *
 * @author lichong
 * 2022年08月03日14:22:22
 */
public class SignatureView extends View {
    public static final String TAG = SignatureView.class.getSimpleName();

    private Paint mPathPaint = new Paint(); // 声明一个画笔对象
    private Path mPath = new Path(); // 声明一个路径对象
    private int mPathPaintColor = Color.BLACK; // 画笔颜色
    private int mStrokeWidth = 3; // 画笔线宽
    private PathPosition mPathPos = new PathPosition(); // 路径位置
    private List<PathPosition> mPathList = new ArrayList<>(); // 路径位置列表
    private PointF mLastPos; // 上次触摸点的横纵坐标

    // 定义一个路径位置实体类,包括上个落点的横纵坐标,以及下个落点的横纵坐标
    public class PathPosition {
        public PointF prePos; // 上个落点的横纵坐标
        public PointF nextPos; // 下个落点的横纵坐标
    }

    public SignatureView(Context context) {
        this(context, null);
    }

    public SignatureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (attrs != null) {
            // 根据SignatureView的属性定义,从布局文件中获取属性数组描述
            TypedArray attrArray = getContext().obtainStyledAttributes(attrs, R.styleable.SignatureView);
            // 根据属性描述定义,获取布局文件中的画笔颜色
            mPathPaintColor = attrArray.getColor(R.styleable.SignatureView_paint_color, Color.BLACK);
            // 根据属性描述定义,获取布局文件中的画笔线宽
            mStrokeWidth = attrArray.getInt(R.styleable.SignatureView_stroke_width, 3);
            attrArray.recycle(); // 回收属性数组描述
        }
        initView(); // 初始化视图
    }
    // 初始化视图
    private void initView() {
        mPathPaint.setStrokeWidth(mStrokeWidth); // 设置画笔的线宽
        mPathPaint.setStyle(Paint.Style.STROKE); // 设置画笔的类型。STROK表示空心,FILL表示实心
        mPathPaint.setColor(mPathPaintColor); // 设置画笔的颜色
        setDrawingCacheEnabled(true); // 开启当前视图的绘图缓存
    }

    // 清空画布
    public void clear() {
        mPath.reset(); // 重置路径对象
        mPathList.clear(); // 清空路径列表
        postInvalidate(); // 立即刷新视图(线程安全方式)
    }
    // 撤销上一次绘制
    public void revoke() {
        if (mPathList.size() > 0) {
            // 移除路径位置列表中的最后一个路径
            mPathList.remove(mPathList.size() - 1);
            mPath.reset(); // 重置路径对象
            for (int i = 0; i < mPathList.size(); i++) {
                PathPosition pp = mPathList.get(i);
                // 移动到上一个坐标点
                mPath.moveTo(pp.prePos.x, pp.prePos.y);
                // 连接上一个坐标点和下一个坐标点
                mPath.quadTo(pp.prePos.x, pp.prePos.y, pp.nextPos.x, pp.nextPos.y);
            }
            postInvalidate(); // 立即刷新视图(线程安全方式)
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(mPath, mPathPaint); // 在画布上绘制指定路径线条
    }

    // 在发生触摸事件时触发
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: // 按下手指
                mPath.moveTo(event.getX(), event.getY()); // 移动到指定坐标点
                mPathPos.prePos = new PointF(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_MOVE: // 移动手指
                // 连接上一个坐标点和当前坐标点
                mPath.quadTo(mLastPos.x, mLastPos.y, event.getX(), event.getY());
                mPathPos.nextPos = new PointF(event.getX(), event.getY());
                mPathList.add(mPathPos); // 往路径位置列表添加路径位置
                mPathPos = new PathPosition(); // 创建新的路径位置
                mPathPos.prePos = new PointF(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_UP: // 松开手指
                // 连接上一个坐标点和当前坐标点
                mPath.quadTo(mLastPos.x, mLastPos.y, event.getX(), event.getY());
                break;
        }
        mLastPos = new PointF(event.getX(), event.getY());
        postInvalidate(); // 立即刷新视图(线程安全方式)
        return true;
    }
}

自定义属性:

<declare-styleable name="SignatureView">
        <attr name="paint_color" format="color" />
        <attr name="stroke_width" format="integer" />
    </declare-styleable>

使用:


            if (v.getId() == R.id.btn_save_signature) { // 点击了保存签名按钮
                if (TextUtils.isEmpty(mImagePath)) {
                    Toast.makeText(SignatureActivity.this, "请先开始然后结束签名", Toast.LENGTH_LONG).show();
                    return;
                }
                BitmapUtil.notifyPhotoAlbum(SignatureActivity.this, mImagePath); // 通知相册来了张新图片
                Toast.makeText(SignatureActivity.this, "已保存签名图片,请到系统相册查看", Toast.LENGTH_LONG).show();
            } else if (v.getId() == R.id.btn_begin_signature) { // 点击了开始签名按钮
                // 开启签名视图的绘图缓存
                signatureView.setDrawingCacheEnabled(true);
            } else if (v.getId() == R.id.btn_reset_signature) { // 点击了重置按钮
                signatureView.clear(); // 清空签名视图
            } else if (v.getId() == R.id.btn_revoke_signature) { // 点击了回退按钮
                signatureView.revoke(); // 回退签名视图的最近一笔绘画
            } else if (v.getId() == R.id.btn_end_signature) { // 点击了结束签名按钮
                if (!signatureView.isDrawingCacheEnabled()) { // 签名视图的绘图缓存不可用
                    Toast.makeText(SignatureActivity.this, "请先开始签名", Toast.LENGTH_LONG).show();
                } else { // 签名视图的绘图缓存当前可用
                    Bitmap bitmap = signatureView.getDrawingCache(); // 从绘图缓存获取位图对象
                    // 生成图片文件的保存路径
//                    mImagePath = String.format("%s/%s.jpg",
//                            getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(),
//                            DateUtil.getNowDateTime());
//                    BitmapUtil.saveImage(mImagePath, bitmap); // 把位图保存为图片文件
//                    signatureIvNew.setImageURI(Uri.parse(mImagePath)); // 设置图像视图的路径对象
                    // 延迟100毫秒后启动绘图缓存的重置任务
                    new Handler(Looper.myLooper()).postDelayed(() -> {
                        // 关闭签名视图的绘图缓存
                        signatureView.setDrawingCacheEnabled(false);
                        // 开启签名视图的绘图缓存
                        signatureView.setDrawingCacheEnabled(true);
                    }, 100);
                }
            }
        }

产品推荐

推荐理由

postman在国内使用已经越来越困难:

  1. 当电脑打开软件多一点点使用postman就会电脑卡的让人冒烟
  2. 登录问题严重
  3. Mock功能服务基本没法使用
  4. 版本更新功能已很匮乏
  5. 某些外力因素导致postman以后能否使用风险较大

出于以上考虑因此笔者自己开发了一款api调试开发工具SmartApi,满足基本日常开发调试api需求

官网地址SmartApi

http://www.smartapi.site/

是的,兄弟们,我还是建立了自己的官网!需要下载的大佬直接去官网下载就可以了蛤,顺便看看官网介绍
看下面的简单的
在这里插入图片描述


旧版本已停止维护

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lichong951

你的鼓励决定更新的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值