这篇文章本来想在一个月前就发布的,最近一直忙于国家电网手持终端的应用开发,所以没抽出时间来写。周末到了,终于可以闲下来整理整理。话不多说,直奔主题。
Android 提供了很多丰富、实用而且很有特色的功能。比如,语音识别、手写签名等等。本篇就为你介绍如何在android上进行个性化的手写签名。
首先大致说说需求:这是一个追求时尚、张扬个性的时代,我们希望在签名的地方,签名的是自己手写出来的很有个性的艺术字,而非根据手势识别出来的标准字体。
设计思路如下,在画板上进行签名(其实就是绘制图片),完成后保存为图片。然后将图片按照一定的比率进行缩放并显示在指定的位置。
这里给出一个实例,实例只是一个简单的例子,如有需要可以进行必要的扩展。这里我们需要一个Listener、一个Dialog、一个Activity这个三个java类。两个layout XML文件。
Listener很简单,主要是对手写板对话框的一个监听。
public interface DialogListener { public void refreshActivity(Object object); }
接着是画板的Dialog
package cn.handwriting; import android.app.Dialog; 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.Path; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager.LayoutParams; import android.widget.Button; import android.widget.FrameLayout; public class WritePadDialog extends Dialog { Context context; LayoutParams p ; DialogListener dialogListener; public WritePadDialog(Context context,DialogListener dialogListener) { super(context); this.context = context; this.dialogListener = dialogListener; } static final int BACKGROUND_COLOR = Color.WHITE; static final int BRUSH_COLOR = Color.BLACK; PaintView mView; /** The index of the current color to use. */ int mColorIndex; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); requestWindowFeature(Window.FEATURE_PROGRESS); setContentView(R.layout.write_pad); p = getWindow().getAttributes(); //获取对话框当前的参数值 p.height = 320;//(int) (d.getHeight() * 0.4); //高度设置为屏幕的0.4 p.width = 480;//(int) (d.getWidth() * 0.6); //宽度设置为屏幕的0.6 getWindow().setAttributes(p); //设置生效 mView = new PaintView(context); FrameLayout frameLayout = (FrameLayout) findViewById(R.id.tablet_view); frameLayout.addView(mView); mView.requestFocus(); Button btnClear = (Button) findViewById(R.id.tablet_clear); btnClear.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mView.clear(); } }); Button btnOk = (Button) findViewById(R.id.tablet_ok); btnOk.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { dialogListener.refreshActivity(mView.getCachebBitmap()); WritePadDialog.this.dismiss(); } catch (Exception e) { e.printStackTrace(); } } }); Button btnCancel = (Button)findViewById(R.id.tablet_cancel); btnCancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { cancel(); } }); } /** * This view implements the drawing canvas. * * It handles all of the input events and drawing functions. */ class PaintView extends View { private Paint paint; private Canvas cacheCanvas; private Bitmap cachebBitmap; private Path path; public Bitmap getCachebBitmap() { return cachebBitmap; } public PaintView(Context context) { super(context); init(); } private void init(){ paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(3); paint.setStyle(Paint.Style.STROKE); paint.setColor(Color.BLACK); path = new Path(); cachebBitmap = Bitmap.createBitmap(p.width, (int)(p.height*0.8), Config.ARGB_8888); cacheCanvas = new Canvas(cachebBitmap); cacheCanvas.drawColor(Color.WHITE); } public void clear() { if (cacheCanvas != null) { paint.setColor(BACKGROUND_COLOR); cacheCanvas.drawPaint(paint); paint.setColor(Color.BLACK); cacheCanvas.drawColor(Color.WHITE); invalidate(); } } @Override protected void onDraw(Canvas canvas) { // canvas.drawColor(BRUSH_COLOR); canvas.drawBitmap(cachebBitmap, 0, 0, null); canvas.drawPath(path, paint); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { int curW = cachebBitmap != null ? cachebBitmap.getWidth() : 0; int curH = cachebBitmap != null ? cachebBitmap.getHeight() : 0; if (curW >= w && curH >= h) { return; } if (curW < w) curW = w; if (curH < h) curH = h; Bitmap newBitmap = Bitmap.createBitmap(curW, curH, Bitmap.Config.ARGB_8888); Canvas newCanvas = new Canvas(); newCanvas.setBitmap(newBitmap); if (cachebBitmap != null) { newCanvas.drawBitmap(cachebBitmap, 0, 0, null); } cachebBitmap = newBitmap; cacheCanvas = newCanvas; } private float cur_x, cur_y; @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { cur_x = x; cur_y = y; path.moveTo(cur_x, cur_y); break; } case MotionEvent.ACTION_MOVE: { path.quadTo(cur_x, cur_y, x, y); cur_x = x; cur_y = y; break; } case MotionEvent.ACTION_UP: { cacheCanvas.drawPath(path, paint); path.reset(); break; } } invalidate(); return true; } } }
Activity是程序的入口,这个必不可少。
package cn.handwriting; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import android.app.Activity; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.TextView; public class HandwritingActivity extends Activity { /** Called when the activity is first created. */ private Bitmap mSignBitmap; private String signPath; private ImageView ivSign; private TextView tvSign; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); setTitle("欢迎使用手写签名"); ivSign =(ImageView)findViewById(R.id.iv_sign); tvSign = (TextView)findViewById(R.id.tv_sign); ivSign.setOnClickListener(signListener); tvSign.setOnClickListener(signListener); } private OnClickListener signListener = new View.OnClickListener() { @Override public void onClick(View v) { WritePadDialog writeTabletDialog = new WritePadDialog( HandwritingActivity.this, new DialogListener() { @Override public void refreshActivity(Object object) { mSignBitmap = (Bitmap) object; signPath = createFile(); /*BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 15; options.inTempStorage = new byte[5 * 1024]; Bitmap zoombm = BitmapFactory.decodeFile(signPath, options);*/ ivSign.setImageBitmap(mSignBitmap); tvSign.setVisibility(View.GONE); } }); writeTabletDialog.show(); } }; /** * 创建手写签名文件 * * @return */ private String createFile() { ByteArrayOutputStream baos = null; String _path = null; try { String sign_dir = Environment.getExternalStorageDirectory() + File.separator; _path = sign_dir + System.currentTimeMillis() + ".jpg"; baos = new ByteArrayOutputStream(); mSignBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); byte[] photoBytes = baos.toByteArray(); if (photoBytes != null) { new FileOutputStream(new File(_path)).write(photoBytes); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (baos != null) baos.close(); } catch (IOException e) { e.printStackTrace(); } } return _path; } }
对应的两个layout文件
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <ImageView android:id="@+id/iv_sign" android:layout_marginTop="50dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <TextView android:id="@+id/tv_sign" android:layout_marginTop="50dp" android:layout_below="@id/iv_sign" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="点此签名" /> </LinearLayout>
write_pad.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:greendroid="http://schemas.android.com/apk/res/com.cyrilmottier.android.gdcatalog" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/tablet_view" android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1" android:background="@color/white"> </FrameLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@android:drawable/bottom_bar" android:paddingTop="4dp" > <Button android:id="@+id/tablet_ok" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="确定" /> <Button android:id="@+id/tablet_clear" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="清除" /> <Button android:id="@+id/tablet_cancel" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="取消" /> </LinearLayout> </LinearLayout>
这里还有个样式的设置,所以在values下添加了一个colors.xml文件。具体参见样例。