金融类APP-手写签名ADD盖章功能实现

来源:互联网 发布:淘宝联盟优惠券 编辑:程序博客网 时间:2024/04/29 10:31

前言:

之前换工作的时候,发现从事金融行业APP开发比较赚钱,为了钱,最近一直在学习,换工作之前在银行工作,虽然也算是金融行业但是却没有证券行业等的收益高,但在银行业务开发中金融行业也能够使用的其中之一就是,手写签名+公章功能的实现。

原理:

从业务逻辑上来说,就是我们提供一个可绘制的面板,让用户可以绘制,绘制完成后,进行写入文件保存或者加盖公章合成图片,然后上传后台,这里图片压缩处理逻辑省略。

绘制面板原理:

绘制面板自定义view的原理:在view中用监听OnTouch()事件,这里我把手当做鼠标来看,我们需要判断鼠标的抬起移动按下操作

1)没有绘制:没有进入OnTouch()监听,也就是鼠标[手]没有接触自定义view

2)什么时候绘制:

【1】手指点下屏幕时调用,使用画笔绘制起点

【2】手指在屏幕上滑动时调用,会记录之前和现在手势的位置,进行绘制,如果,之前位置和手势的位置相差大于3的时候,生成贝塞尔绘制曲线,为了看起来线条流畅,一般,贝塞尔曲线的操作点为起点和终点的一半,二次贝塞尔,实现平滑曲线;手势x, 手势Y为操作点,之前,贝塞尔曲线的操作点为起点和终点的一半xy为终点,我相信很多亲看到的算法都和小编说的一样哈。

【3】手指在屏幕上抬起的时候,绘制【2】操作得到的画笔中的内容

3)至于图片保存,就是比较常见的也是比较基础的bitmap or other object 写入File,可选择自己擅长的样式

绘制面板自定义view代码:

write_panel_layout.xml

代码如下:

package com.electronic.signature.view;import android.annotation.SuppressLint;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.Path;import android.graphics.PorterDuff;import android.support.annotation.ColorInt;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.util.Log;import android.view.MotionEvent;import android.view.View;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;/** * 绘制Path的View 用于签名 * * @author fuqinming */@SuppressLint("ClickableViewAccessibility")public class LinePathView extends View {    //标签    private static final String TAG = LinePathView.class.getSimpleName();    //activity对象    private Context mContext;    //笔画X坐标起点    private float mX;    //笔画Y坐标起点    private float mY;    //绘制画笔    private final Paint mGesturePaint = new Paint();    //保存路径    private final Path mPath = new Path();    //背景画布    private Canvas cacheCanvas;    //背景Bitmap缓存    private Bitmap cachebBitmap;    //是否已经手绘签名    private boolean isTouched = false;    //画笔粗细[画笔宽度]    private int mPaintWidth = 10;    //画笔颜色[前景色]    private int mPenColor = Color.BLACK;    //背景色[背景色]    private int mBackColor = Color.TRANSPARENT;    public LinePathView(Context context) {        super(context);        init(context);    }    public LinePathView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    public LinePathView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context);    }    public void init(Context context) {        this.mContext = context;        mGesturePaint.setAntiAlias(true);        mGesturePaint.setStyle(Style.STROKE);        mGesturePaint.setStrokeWidth(mPaintWidth);        mGesturePaint.setColor(mPenColor);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        cachebBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);        cacheCanvas = new Canvas(cachebBitmap);        cacheCanvas.drawColor(mBackColor);        isTouched = false;    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                touchDown(event);                break;            case MotionEvent.ACTION_MOVE:                isTouched = true;                touchMove(event);                break;            case MotionEvent.ACTION_UP:                cacheCanvas.drawPath(mPath, mGesturePaint);                mPath.reset();                break;        }        // 更新绘制        invalidate();        return true;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawBitmap(cachebBitmap, 0, 0, mGesturePaint);        // 通过画布绘制多点形成的图形        canvas.drawPath(mPath, mGesturePaint);    }    // 手指点下屏幕时调用    private void touchDown(MotionEvent event) {        // mPath.rewind();        // 重置绘制路线,即隐藏之前绘制的轨迹        mPath.reset();        float x = event.getX();        float y = event.getY();        mX = x;        mY = y;        // mPath绘制的绘制起点        mPath.moveTo(x, y);    }    // 手指在屏幕上滑动时调用    private void touchMove(MotionEvent event) {        final float x = event.getX();        final float y = event.getY();        final float previousX = mX;        final float previousY = mY;        final float dx = Math.abs(x - previousX);        final float dy = Math.abs(y - previousY);        // 两点之间的距离大于等于3时,生成贝塞尔绘制曲线        if (dx >= 3 || dy >= 3) {            // 设置贝塞尔曲线的操作点为起点和终点的一半            float cX = (x + previousX) / 2;            float cY = (y + previousY) / 2;            // 二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点            mPath.quadTo(previousX, previousY, cX, cY);            // 第二次执行时,第一次结束调用的坐标值将作为第二次调用的初始坐标值            mX = x;            mY = y;        }    }    /**     * 清除画板     */    public void clear() {        if (cacheCanvas != null) {            isTouched = false;            mGesturePaint.setColor(mPenColor);            cacheCanvas.drawColor(mBackColor, PorterDuff.Mode.CLEAR);            mGesturePaint.setColor(mPenColor);            invalidate();        }    }    /**     * 保存画板     *     * @param path 保存到路劲     */    public void save(String path) throws IOException {        save(path, false, 0);    }    /**     * 保存画板     *     * @param path       保存到路劲     * @param clearBlank 是否清楚空白区域     * @param blank      边缘空白区域     */    public void save(String path, boolean clearBlank, int blank) throws IOException {        Bitmap bitmap = cachebBitmap;        //BitmapUtil.createScaledBitmapByHeight(srcBitmap, 300);//  压缩图片        if (clearBlank) {            bitmap = clearBlank(bitmap, blank);        }        ByteArrayOutputStream bos = new ByteArrayOutputStream();        bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);        byte[] buffer = bos.toByteArray();        if (buffer != null) {            File file = new File(path);            if (file.exists()) {                file.delete();            }            OutputStream outputStream = new FileOutputStream(file);            outputStream.write(buffer);            outputStream.close();        }    }    /**     * 获取画板的bitmap     *     * @return     */    public Bitmap getBitMap() {        setDrawingCacheEnabled(true);        buildDrawingCache();        Bitmap bitmap = getDrawingCache();        setDrawingCacheEnabled(false);        return bitmap;    }    /**     * 逐行扫描 清楚边界空白。     *     * @param bp     * @param blank 边距留多少个像素     * @return     */    private Bitmap clearBlank(Bitmap bp, int blank) {        int HEIGHT = bp.getHeight();        int WIDTH = bp.getWidth();        int top = 0, left = 0, right = 0, bottom = 0;        int[] pixs = new int[WIDTH];        boolean isStop;        for (int y = 0; y < HEIGHT; y++) {            bp.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--) {            bp.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++) {            bp.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--) {            bp.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(bp, left, top, right - left, bottom - top);    }    /**     * 设置画笔宽度 默认宽度为10px     *     * @param mPaintWidth     */    public void setPaintWidth(int mPaintWidth) {        mPaintWidth = mPaintWidth > 0 ? mPaintWidth : 10;        this.mPaintWidth = mPaintWidth;        mGesturePaint.setStrokeWidth(mPaintWidth);    }    public void setBackColor(@ColorInt int backColor) {        mBackColor = backColor;    }    /**     * 设置画笔颜色     *     * @param mPenColor     */    public void setPenColor(int mPenColor) {        this.mPenColor = mPenColor;        mGesturePaint.setColor(mPenColor);    }    /**     * 是否有签名     *     * @return     */    public boolean getTouched() {        return isTouched;    }}

下面是布局中调用自定义view

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <Button            android:id="@+id/clear_btn"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="清除" />        <Button            android:id="@+id/save_btn"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="保存并返回" />        <Button            android:id="@+id/change_color_btn"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="改变颜色" />        <Button            android:id="@+id/change_width_btn"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="改变宽度" />    </LinearLayout>    <com.electronic.signature.view.LinePathView        android:id="@+id/mPathView"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_gravity="top"        android:layout_margin="15dp"        android:background="#FFFFFF" /></LinearLayout>

效果如下:

这里写图片描述

其中的蓝色框区域就是自定义手写绘制面板区域

Activity类

package com.electronic.signature;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;import com.electronic.signature.view.LinePathView;import java.io.IOException;public class LandscapeActivity extends Activity implements View.OnClickListener {    //绘制面板    private LinePathView pathView;    //清空    private Button land_clear;    //保存    private Button land_save;    private void initView() {        pathView = (LinePathView) findViewById(R.id.mPathView);        land_clear = (Button) findViewById(R.id.clear_btn);        land_save = (Button) findViewById(R.id.save_btn);    }    /**     * 数据装载     */    private void initData() {    }    /**     * 点击事件     */    private void initAction() {        land_save.setOnClickListener(this);        land_clear.setOnClickListener(this);    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.write_panel_layout);        initView();        initData();        initAction();    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.save_btn:                if (pathView.getTouched()) {                    try {                        pathView.save("/sdcard/land_qm.png", true, 10);                        setResult(101);                        finish();                    } catch (IOException e) {                        //e.printStackTrace();                        Log.e("fuqinming","呵呵报错了博阿措信息为:"+e.getMessage());                        Toast.makeText(LandscapeActivity.this, "保存文件失败,请检查权限~", Toast.LENGTH_SHORT).show();                    }                } else {                    Toast.makeText(LandscapeActivity.this, "您没有签名~", Toast.LENGTH_SHORT).show();                }                break;            case R.id.clear_btn:                pathView.clear();                break;            default:                break;        }    }}

我这里处理,采用当我绘制完成之后,返回前面一个页面进行显示并加盖公章

回调页面的处理

package com.electronic.signature;import android.app.Activity;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.os.Bundle;import android.os.Environment;import android.support.annotation.BinderThread;import android.support.v7.app.AppCompatActivity;import android.text.Html;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;import java.io.File;/** * @author fuqinming *         <p> *         电子签名学习/手写签名 */public class MainActivity extends Activity implements View.OnClickListener {    //区域剪裁    private Button region_tailoring_btn;    //横屏旋转    private Button screen_change_btn;    //区域剪裁画板    private ImageView write_panel_img;    //横屏签名画板    private ImageView write_panel_img2;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        region_tailoring_btn.setOnClickListener(this);        screen_change_btn.setOnClickListener(this);    }    private void initView() {        region_tailoring_btn = (Button) findViewById(R.id.region_tailoring_btn);        screen_change_btn = (Button) findViewById(R.id.screen_change_btn);        write_panel_img = (ImageView) findViewById(R.id.write_panel_img);        write_panel_img2 = (ImageView) findViewById(R.id.write_panel_img2);    }    @Override    public void onClick(View view) {        switch (view.getId()) {            //区域剪裁            case R.id.region_tailoring_btn:                // TODO Auto-generated method stub                startActivityForResult(new Intent(this, HandWriteActivity.class), 1);                break;            //横屏签名            case R.id.screen_change_btn:                startActivityForResult(new Intent(MainActivity.this, LandscapeActivity.class), 1);                break;            default:                break;        }    }//    public static String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "qm.png";//    public static String path1 = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "ls.png";    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        if (resultCode == 100) {            BitmapFactory.Options options = new BitmapFactory.Options();            options.inSampleSize = 2;            Bitmap bm = BitmapFactory.decodeFile("/sdcard/qm.png", options);            bm = addGongZhang(bm);            write_panel_img.setImageBitmap(bm);        } else if (resultCode == 101) {            BitmapFactory.Options options = new BitmapFactory.Options();            options.inSampleSize = 2;            Bitmap bm = BitmapFactory.decodeFile("/sdcard/land_qm.png", options);            bm = addGongZhang(bm);            write_panel_img2.setImageBitmap(bm);        }    }    private Bitmap addGongZhang(Bitmap bm) {        Bitmap bmp = BitmapFactory.decodeResource(this.getResources(), R.mipmap.test_gz_img);        bmp = resizeImage(bmp, 380, 380);        //Bitmap newb = Bitmap.createBitmap( 300, 300, Bitmap.Config.ARGB_8888 );        return combineBitmap(bm, bmp);    }    //使用Bitmap加Matrix来缩放    public static Bitmap resizeImage(Bitmap bitmap, int w, int h) {        Bitmap BitmapOrg = bitmap;        int width = BitmapOrg.getWidth();        int height = BitmapOrg.getHeight();        int newWidth = w;        int newHeight = h;        float scaleWidth = ((float) newWidth) / width;        float scaleHeight = ((float) newHeight) / height;        Matrix matrix = new Matrix();        matrix.postScale(scaleWidth, scaleHeight);        // if you want to rotate the Bitmap        // matrix.postRotate(45);        Bitmap resizedBitmap = Bitmap.createBitmap(BitmapOrg, 0, 0, width,                height, matrix, true);        return resizedBitmap;        //return new BitmapDrawable(resizedBitmap);    }    /**     * 合并两张bitmap为一张     *     * @param background     * @param foreground     * @return Bitmap     */    public static Bitmap combineBitmap(Bitmap background, Bitmap foreground) {        if (background == null) {            return null;        }        int bgWidth = background.getWidth();        int bgHeight = background.getHeight();        int fgWidth = foreground.getWidth();        int fgHeight = foreground.getHeight();        if (bgWidth < fgWidth) {            bgWidth = fgWidth;        }        if (bgHeight < fgHeight) {            bgHeight = fgHeight;        }        Bitmap newmap = Bitmap.createBitmap(bgWidth, bgHeight, Bitmap.Config.ARGB_8888);        Canvas canvas = new Canvas(newmap);        canvas.drawBitmap(background, 0, 0, null);        canvas.drawBitmap(foreground, 0, 0, null);        canvas.save(Canvas.ALL_SAVE_FLAG);        canvas.restore();        return newmap;    }}

这样就完成了,因为本人手机>=6.0版本,固本人添加了权限判断,也算是比较简单粗暴的方式哈。

运行效果

绘制前:

这里写图片描述

绘制….

这里写图片描述

绘制后:

这里写图片描述

希望本篇文章对你和对未来的自己有用,项目学习代码已经上传.

资源地址:

http://download.csdn.net/download/onlymetagain/10112750

原创粉丝点击