Android 手写输入的实现(保存涂鸦文字)

泡在网上的日子 / 文 发表于2012-12-27 23:02 次阅读 涂鸦


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文件。

收藏 赞 (4) 踩 (3)
上一篇:自定义 Android 消息对话框 (AlertDialog) 的样式
Android 提供了 AlertDialog类可通过其内部类 Builder轻松创建对话框窗口,但是没法对这个对话框窗口进行定制,为了修改 AlertDialog 窗口显示的外观,解决的办法就是创建一个指定的 AlertDialog 和 AlertDialog.Builder 类。 定义外观 我们希望将上面默认的
下一篇:android手写文字(涂鸦)实现
类似米聊、微信上的涂鸦和手写文字功能 实现原理是自定义View,通过手势识别获取轨迹,然后通过画笔画图 这里添加了手势记录功能,并不难理解 代码 public class TuyaView extends View {private Bitmap mBitmap;private Canvas mCanvas;private Path mPath;