在Android中,我们常用的控件,例如按钮(Button)、文本框(TextView),可编辑文本框(EditText),列表框(ListView),复选框(CheckBox),单选框(RadioButton),滚动条(Gallery),微调器(Spinner), 等等,还有一些比较先进的有着特殊用途的View组件,例如AutoCompleteTextView, ImageSwitcher和 TextSwitcher。除此之外,种类繁多的像 线性布局(LinearLayout), 框架布局(FrameLayout), 这样的布局组件(Layout)也被认为是View组件,他们是从View类派生过来的。本文呢,我们就先创建一个网络签名的控件SignatureView,具体的理论,本文就先不介绍这么多了,有点饿了,赶紧写完去吃饭,下次再详细研究下里面的理论知识。好,开始我们的实验。
第一步:创建自定义控件SignatureView.java
package com.figo.helloworld;
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.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 自定义签名控件
*
* @author zhuzhifei
* 版权所有 20120912
*/
public class SignatureView extends View {
private Bitmap mBitmap;// 绘成的图片
private Canvas mCanvas;// 画布
private Path mPath;// 绘图路径
private Paint mBitmapPaint;// 绘制图片的画笔
private Paint mPathPaint;// 绘制路径的画笔
private float mX, mY;// 坐标
private static final float TOUCH_TOLERANCE = 4;// 公差4dp
// 构造函数
public SignatureView(Context context) {
super(context);
init();
}
// 构造函数
public SignatureView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
// 构造函数
public SignatureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
// 新建绘图路径,画笔,和画布
private void init() {
mPath = new Path();// 绘图路径
mBitmapPaint = new Paint(Paint.DITHER_FLAG);// 绘制图片的画笔
mPathPaint = new Paint();// 绘制路径的画笔
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true);
mPathPaint.setColor(0xFF000000);// 颜色
mPathPaint.setStyle(Paint.Style.STROKE);// 样式 线条
mPathPaint.setStrokeJoin(Paint.Join.ROUND);
mPathPaint.setStrokeCap(Paint.Cap.ROUND);
mPathPaint.setStrokeWidth(7);// 笔画宽度
}
// 测量手机屏幕宽度和高度
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
this.createBitmap();
}
// 生成签名图片
protected void createBitmap() {
if (mBitmap != null) {
return;
}
mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
mBitmap.eraseColor(Color.TRANSPARENT);
mCanvas = new Canvas(mBitmap);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
// 绘制视图
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPathPaint);
}
// 开始绘制
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
// 绘制过程
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);// 横坐标移动量
float dy = Math.abs(y - mY);// 纵坐标移动量
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);// 移动画笔
mX = x;
mY = y;
}
}
// 停止绘制
private void touch_up() {
mPath.lineTo(mX, mY);// 路径到达终点坐标
mCanvas.drawPath(mPath, mPathPaint);// 通过路径,在画布上画图
mPath.reset();// 重新设置绘图路径到原始状态
}
// 触屏事件
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();// 横坐标
float y = event.getY();// 纵坐标
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:// 手指接触到屏幕
touch_start(x, y);
invalidate();// 绘制结束更新视图,触发ondraw事件
break;
case MotionEvent.ACTION_MOVE:// 手指移动
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:// 手指释放
touch_up();
invalidate();
break;
}
return true;
}
// 清除之前画的图像
public void clearSignature() {
mBitmap.eraseColor(Color.TRANSPARENT);
invalidate();
}
// 获取画好的图像
public Bitmap getSignatureBitmap() {
return mBitmap;
}
}
第二步:设计Activity对应的布局页面signature.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.figo.helloworld.SignatureView
android:id="@+id/signatureView1"
android:layout_width="fill_parent"
android:layout_height="180dp"
android:layout_alignParentLeft="true"
android:background="@drawable/sign_tip" />
<Button
android:id="@+id/btnSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/signatureView1"
android:layout_marginRight="104dp"
android:layout_marginTop="55dp"
android:onClick="onSaveClick"
android:text="保 存" />
<Button
android:id="@+id/btnReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/btnSave"
android:layout_alignBottom="@+id/btnSave"
android:layout_marginRight="34dp"
android:layout_toLeftOf="@+id/btnSave"
android:onClick="onResetClick"
android:text="重 签" />
<ImageView
android:id="@+id/imgShow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/btnReset"
android:layout_marginTop="18dp"
android:background="@drawable/sign_tip"/>
</RelativeLayout>
第三步:创建Acitivity SignatureActivity.java 这个activity里面我们将引用上面创建的控件
package com.figo.helloworld;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.ImageView;
/**
* 使用自定义绘图控件 签名、保存签名图片、显示签名
*
* @author zhuzhifei 版权所有 20120912
*/
public class SignatureActivity extends Activity {
SignatureView sigView = null;
private Bitmap newSigBmp = null;
private ImageView image;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.signature);
initView();
}
private void initView() {
sigView = (SignatureView) findViewById(R.id.signatureView1);
image = (ImageView) findViewById(R.id.imgShow);
}
public void onResetClick(View view) {
sigView.clearSignature();
}
public void onSaveClick(View view) throws IOException {
// 判断是否有签名后再操作...
Bitmap bitMap = sigView.getSignatureBitmap();
// 方法一:直接显示
// image.setImageBitmap(bitMap);
// 方法二:保存后再显示
newSigBmp = scaleToNewBitmap(bitMap, 0.3f);
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String fn = df.format(new Date());
String path = saveBitmapToPngFile(newSigBmp, fn);
image.setImageURI(Uri.parse(path));
}
// 更换图片尺寸
private Bitmap scaleToNewBitmap(Bitmap origin, float scaleRate) {
Matrix matrix = new Matrix();
matrix.postScale(scaleRate, scaleRate);
Bitmap newBitmap = Bitmap.createBitmap(origin, 0, 0, origin.getWidth(),
origin.getHeight(), matrix, true);
return newBitmap;
}
// 将bmp格式图片转换成png格式,同时保存到sdcard/Sign这个文件夹里面
private String saveBitmapToPngFile(Bitmap b, String name) {
String currentPath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/Sign";
FileOutputStream fos = null;
try {
File sddir = new File(currentPath);
if (!sddir.exists()) {
sddir.mkdirs();
}
File file = new File(currentPath + "/" + name + ".png");
if (file.exists()) {
file.delete();
}
file.createNewFile();
fos = new FileOutputStream(file);
if (fos != null) {
b.compress(Bitmap.CompressFormat.PNG, 50, fos);
fos.close();
}
return currentPath + "/" + name + ".png";
} catch (Exception e) {
e.printStackTrace();
}
return currentPath;
}
}
第四步:AndroidManifest.xml 注册Acitivity和设置写sdcard的权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".SignatureActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
第五步:运行效果
点击保存后,签名图片写入sdcard,同时将图片显示在下面的一个imageView里面