Gesture Builder:自定义手势文件

自 SDK 1.6 开始,Android手机已支持内置 Gesture Builder 程序,若是被Google签署(Signed)过出厂的手机应会内置此程序。

Gesture Builder 提供了手写识别的功能,让用户以类似于涂鸦的方式绘制一个手写符号,对应一个字符串名称,存储在 /mnt/sdcard/gestures 文件中。

为了练手,编写了Gesture Builder的应用程序,运行效果如下:

Gesture Builder:自定义手势文件_第1张图片

Gesture Builder:自定义手势文件_第2张图片

界面布局如下:

gesture_builder.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:id="@+id/gesture_builder_layout_top">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="18sp"
            android:textColor="@android:color/white"
            android:text="名称:"/>

        <EditText
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:inputType="text"
            android:background="@android:color/white"
            android:id="@+id/gesture_builder_txt_name"/>

        </LinearLayout>

    <android.gesture.GestureOverlayView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gestureStrokeType="multiple"
        android:gestureColor="#FFFF00"
        android:layout_below="@+id/gesture_builder_layout_top"
        android:layout_above="@+id/gesture_builder_layout_bottom"
        android:layout_marginRight="30dp"
        android:id="@+id/gesture_builder_gesture_overlay_view" >

         </android.gesture.GestureOverlayView>

    <SlidingDrawer
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:content="@+id/gesture_builder_content"
        android:handle="@+id/gesture_builder_handler"
        android:id="@+id/gesture_builder_sliding_drawer">

        <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="SlidingDrawer"
        android:src="@drawable/thumb_select"
        android:id="@+id/gesture_builder_handler"/>

        <ListView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:choiceMode="singleChoice"
            android:background="@drawable/listview_bg"
            android:divider="@drawable/listview_divider_style"
            android:dividerHeight="2dp"
            android:id="@+id/gesture_builder_content"></ListView>
        </SlidingDrawer>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:weightSum="2"
        android:layout_alignParentBottom="true"
        style="@android:style/ButtonBar"
        android:orientation="horizontal"
        android:id="@+id/gesture_builder_layout_bottom">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Done"
            android:id="@+id/gesture_builder_btn_done"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Discard"
            android:id="@+id/gesture_builder_btn_discard"/>

        </LinearLayout>

</RelativeLayout>

gesture_builder_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:id="@+id/gesture_builder_list_image"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:lines="1"
        android:textColor="#f69900"
        android:textSize="18sp"
        android:textStyle="bold"
        android:id="@+id/gesture_builder_list_text"/>

</LinearLayout>

样式风格如下:

listview_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <gradient
        android:startColor="#1C86EE"
        android:centerColor="#FFFFFF"
        android:endColor="#1C86EE"
        android:centerX="0.1"
        android:angle="135" />

</shape>

listview_divider_style.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <gradient
        android:startColor="#eeee00"
        android:centerColor="#ff4500"
        android:endColor="#eeee00"/>
</shape>

适配器如下:

GestureBuilderAdapter.java

package com.xx.xx;

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;

import java.util.List;

public class GestureBuilderAdapter extends BaseAdapter {

    private Context context;
    private List<String> names;
    private List<Bitmap> bitmaps;

    public GestureBuilderAdapter(Context context, List<String> gestureNames, List<Bitmap> gestureBitmaps){
        this.context = context;
        this.names = gestureNames;
        this.bitmaps = gestureBitmaps;
    }

    @Override
    public int getCount() {
        return this.names.size();
    }

    @Override
    public Object getItem(int position) {
        return names.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        convertView = LayoutInflater.from(context).inflate(R.layout.gesture_builder_list_item, null);

        ImageView imageView = (ImageView)convertView.findViewById(R.id.gesture_builder_list_image);
        imageView.setImageBitmap(bitmaps.get(position));

        TextView textView = (TextView)convertView.findViewById(R.id.gesture_builder_list_text);
        textView.setText(names.get(position));

        return convertView;
    }
}

Activity如下:

GestureBuilderActivity.java

package com.xx.xx;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SlidingDrawer;
import android.widget.Toast;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class GestureBuilderActivity extends Activity {

    private LinearLayout top;
    private EditText txtName;
    private LinearLayout bottom;
    private Button btnDone;
    private GestureOverlayView gestureOverlayView;
    private SlidingDrawer slidingDrawer;
    private ListView listView;
    private ImageView imageView;

 /**
     * 当前手势
     */
    private Gesture gesture;
    /**
     * 手势文件的存储路径
     */
    private String gesPath;
    /**
     * 手势名称列表
     */
    private List<String> gestureNames = new ArrayList<>();
    /**
     * 手势图片列表
     */
    private List<Bitmap> gestureBitmaps = new ArrayList<>();
    /**
     * 适配器
     */
    private GestureBuilderAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.gesture_builder);

        /**
         * 检测sdcard是否存在
         */
        if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
            Toast.makeText(this, "sdcard不存在!", Toast.LENGTH_SHORT).show();
            finish();
        }

        /**
         * 手势文件默认存储在“/mnt/sdcard/gestures”文件中
         */
        gesPath = new File(Environment.getExternalStorageDirectory(), "gestures").getAbsolutePath();

        /**
         * ListView适配器
         */
        adapter = new GestureBuilderAdapter(this, gestureNames, gestureBitmaps);

        top = (LinearLayout)findViewById(R.id.gesture_builder_layout_top);
        txtName = (EditText)findViewById(R.id.gesture_builder_txt_name);
        gestureOverlayView = (GestureOverlayView)findViewById(R.id.gesture_builder_gesture_overlay_view);
        bottom = (LinearLayout)findViewById(R.id.gesture_builder_layout_bottom);
        btnDone = (Button)findViewById(R.id.gesture_builder_btn_done);
        Button btnDiscard = (Button)findViewById(R.id.gesture_builder_btn_discard);

        slidingDrawer = (SlidingDrawer)findViewById(R.id.gesture_builder_sliding_drawer);
        imageView = (ImageView)findViewById(R.id.gesture_builder_handler);
        listView = (ListView)findViewById(R.id.gesture_builder_content);

        /**
         * Done按钮默认不可用
         */
        btnDone.setEnabled(false);

        /**
         * 当名称不为空时,Done按钮可用
         */
        txtName.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (gesture != null && txtName.getText().toString().trim().length() > 0){
                    btnDone.setEnabled(true);
                } else {
                    btnDone.setEnabled(false);
                }
                return false;
            }
        });

        /**
         * 监听gesture,实现gesture开始和结束的事件
         */
        gestureOverlayView.addOnGestureListener(new GestureOverlayView.OnGestureListener() {
            @Override
            public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
                gesture = null;
                btnDone.setEnabled(false);
            }

            @Override
            public void onGesture(GestureOverlayView overlay, MotionEvent event) {

            }

            @Override
            public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
                gesture = overlay.getGesture();
                if (gesture != null && txtName.getText().toString().trim().length() > 0) {
                    btnDone.setEnabled(true);
                }
            }

            @Override
            public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {

            }
        });

        /**
         * 添加手势
         */
        btnDone.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String name = txtName.getText().toString().trim();
                GestureLibrary library = GestureLibraries.fromFile(gesPath);
                File file = new File(gesPath);

                /**
                 * 若gestures文件存在,则判断是否有同名手势已经存在,若有,则移除后添加,若没有直接添加
                 */
                if (file.exists()){
                    if (library.load()){
                        Set<String> entries = library.getGestureEntries();
                        if (entries.contains(name)){
                            List<Gesture> list = library.getGestures(name);
                            for (int i=0;i<list.size();i++){
                                library.removeGesture(name, list.get(i));
                            }
                        }
                    }
                }

                /**
                 * 添加手势
                 */
                library.addGesture(name, gesture);

                /**
                 * 保存手势
                 */
                if (library.save()){
                    txtName.setText("");
                    gestureOverlayView.clear(true);
                    btnDone.setEnabled(false);
                    Toast.makeText(GestureBuilderActivity.this, "保存成功,路径为:"+gesPath, Toast.LENGTH_SHORT).show();
                }else
                {
                    Toast.makeText(GestureBuilderActivity.this, "保存失败", Toast.LENGTH_SHORT).show();
                }

                /**
                 * 拉开抽屉,显示gesture列表
                 */
                slidingDrawer.toggle();
            }
        });

        /**
         * 取消手势
         */
        btnDiscard.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                txtName.setText("");
                gestureOverlayView.clear(true);
                gesture = null;
                btnDone.setEnabled(false);
            }
        });

        /**
         * 初始化ListView
         */
        listView.setAdapter(adapter);
        UpdateGestureList();

        /**
         * 抽屉打开,显示ListView
         */
        slidingDrawer.setOnDrawerOpenListener(new SlidingDrawer.OnDrawerOpenListener() {
            @Override
            public void onDrawerOpened() {
                imageView.setImageResource(R.drawable.thumb_normal);
                top.setVisibility(View.GONE);
                gestureOverlayView.setVisibility(View.GONE);
                bottom.setVisibility(View.GONE);

                GestureBuilderActivity.this.UpdateGestureList();
            }
        });

        /**
         * 抽屉关闭,隐藏ListView
         */
        slidingDrawer.setOnDrawerCloseListener(new SlidingDrawer.OnDrawerCloseListener() {
            @Override
            public void onDrawerClosed() {
                imageView.setImageResource(R.drawable.thumb_select);
                top.setVisibility(View.VISIBLE);
                gestureOverlayView.setVisibility(View.VISIBLE);
                bottom.setVisibility(View.VISIBLE);
            }
        });

        /**
         * 长按删除
         */
        listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
                AlertDialog.Builder builder = new AlertDialog.Builder(GestureBuilderActivity.this);
                builder.setTitle("是否删除该项?");
                builder.setIcon(R.drawable.ic_launcher);
                builder.setMessage("点击确定,将删除该手势!");
                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        String name = gestureNames.get(position);
                        File file = new File(gesPath);
                        if (file.exists()){
                            GestureLibrary library = GestureLibraries.fromFile(file);
                            if (library != null && library.load()){
                                ArrayList<Gesture> gestures = library.getGestures(name);
                                if (gestures != null){
                                    for (Gesture ges : gestures){
                                        library.removeGesture(name, ges);
                                    }
                                }
                                library.save();
                                GestureBuilderActivity.this.UpdateGestureList();
                            }
                        }
                    }
                });
                builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        return;
                    }
                });

                AlertDialog dialog = builder.create();
                dialog.show();

                return false;
            }
        });
    }

    /**
     * 重新读取gestures中的手势,更新ListView
     */
    private void UpdateGestureList(){
        /**
         * 读取gestures中的手势
         */
        File file = new File(gesPath);
        if (!file.exists()){
            return;
        }

        gestureNames.clear();
        gestureBitmaps.clear();

        GestureLibrary library = GestureLibraries.fromFile(file);
        if (library == null || !library.load()){
            return;
        }

        Object[] names = library.getGestureEntries().toArray();
        if (names == null || names.length < 1){
            return;
        }
        for (Object obj : names){
            String name = obj.toString();
            ArrayList<Gesture> gestures = library.getGestures(name);
            if (gestures != null){
                for (Gesture ges : gestures){
                    gestureNames.add(name);
                    gestureBitmaps.add(ges.toBitmap(80,80,12,Color.YELLOW));
                }
            }
        }
        adapter.notifyDataSetChanged();
    }
}

在 AndoridManifest.xml 中需要定义访问外部存储的权限。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

参考文章:http://blog.csdn.net/ta893115871/article/details/7822816

你可能感兴趣的:(gesture)