项目中用到签名的功能。实现完记录一下。
效果图:
首先是签名的功能。原理很简单将我们手指一动的轨迹绘制出来就可以了。主要用到了canvas的绘制path的api。 canvas.drawPath(mPath,mPaint); 剩下的主要就是我们怎么确定path的轨迹。
path轨迹的确定可以参考文章Path之贝赛尔曲线和手势轨迹、水波纹效果
path的轨迹的确定无非就是 Path.moveTo(x,y)来指定path的起点,Path.lineTo(绘制直线),Path.quadTo(绘制二阶贝赛尔 曲线),Path.cubicTo() (绘制三阶贝赛尔)等。
使用lineTo()也能实现效果,但是它只是把我们手指绘制的点连接起来放大后会有马赛克的效果。所以用quadTo绘制曲线比较好。
public void quadTo(float x1, float y1, float x2, float y2)
参数中(x1,y1)是控制点坐标,(x2,y2)是终点坐标
整条线的起始点是通过Path.moveTo(x,y)来指定的,而如果我们连续调用quadTo(),前一个quadTo()的终点,就是下一个quadTo()函数的起点;如果初始没有调用Path.moveTo(x,y)来指定起始点,则默认以控件左上角(0,0)为起始
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GRAY);
canvas.drawPath(mPath,mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.moveTo(event.getX(),event.getY());
mPreX = event.getX();
mPreY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float endX = (mPreX+event.getX())/2;
float endY = (mPreY+event.getY())/2;
mPath.quadTo(mPreX,mPreY,endX,endY);
mPreX = event.getX();
mPreY =event.getY();
invalidate();
break;
}
return super.onTouchEvent(event);
}
就这几行就可完成 具体详解请看Path之贝赛尔曲线和手势轨迹、水波纹效果 写的很详细,demo也是参考的此文稍微优化了下。
然后就是把我们绘制出来的签名保存到本地。
主要就是把我们的自定义view转化成一个bitmap
public Bitmap getChartBitmap() {
// 创建一个bitmap 根据我们自定义view的大小
Bitmap returnedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
// 绑定canvas
Canvas canvas = new Canvas(returnedBitmap);
// 获取视图的背景
Drawable bgDrawable = getBackground();
if (bgDrawable != null)
// 如果有就绘制
bgDrawable.draw(canvas);
else
// 没有就绘制白色
canvas.drawColor(Color.WHITE);
// 绘制
draw(canvas);
return returnedBitmap;
}
转化完成之后就是确定一个保存路径保和我们保存的格式存到本地了
public boolean saveToGallery(String fileName, String subFolderPath, String fileDescription, Bitmap.CompressFormat
format, int quality) {
// 控制图片质量
if (quality < 0 || quality > 100)
quality = 50;
long currentTime = System.currentTimeMillis();
File extBaseDir = Environment.getExternalStorageDirectory();
File file = new File(extBaseDir.getAbsolutePath() + "/DCIM/" + subFolderPath);
if (!file.exists()) {
if (!file.mkdirs()) {
return false;
}
}
String mimeType = "";
switch (format) {
case PNG:
mimeType = "image/png";
if (!fileName.endsWith(".png"))
fileName += ".png";
break;
case WEBP:
mimeType = "image/webp";
if (!fileName.endsWith(".webp"))
fileName += ".webp";
break;
case JPEG:
default:
mimeType = "image/jpeg";
if (!(fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")))
fileName += ".jpg";
break;
}
mFilePath = file.getAbsolutePath() + "/" + fileName;
FileOutputStream out = null;
try {
out = new FileOutputStream(mFilePath);
Bitmap b = getChartBitmap();
b.compress(format, quality, out);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
long size = new File(mFilePath).length();
ContentValues values = new ContentValues(8);
// store the details
values.put(MediaStore.Images.Media.TITLE, fileName);
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.DATE_ADDED, currentTime);
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType);
values.put(MediaStore.Images.Media.DESCRIPTION, fileDescription);
values.put(MediaStore.Images.Media.ORIENTATION, 0);
values.put(MediaStore.Images.Media.DATA, mFilePath);
values.put(MediaStore.Images.Media.SIZE, size);
return getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) != null;
}
完整代码:
package com.chs.notificationtest;
import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 作者:chs on 2016/9/14 16:14
* 邮箱:657083984@qq.com
*/
public class SingerView extends View {
private Paint mPaint;
private Path mPath;
private float mPreX,mPreY;
private String mFilePath;
public SingerView(Context context) {
super(context);
init(context);
}
public SingerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SingerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(2);
mPaint.setAntiAlias(true);
mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GRAY);
canvas.drawPath(mPath,mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.moveTo(event.getX(),event.getY());
mPreX = event.getX();
mPreY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float endX = (mPreX+event.getX())/2;
float endY = (mPreY+event.getY())/2;
mPath.quadTo(mPreX,mPreY,endX,endY);
mPreX = event.getX();
mPreY =event.getY();
invalidate();
break;
}
return super.onTouchEvent(event);
}
public boolean saveToGallery(String fileName, String subFolderPath, String fileDescription, Bitmap.CompressFormat
format, int quality) {
// 控制图片质量
if (quality < 0 || quality > 100)
quality = 50;
long currentTime = System.currentTimeMillis();
File extBaseDir = Environment.getExternalStorageDirectory();
File file = new File(extBaseDir.getAbsolutePath() + "/DCIM/" + subFolderPath);
if (!file.exists()) {
if (!file.mkdirs()) {
return false;
}
}
String mimeType = "";
switch (format) {
case PNG:
mimeType = "image/png";
if (!fileName.endsWith(".png"))
fileName += ".png";
break;
case WEBP:
mimeType = "image/webp";
if (!fileName.endsWith(".webp"))
fileName += ".webp";
break;
case JPEG:
default:
mimeType = "image/jpeg";
if (!(fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")))
fileName += ".jpg";
break;
}
mFilePath = file.getAbsolutePath() + "/" + fileName;
FileOutputStream out = null;
try {
out = new FileOutputStream(mFilePath);
Bitmap b = getChartBitmap();
b.compress(format, quality, out);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
long size = new File(mFilePath).length();
ContentValues values = new ContentValues(8);
// store the details
values.put(MediaStore.Images.Media.TITLE, fileName);
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.DATE_ADDED, currentTime);
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType);
values.put(MediaStore.Images.Media.DESCRIPTION, fileDescription);
values.put(MediaStore.Images.Media.ORIENTATION, 0);
values.put(MediaStore.Images.Media.DATA, mFilePath);
values.put(MediaStore.Images.Media.SIZE, size);
return getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) != null;
}
public Bitmap getChartBitmap() {
// 创建一个bitmap 根据我们自定义view的大小
Bitmap returnedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
// 绑定canvas
Canvas canvas = new Canvas(returnedBitmap);
// 获取视图的背景
Drawable bgDrawable = getBackground();
if (bgDrawable != null)
// 如果有就绘制
bgDrawable.draw(canvas);
else
// 没有就绘制白色
canvas.drawColor(Color.WHITE);
// 绘制
draw(canvas);
return returnedBitmap;
}
public String getPath(){
return mFilePath;
}
}
保存完我们希望能像gif图中一样显示到界面上,很简单通过路径创建个bitmap 给ImageView就可以了
Bitmap bitmap = BitmapFactory.decodeFile(mSingerView.getPath());
mImageView.setImageBitmap(bitmap);