金融类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
阅读全文
0 0
- 金融类APP-手写签名ADD盖章功能实现
- android 电子签名 手写签名 功能实现
- Android自定义View实现手写签名的功能
- 金融类app被拒
- 金融类APP被拒
- flex实现手写在线签名
- flex实现手写在线签名
- 贝塞尔曲线实现手写签名
- div+css实现盖章
- 移动app与CRM签名功能的实现
- 盖章
- Winform开发中手写签名的实现
- iOS 下 实现手写签名(OC)
- canvas画布实现手写签名效果
- 手写签名
- android实现签名功能
- 通达OA 工作流电子签章 盖章Or手写
- 关于金融类APP测试的策略分析
- 去除网页广告--浏览器插件
- 这是我的第一篇博客
- 数据结构之链表
- Storm 核心概念及工作原理
- MATLAB图像处理 Object Analysis 笔记
- 金融类APP-手写签名ADD盖章功能实现
- Android 端天气预报APP的实现(七)实现城市定位功能
- 访问tomcat出现java.lang.IllegalStateException No output folder错误解决方法
- git常见命令
- Resnet学习笔记(四)--train_imagenet.py
- CTEX的一些常用技巧
- 如何用ps制作苹果
- 给定一个数判断是否为2的幂
- linux定时任务