Android涂鸦|建立自己的手写笔画图案

泡在网上的日子 / 文 发表于2012-11-19 02:32 次阅读 涂鸦


自SDK 1.6开始,Android手机已支持内置Gesture Builder程序,若是被Google签署(Signed)过出厂的手机应会内置此程序,如果是程序开发人员,可在SDK文件夹里的“\android\platforms\ android\samples\GestureBuilder”找到这支程序的源代码,编译后即可生成各SDK版本可使用的Gesture Builder程序(apk)自行安装使用。

Gesture Builder提供了一手写识别的功能,让用户以类似于涂鸦的方式绘制一个手写符号,使之对应一个字符串名称,然而GestureBuilder功能虽完整,但在手写字符串的创建上却有些限制,如:制式化的建立方式、无法自行配置涂鸦区、查看手写(Gesture)以ListView来呈现等,在实际开发上稍显“复杂”了些。

下面看一下配置UI界面的activity_gesture_builder_demo.xml文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     
    xmlns:tools="http://schemas.android.com/tools"
     
    android:layout_width="fill_parent"
     
    android:layout_height="fill_parent"
     
    android:background="@drawable/white" >
     
    <LinearLayout
     
        android:id="@+id/linear_top_id"
     
        android:layout_width="fill_parent"
     
        android:layout_height="wrap_content"
     
        android:layout_alignParentTop="true"
     
       android:weightSum="2"
     
        android:orientation="horizontal" />
     
       <TextView
     
            android:id="@+id/text_id"
     
            android:layout_width="wrap_content"
     
            android:layout_height="wrap_content"
     
            android:padding="@dimen/padding_medium"
     
            android:text="@string/hello_world"
     
            android:textSize="18sp"
     
            tools:context=".GestureBuilderDemo" />
     
        <EditText
     
            android:id="@+id/edit_id"
     
            android:layout_width="fill_parent"
     
            android:layout_height="wrap_content"
     
            android:inputType="text" >
     
   /LinearLayout>
     
    <android.gesture.GestureOverlayView
     
        android:id="@+id/myGestures1"
     
        android:layout_width="fill_parent"
     
        android:layout_height="300dip"
     
        android:layout_below="@+id/linear_top_id"
     
        android:layout_marginRight="30sp"
     
        android:gestureColor="#8909"
     
        android:gestureStrokeType="multiple" />
     
<!--  android:gestureStrokeType="multiple"
      表示多笔支持,single则支持单一笔画
     
-->
     
    <SlidingDrawer
     
        android:id="@+id/slidingDreaer"
     
        android:layout_width="wrap_content"
     
        android:layout_height="wrap_content"
     
        android:content="@+id/content"
     
        android:handle="@+id/handler"
     
        android:orientation="horizontal" >
     
       <ImageView
     
            android:id="@+id/handler"
     
            android:layout_width="wrap_content"
     
            android:layout_height="wrap_content"
     
            android:contentDescription="@string/app_name"
     
            android:src=http://blog.csdn.net/ta893115871/article/details/"@drawable/open" />
     
        <ListView
     
            android:id="@+id/content"
     
            android:layout_width="fill_parent"
     
            android:layout_height="wrap_content"
     
            android:choiceMode="singleChoice"
     
            android:background="@drawable/back"
     
            android:divider="@drawable/divider" >
     
       </ListView>
     
   </SlidingDrawer>
     
    <LinearLayout
     
        android:id="@+id/linear_botton_id"
     
        android:layout_width="fill_parent"
     
        android:layout_height="wrap_content"
     
        android:layout_alignParentBottom="true"
     
        android:weightSum="2"
     
        style="@android:style/ButtonBar"
        android:orientation="horizontal" >
     
       <Button
     
            android:id="@+id/button1_id"
     
            android:layout_width="wrap_content"
     
            android:layout_height="wrap_content"
     
            android:layout_weight="1.0"
     
            android:text="@string/str_button1" />;
     
        <Button
     
            android:id="@+id/button2_id"
     
            android:layout_width="wrap_content"
     
            android:layout_height="wrap_content"
     
            android:layout_weight="1.0"
     
            android:text="@string/str_button2" />
     
  </LinearLayout>
     
</RelativeLayout>

其中有一个TAG为<android.gesture. GestureOverlayView>的Widget,可称为“手写绘图区”,当中有两项较重要的属性,分别为android:layout_width设置为“fill_parent”以及android:gestureStrokeType设置为“multiple”,这表示为支持多笔画,若设置为“single”则仅支持单一笔画。

AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     
    package="com.example.gesturebuilderdemo"
     
    android:versionCode="1"
     
    android:versionName="1.0"
android:installLocation="preferExternal"
     
    >
     
    <uses-sdk
     
        android:minSdkVersion="8"
     
        android:targetSdkVersion="15" />
     
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     
    <application
     
        android:icon="@drawable/ic_launcher"
     
        android:label="@string/app_name"
     
        android:theme="@style/AppTheme" >
     
       <activity
     
            android:name=".GestureBuilderDemo"
     
            android:label="@string/title_activity_gesture_builder_demo" >
     
           <intent-filter>
     
                <action android:name="android.intent.action.MAIN" />
     
               <category android:name="android.intent.category.LAUNCHER" />
     
            </intent-filter>
     
        /activity>
     
   /application>
     
/manifest>

由于Gesture手写Libary文件"/sdcard/gestures"默认保存在SD存储卡中,所以需要写入External Storage的权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

再看java文件中看几个函数:
Gesture对象是自GestureOverlayView.getGesture() 所取得的手写对象;GestureLibraries为保存手写背后所包含的意义(String),本范例利用GestureLibraries.fromFile()方法来加载预设的Gesture文件,倘若默认手机的SD存储卡中尚未创建Gesture手写数据文件,此程序也会处理创建新文件的工作。此外,程序中举例应用了GestureLibraries.addGesture()新建手写数据、GestureLibraries.save()保存写入手写数据GestureLibraries.load()加载手写数据、GestureLibraries. removeGesture()删除手写数据等方法。

GestureBuilderDemo.java

package com.example.gesturebuilderdemo;
     
import java.io.File;
     
import java.util.ArrayList;
     
import java.util.List;
     
import java.util.Set;
     
import android.os.Bundle;
     
import android.os.Environment;
     
import android.app.Activity;
     
import android.gesture.Gesture;
     
import android.gesture.GestureLibraries;
     
import android.gesture.GestureLibrary;
     
import android.gesture.GestureOverlayView;
     
import android.gesture.GestureOverlayView.OnGestureListener;
     
import android.graphics.Bitmap;
     
import android.graphics.Color;
     
import android.util.Log;
     
import android.view.KeyEvent;
     
import android.view.Menu;
     
import android.view.MotionEvent;
     
import android.view.View;
     
import android.view.View.OnClickListener;
     
import android.view.View.OnKeyListener;
     
import android.widget.AdapterView;
     
import android.widget.AdapterView.OnItemClickListener;
     
import android.widget.Button;
     
import android.widget.EditText;
     
import android.widget.ImageView;
     
import android.widget.LinearLayout;
     
import android.widget.ListView;
     
import android.widget.SlidingDrawer;
     
import android.widget.SlidingDrawer.OnDrawerCloseListener;
     
import android.widget.SlidingDrawer.OnDrawerOpenListener;
     
import android.widget.Toast;
     
public class GestureBuilderDemo extends Activity {
     
private static final  String TAG="GestureBuilderDemo";
     
    
private Button mButton1, mButton2;
     
private GestureOverlayView mGestureOverlayView;//手写绘制区
     
private EditText mEditText;
     
private Gesture ges;
     
private GestureLibrary lib;
     
private String gesPath;
     
// ----------------------------
     
private ImageView mImageView;//拉动式抽屉
     
private SlidingDrawer mDrawer;//拉动式抽屉的手柄
     
private ListView mListView;//拉动式抽屉的内容
     
private List&lt;String&gt; gesNames = new ArrayList&lt;String&gt;();//保存手写的名称集合
     
private List&lt;Bitmap&gt; gesPics = new ArrayList&lt;Bitmap&gt;();//保存转换为手写的图片的集合
     
    
//------------------- -------
     
private LinearLayout layout_bottom;//底部的2个<A class=relatedlink  target=_blank>按钮</A><A class=relatedlink  target=_blank>布局</A>
     
private LinearLayout layout_top;//顶部的2个按钮布局
     
private MyListAdapter adapter;//适配器
     
    
@Override
     
public void onCreate(Bundle savedInstanceState) {
     
  super.onCreate(savedInstanceState);
     
  /* 查看SDCard是否存在 */
     
  setContentView(R.layout.activity_gesture_builder_demo);
     
  if (!Environment.MEDIA_MOUNTED.equals(Environment
     
    .getExternalStorageState())) {
     
   Toast.makeText(this, "SDCard不存在!", Toast.LENGTH_SHORT).show();
     
   this.finish();
     
  }
     
  /* 取得系统默认的GestureLibrary的文件路径 */
     
  gesPath = new File(Environment.getExternalStorageDirectory(),
     
    "gestures").getAbsolutePath();
     
  mButton1 = (Button) this.findViewById(R.id.button1_id);
     
  mButton2 = (Button) this.findViewById(R.id.button2_id);
     
  mGestureOverlayView = (GestureOverlayView) this
     
    .findViewById(R.id.myGestures1);
     
  mEditText = (EditText) this.findViewById(R.id.edit_id);
     
  mButton1.setEnabled(false);
     
  mImageView = (ImageView) this.findViewById(R.id.handler);
     
  mDrawer = (SlidingDrawer) this.findViewById(R.id.slidingDreaer);
     
  mListView = (ListView) this.findViewById(R.id.content);
     
  mEditText.setOnKeyListener(keyListener);
     
  mGestureOverlayView.addOnGestureListener(onGestureListener);
     
  mButton1.setOnClickListener(listener1);
     
  mButton2.setOnClickListener(listener2);
     
  layout_bottom=(LinearLayout)this.findViewById(R.id.linear_botton_id);
     
  layout_top=(LinearLayout)this.findViewById(R.id.linear_top_id);
     
      
     
  adapter=new MyListAdapter(this,gesNames,gesPics);
     
      
     
  getExitGesture();//读取SD卡中的/sdcard/gestures里建立的手写,并显示在ListView中
     
  mListView.setAdapter(adapter);
     
  mListView.setOnItemClickListener(list_listener);
     
  mDrawer.setOnDrawerOpenListener(onDrawerOpenListener_open);
     
  mDrawer.setOnDrawerCloseListener(onDrawerCloseListener_close);
     
}
     
OnItemClickListener  list_listener=new OnItemClickListener(){
     
  public void onItemClick(AdapterView&lt;?&gt; arg0, View arg1, int arg2,
     
    long arg3) {
     
   // TODO Auto-generated method stub
     
   Toast.makeText(GestureBuilderDemo.this, "GestureName:"+gesNames.get(arg2),
     
     Toast.LENGTH_SHORT).show();
     
  }
     
      
     
};
     
//抽屉打开
     
OnDrawerOpenListener  onDrawerOpenListener_open=new OnDrawerOpenListener(){
     
  public void onDrawerOpened() {
     
   // TODO Auto-generated method stub
     
   mImageView.setImageResource(R.drawable.close);
     
   layout_bottom.setVisibility(View.GONE);
     
   layout_top.setVisibility(View.GONE);
     
   mGestureOverlayView.setVisibility(View.GONE);
     
       
     
   getExitGesture();
     
  }
     
      
     
};
     
//抽屉关闭
     
OnDrawerCloseListener  onDrawerCloseListener_close=new OnDrawerCloseListener(){
     
  public void onDrawerClosed() {
     
   // TODO Auto-generated method stub
     
   mImageView.setImageResource(R.drawable.open);
     
   layout_bottom.setVisibility(View.VISIBLE);
     
   layout_top.setVisibility(View.VISIBLE);
     
   mGestureOverlayView.setVisibility(View.VISIBLE);
     
  }
     
      
     
};
     
//读取SD卡中的/sdcard/gestures里建立的手写,并显示在ListView中
     
public void getExitGesture() {
     
  Log.i(TAG, "getExitGesture()");
     
  gesNames.clear();
     
  gesPics.clear();
     
  File f = new File(gesPath);
     
  lib = GestureLibraries.fromFile(f);
     
  if (f.exists()) {
     
   if (!lib.load()) {
     
    Toast.makeText(GestureBuilderDemo.this, "加载失败!!",
     
      Toast.LENGTH_SHORT).show();
     
   } else {
     
    Object[] obj = lib.getGestureEntries().toArray();
     
    for (int i = 0; i &lt; obj.length; i++) {
     
     ArrayList&lt;Gesture&gt; al = lib.getGestures(obj[i].toString());
     
    // Log.i(TAG, "i="+i);
     
     for (int j = 0; j &lt; al.size(); j++) {
     
    //  Log.i(TAG, "j="+j);
     
    //  Log.i(TAG, "obj[i].toString()==="+obj[i].toString());
     
      // 手写名称
     
      gesNames.add(obj[i].toString());
     
      Gesture gs = (Gesture) al.get(j);
     
      //将手写转成Bitmap图片
     
      gesPics.add(gs.toBitmap(50, 50, 12, Color.MAGENTA));
     
     }
     
    }
     
   }
     
  } else {
     
   Toast.makeText(GestureBuilderDemo.this, "文件不存在!",
     
     Toast.LENGTH_SHORT).show();
     
  }
     
      
     
  adapter.notifyDataSetChanged();
     
}
     
OnGestureListener onGestureListener = new OnGestureListener() {
     
  public void onGesture(GestureOverlayView overlay, MotionEvent event) {
     
   // TODO Auto-generated method stub
     
  }
     
  public void onGestureCancelled(GestureOverlayView overlay,
     
    MotionEvent event) {
     
   // TODO Auto-generated method stub
     
  }
     
  public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
     
   // TODO Auto-generated method stub
     
   ges = overlay.getGesture();
     
   if (ges != null
     
     &amp;&amp; mEditText.getText().toString().trim().length() != 0) {
     
    mButton1.setEnabled(true);
     
   }
     
  }
     
  public void onGestureStarted(GestureOverlayView overlay,
     
    MotionEvent event) {
     
   // TODO Auto-generated method stub
     
   ges = null;
     
   mButton1.setEnabled(false);
     
  }
     
};
     
OnKeyListener keyListener = new OnKeyListener() {
     
  public boolean onKey(View arg0, int arg1, KeyEvent arg2) {
     
   // TODO Auto-generated method stub
     
   if (ges != null
     
     &amp;&amp; mEditText.getText().toString().trim().length() != 0) {
     
    mButton1.setEnabled(true);
     
   } else {
     
    mButton1.setEnabled(false);
     
   }
     
   return false;
     
  }
     
};
     
@Override
     
public boolean onCreateOptionsMenu(Menu menu) {
     
  getMenuInflater().inflate(R.menu.activity_gesture_builder_demo, menu);
     
  return true;
     
}
     
OnClickListener listener1 = new OnClickListener() {
     
  public void onClick(View arg0) {
     
   // TODO Auto-generated method stub
     
   String gestureName = mEditText.getText().toString().trim();
     
   lib = GestureLibraries.fromFile(gesPath);
     
   File f = new File(gesPath);
     
   if (!f.exists()) {
     
    /* 文件不存在就直接写入 */
     
    lib.addGesture(gestureName, ges);
     
    if (lib.save()) {
     
     mEditText.setText("");
     
     mGestureOverlayView.clear(true);
     
     mButton1.setEnabled(false);
     
     Toast.makeText(GestureBuilderDemo.this,
     
       "保存成功,路径为:" + gesPath, Toast.LENGTH_SHORT).show();
     
    } else {
     
     Toast.makeText(GestureBuilderDemo.this, "保存失败!",
     
       Toast.LENGTH_SHORT).show();
     
    }
     
   } else {
     
    // 文件存在时,先读取已经存在的Gesture
     
    if (lib.load()) {
     
     /* 如果Library中存在相同名称,则先将其移除再写入 */
     
     Set&lt;String&gt; set = lib.getGestureEntries();
     
     if (set.contains(gestureName)) {
     
      ArrayList&lt;Gesture&gt; list = lib.getGestures(gestureName);
     
      for (int i = 0; i &lt; list.size(); i++) {
     
       //删除手写数据
     
       lib.removeGesture(gestureName, list.get(i));
     
      }
     
     }
     
     //新增手写数据
     
     lib.addGesture(gestureName, ges);
     
     // 保存写入手写数据
     
     if (lib.save()) {
     
      mEditText.setText("");
     
      mGestureOverlayView.clear(true);
     
      mButton1.setEnabled(false);
     
      Toast.makeText(GestureBuilderDemo.this,
     
        "保存成功,路径为:" + gesPath, Toast.LENGTH_SHORT)
     
        .show();
     
     } else {
     
      Toast.makeText(GestureBuilderDemo.this, "保存失败!",
     
        Toast.LENGTH_SHORT).show();
     
     }
     
    } else {
     
     Toast.makeText(GestureBuilderDemo.this, "加载失败!",
     
       Toast.LENGTH_SHORT).show();
     
    }
     
   }
     
   mDrawer.toggle();
     
  }
     
};
     
OnClickListener listener2 = new OnClickListener() {
     
  public void onClick(View arg0) {
     
   // TODO Auto-generated method stub
     
   mEditText.setText("");
     
   mGestureOverlayView.clear(true);
     
   mButton1.setEnabled(false);
     
  }
     
};
     
}

ListView的适配器文件:

package com.example.gesturebuilderdemo;
     
import java.util.List;
     
import android.content.Context;
     
import android.graphics.Bitmap;
     
import android.view.LayoutInflater;
     
import android.view.View;
     
import android.view.ViewGroup;
     
import android.widget.BaseAdapter;
     
import android.widget.ImageView;
     
import android.widget.TextView;
     
public class MyListAdapter extends BaseAdapter {
     
private Context mContext;
     
private List&lt;String&gt; gesNames ;
     
private List&lt;Bitmap&gt; gesPics ;
     
    
public MyListAdapter(Context mContext,List&lt;String&gt; gesNames,List&lt;Bitmap&gt; gesPics )
     
{
     
  this.mContext=mContext;
     
  this.gesNames=gesNames;
     
  this.gesPics=gesPics;
     
}
     
public int getCount() {
     
  // TODO Auto-generated method stub
     
  return gesNames.size();
     
}
     
public Object getItem(int arg0) {
     
  // TODO Auto-generated method stub
     
  return gesNames.get(arg0);
     
}
     
public long getItemId(int position) {
     
  // TODO Auto-generated method stub
     
  return position;
     
}
     
public View getView(int position, View convertView, ViewGroup parent) {
     
  // TODO Auto-generated method stub
     
  convertView= LayoutInflater.from(mContext).inflate(R.layout.list, null);
     
  ImageView img=(ImageView)convertView.findViewById(R.id.img_id);
     
  img.setImageBitmap(gesPics.get(position));
     
      
     
  TextView text=(TextView)convertView.findViewById(R.id.text_id);
     
  text.setText(gesNames.get(position));
     
      
     
  return convertView;
     
}
     
}

ListView的布局配置文件:

<?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" >
     
   <ImageView
     
        android:id="@+id/img_id"
     
        android:layout_width="wrap_content"
     
        android:layout_height="wrap_content"
     
        android:layout_alignParentLeft="true"
     
        android:contentDescription="@string/app_name" />
     
    <TextView
     
        android:id="@+id/text_id"
     
        android:layout_width="wrap_content"
     
        android:layout_height="wrap_content"
     
        android:layout_toRightOf="@+id/img_id"
     
        android:lines="1"
     
        android:textColor="#f699"
     
        android:textSize="20sp"
     
        android:textStyle="bold" />
     
</RelativeLayout>

文件中有详细说明,不多说了。
当建立完手写和输入名称后自动把添加按钮Enable,所用的监听器为: mEditText.setOnKeyListener(keyListener);
点击添加按钮后悔自动打开抽屉获取以ListView显示在抽屉中。


界面如下所示:

EyeAndroid.com47361022350301.png

EyeAndroid.com47361022350302.png


EyeAndroid.com47361022350303.png






收藏 赞 (0) 踩 (0)
上一篇:android自定义控件实例
很多时候android常用的控件不能满足我们的需求,那么我们就需要自定义一个控件了。今天做了一个自定义控件的实例,来分享下。 首先定义一个layout实现按钮内部布局: ?xml version="1.0" encoding="utf-8"? LinearLayout xmlns:android="http://schemas.andr
下一篇:android自定义控件:坚直滚动的TextView
最近突然对原来做的一个项目有想法,当时是一个显示文本的界面会循环滚动,因为时间比较仓促,就以实现需求为目的写了一个滚动的TextView,结果还是效果挺好的。现在想把它分享给大家,这次写demo是从零开始,没在原来的项目基础上改,因为我发现原来的实现