Android 自定义手势--输入法手势技术

进行软件开发时,通常我们都喜欢使用较新版本的工具,但这里我为什么使用低版本的SDK来开发Android游戏呢?这里介绍下原因:

1、Android SDK 属于向下兼容!那么低版本可以运行的,高版本基本上更是没问题!(当然每次SDK的更新也会带来新功能,或者修改了一些原来的BUG等等,那么其实对于游戏开发来说,如果你的游戏中不需要更高的SDK版本的支持情况下,完全不必去追求最新的SDK!)

2、使用低版本进行游戏开发这样能兼顾更多的机型,获取更多的用户!

3、大家都知道Android SDK 每次版本的更新,底层代码也会更健壮和优化了!比如我们公司的网游Android版在G2(SDK1.5)上跑起来稍微有些卡,而在我的手机上(SDK2.2)运行起来流畅的没说的~各种舒坦~~但是这样也会带来一些弊端,比如我们自己游戏如果上来就用高版本SDK进行开发,那么对于性能、内存上到底如何,我们都不会很容易的看出其效果,如果我们用低版本的SDK则会让我们明显的感受到性能到底如何~你想想如果你的游戏在1.5 ,1.6上跑起来很流畅,那放在更高版本的SDK机器上更是没说的啦~

     总结:游戏开发中,如果你游戏不需要更高的API的支持,那么推荐基于SDK 1.5和1.6来开发!

在上一篇中我给大家介绍了触摸屏手势操作,但是这种触屏手势的操作比较有局限性;比如我们都知道Android可以利用手势来解锁,类似九宫格形式的,通过自定义的一个单笔画手势可以解开屏幕锁,还可以自定义笔画手势来启动一个应用等,那么这种所谓的笔画手势其实就是今天我要给大家讲解的输入法手势识别技术!这种手势是我们可以自己来自定义,而不像之前的触屏手势操作只是利用Android 对一些触屏动作的封装罢了。下面上几张手机自定义笔划手势解锁的的截图:

OK,那么既然利用手势既然能进行解锁等操作,那么我们游戏开发中,更是可以加入这一亮点了,比如在游戏中我画个圆形执行换背景操作,画个X表示退出游戏等等,等等、哈哈 是不是感觉很有意思了?好的,下面就开始进入讲解!

首先本篇主要学习两点:

1、如何创建输入法手势、删除输入法手势、从SD卡中读取出手势文件!

2、当输入法手势创建后,如何来匹配出我们的自定义手势!

       下面我们来熟习两个类和几个概念:

1、什么是 GestureOverlayView ?  简单点说其实就是一个手写绘图区;

2、什么是 GestureLibrary ?   这个类是对手势进行保存、删除等操作的,一个存放手势的小仓库!

3、笔划是什么,字体笔画?  是的,其实就是跟我们写字的笔划一个概念!

4、什么是笔划类型?   输入法手势操作中,笔划类型有两种;一种是:单一笔划,另外一种是:多笔划

所谓单一笔划笔划就是一笔划画出一个手势,从你手指接触屏幕开始到你离开屏幕笔画就会立刻形成一个手势!一气呵成!

而多笔划则是可以在一定紧凑时间内随意几笔划都可,然后超过这个紧凑时间后便会形成一个手势!

先出项目截图,简单说下其功能和操作:

图1界面中分为3块,从上到下依次是:TextView ,EditText,SurfaceView;然后在SurfaceView后面还有一个覆盖全屏的GestureOverlayView!

图2界面是在创建好的手势中匹配手势的界面,这里很清晰看出来,找的很对 ~嘿嘿~

先看下main.xml:

XML/HTML代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical" android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <TextView android:id="@+id/himi_tv" android:layout_width="fill_parent"
    android:layout_height="wrap_content" android:text="@string/hello"
    android:textSize="15sp" android:textColor="#FFFFFF00" />
  <EditText android:id="@+id/himi_edit" android:layout_width="fill_parent"
    android:layout_height="wrap_content" />
  <RelativeLayout android:layout_width="fill_parent"
    android:layout_height="wrap_content" android:layout_weight="1">
    <com.himi.MySurfaceView android:id="@+id/view3d"
      android:layout_width="fill_parent" android:layout_height="fill_parent" />
    <android.gesture.GestureOverlayView
      android:id="@+id/himi_gesture" android:layout_width="fill_parent"
      android:layout_height="fill_parent" android:layout_weight="1.0"/>
  </RelativeLayout>
</LinearLayout>

xml中注册的有我们自定义的surfaceview,对此不太熟悉可以去看下Android游戏开发6,不多解释了。关于GestureOverlayView这里也只是简单的定义了宽高,还有一些重要的属性设置在代码中设置了,当然xml也可以设置的。

下面看MainActivity.java:

Java代码

/**
 *@author Himi
 *@输入法手势识别
 *@注意: android.gesture这个类在api-4(SDK1.6)才开始支持的!
 *@提醒:默认存到SD卡中,所以别忘记在AndroidMainfest.xml加上SD卡读写权限!
 */
public class MainActivity extends Activity {
  private GestureOverlayView gov;// 创建一个手写绘图区
  private Gesture gesture;// 手写实例
  private GestureLibrary gestureLib;//创建一个手势仓库
  private TextView tv;
  private EditText et;
  private String path;//手势文件路径
  private File file;//
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.main);
    tv = (TextView) findViewById(R.id.himi_tv);
    et = (EditText) findViewById(R.id.himi_edit);
    gov = (GestureOverlayView) findViewById(R.id.himi_gesture);
    gov.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);//设置笔划类型
    // GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE 设置支持多笔划
    // GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE 仅支持单一笔划
    path = new File(Environment.getExternalStorageDirectory(), "gestures").getAbsolutePath();
    //得到默认路径和文件名/sdcard/gestures
    file = new File(path);//实例gestures的文件对象
    gestureLib = GestureLibraries.fromFile(path);//实例手势仓库
    gov.addOnGestureListener(new OnGestureListener() { // 这里是绑定手写绘图区
          @Override
          // 以下方法是你刚开始画手势的时候触发
          public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) {
            tv.setText("请您在紧凑的时间内用两笔划来完成一个手势!西西~");
          }
          @Override
          // 以下方法是当手势完整形成的时候触发
          public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) {
            gesture = overlay.getGesture();// 从绘图区取出形成的手势
            if (gesture.getStrokesCount() == 2) {//我判定当用户用了两笔划
              //(强调:如果一开始设置手势笔画类型是单一笔画,那你这里始终得到的只是1!)
              if (event.getAction() == MotionEvent.ACTION_UP) {//判定第两笔划离开屏幕
                //if(gesture.getLength()==100){}//这里是判定长度达到100像素
                if (et.getText().toString().equals("")) {
                  tv.setText("由于您没有输入手势名称,so~保存失败啦~");
                } else {
                  tv.setText("正在保存手势...");
                  addGesture(et.getText().toString(), gesture);//我自己写的添加手势函数
                }
              }
            } else {
              tv.setText("请您在紧凑的时间内用两笔划来完成一个手势!西西~");
            }
          }
          @Override
          public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {
          }
          @Override
          public void onGesture(GestureOverlayView overlay, MotionEvent event) {
          }
        });
    //----这里是在程序启动的时候进行遍历所有手势!------
    if (!gestureLib.load()) {
      tv.setText("Himi提示:手势超过9个我做了删除所有手势的操作,为了界面整洁一些!"
          + " 输入法手势练习~(*^__^*)~ 嘻嘻!/n操作介绍:(画手势我设置必须画两笔划才行哦~)/n1." +
              "添加手势:先EditText中输入名称,然后在屏幕上画出手势!/n2.匹配手势:"
          + "在EditText输入/"himi/",然后输入手势即可! ");
    } else {
      Set<String> set = gestureLib.getGestureEntries();//取出所有手势
      Object ob[] = set.toArray();
      loadAllGesture(set, ob);
    }
  }
}

这个就是MainActivity主要代码了,其中添加手势、匹配手势、遍历手势、将手势转成图片这些我都单独写成了函数,这样让各位童鞋更清晰思路一些。

从以上代码中我们看出在创建手势之前,手写绘图区(GestureOverlayView)肯定先被创建出来,然后我们就可以在其区域中进行笔划绘画手势了,当然绘画手势前,我们也需要设置了笔划类型,也就是我一开始给大家介绍的,其后最重要的就是手写绘图区的手势监听器绑定,增加OnGestureListener这个监听器重写了四个函数,这里最重要的就两个函数:

  onGestureStarted   和  onGestureEnded  :手势开始和手势结束的监听函数。

尤其是手势结束监听这个函数尤为重要,在其中我设置好几个条件语句,这么几个条件一方面是让大家了解Gesture中一些比较重要常用的方法,另一方面我要提醒各位童鞋:

如果你设置笔划类型是多笔划类型的,那么理想状态下,应该是在一段紧凑时间内,不管你使用了几笔划来绘制手势,系统都应该在判定你在一定短暂时间内没有再进行笔划的时候才应该创建手势,并且系统响应此函数;

其实错了,一开始我也这么想,但是发现,不管你设置的笔划类型是单一的还是多笔划当你手指离开屏幕,不管你当前是第几笔,Android都会去响应这个完成函数,so~ 我在这里调用手势Gesture类中的getStrokesCount()函数,这个函数会记录在紧凑时间内你绘制手势的笔划数,那么根据这个函数我们就可以解决手指离开屏幕总被响应的问题了,因为单一笔划类型永远这个值不会大于1!

而 if (event.getAction() == MotionEvent.ACTION_UP) {}写这个只是给大家演示第二个参数按键动作该怎么用。

那么我们下面就来看如何创建一个手势:

Java代码

public void addMyGesture(String name, Gesture gesture) {
    try {
      if (name.equals("himi")) {
        findGesture(gesture);
      } else {
        // 关于两种方式创建模拟器的SDcard在【Android2D游戏开发之十】有详解
        if (Environment.getExternalStorageState() != null) {// 这个方法在试探终端是否有sdcard!
          if (!file.exists()) {// 判定是否已经存在手势文件
            // 不存在文件的时候我们去直接把我们的手势文件存入
            gestureLib.addGesture(name, gesture);
            if (gestureLib.save()) {////保存到文件中
              gov.clear(true);//清除笔画
              // 注意保存的路径默认是/sdcard/gesture ,so~别忘记AndroidMainfest.xml加上读写权限!
              // 这里抱怨一下,咳咳、其实昨天就应该出这篇博文的,就是因为这里总是异常,今天仔细看了
              // 才发现不是没写权限,而是我虽然在AndroidMainfest.xml中写了权限,但是写错了位置..哭死!
              tv.setText("保存手势成功!因为不存在手势文件," + "所以第一次保存手势成功会默认先创" +
                  "建了一个手势文件!然后将手势保存到文件中.");
              et.setText("");
              gestureToImage(gesture);
            } else {
              tv.setText("保存手势失败!");
            }
          } else {//当存在此文件的时候我们需要先删除此手势然后把新的手势放上
            //读取已经存在的文件,得到文件中的所有手势
            if (!gestureLib.load()) {//如果读取失败
              tv.setText("手势文件读取失败!");
            } else {//读取成功
              Set<String> set = gestureLib.getGestureEntries();//取出所有手势
              Object ob[] = set.toArray();
              boolean isHavedGesture = false;
              for (int i = 0; i < ob.length; i++) {//这里是遍历所有手势的name
                if (((String) ob[i]).equals(name)) {//和我们新添的手势name做对比
                  isHavedGesture = true;
                }
              }
              if (isHavedGesture) {//如果此变量为true说明有相同name的手势
//----备注1-------------------//gestureLib.removeGesture(name, gesture);//删除与当前名字相同的手势
/*----备注2-----------------*/gestureLib.removeEntry(name);
                gestureLib.addGesture(name, gesture);
              } else {
                gestureLib.addGesture(name, gesture);
              }
              if (gestureLib.save()) {
                gov.clear(true);//清除笔画
                gestureToImage(gesture);
                tv.setText("保存手势成功!当前所有手势一共有:" + ob.length + "个");
                et.setText("");
              } else {
                tv.setText("保存手势失败!");
              }
              ////------- --以下代码是当手势超过9个就全部清空 操作--------
              if (ob.length > 9) {
                for (int i = 0; i < ob.length; i++) {//这里是遍历删除手势
                  gestureLib.removeEntry((String) ob[i]);
                }
                gestureLib.save();
                if (MySurfaceView.vec_bmp != null) {
                  MySurfaceView.vec_bmp.removeAllElements();//删除放置手势图的容器
                }
                tv.setText("手势超过9个,已全部清空!");
                et.setText("");
              }
              ob = null;
              set = null;
            }
          }
        } else {
          tv.setText("当前模拟器没有SD卡 - -。");
        }
      }
    } catch (Exception e) {
      tv.setText("操作异常!");
    }
  }

这里也都很好理解,套路类似之前File文件存储的套路,先判断SD是否存在,然后是文件是否存在:

如果文件不存在就先直接添加到手势到手势仓库中,然后手势仓调用gestureLib.save()才算把手势存到SD卡的手势文件中。

文件存在的话还要去判定是否文件中包含了相同名字的手势;当然这里可以不判定是否有相同手势名存在,然后进行删除操作!其实也可不删除,直接添加进去当前新建的手势;原因看了下面的备注解释就明白了;

备注1:因为gestureLib保存的手势是个HashMap, key=手势的名字,value=手势,所以gestureLib.removeGesture(name, gesture);这种删除方式只是删除了手势,该手势名字依旧保存在hashmap中,下次还有相同的name手势存入的时候Hashmap就直接覆盖本条目了。所以根据Hashmap的特征,我们可以不进行删除操作,可以直接gestureLib.addGesture(name, gesture);因为如果出现相同的手势名字的手势,Hashmap就会根据key(手势的名字)直接覆盖其条目的value(手势)滴~

备注2:这里也是一种删除手势的方式,但是这种方式跟备注1的不同,这里是将Hashmap中的条目删除,也就是说key和value都被删去!

下面看下如何把手势转成bitmap:

Java代码

public void gestureToImage(Gesture ges) {//将手势转换成Bitmap
   //把手势转成图片,存到我们SurfaceView中定义的Image容器中,然后都画出来~
   if (MySurfaceView.vec_bmp != null) {
     MySurfaceView.vec_bmp.addElement(ges.toBitmap(100, 100, 12, Color.GREEN));
   }
}

下面是如何遍历手势:

Java代码

public void loadAllGesture(Set<String> set, Object ob[]) { //遍历所有的手势
    if (gestureLib.load()) {//读取最新的手势文件
      set = gestureLib.getGestureEntries();//取出所有手势
      ob = set.toArray();
      for (int i = 0; i < ob.length; i++) {
        //把手势转成Bitmap
        gestureToImage(gestureLib.getGestures((String) ob[i]).get(0));
        //这里是把我们每个手势的名字也保存下来
        MySurfaceView.vec_string.addElement((String) ob[i]);
      }
    }
  }

下面最后来看看手势的匹配:

Java代码

public void findGesture(Gesture gesture) {
  try {
    // 关于两种方式创建模拟器的SDcard在【Android2D游戏开发之十】有详解
    if (Environment.getExternalStorageState() != null) {// 这个方法在试探终端是否有sdcard!
      if (!file.exists()) {// 判定是否已经存在手势文件
        tv.setText("匹配手势失败,因为手势文件不存在!!");
      } else {//当存在此文件的时候我们需要先删除此手势然后把新的手势放上
        //读取已经存在的文件,得到文件中的所有手势
        if (!gestureLib.load()) {//如果读取失败
          tv.setText("匹配手势失败,手势文件读取失败!");
        } else {//读取成功
          List<Prediction> predictions = gestureLib.recognize(gesture);
          //recognize()的返回结果是一个prediction集合,
          //包含了所有与gesture相匹配的结果。
          //从手势库中查询匹配的内容,匹配的结果可能包括多个相似的结果,
          if (!predictions.isEmpty()) {
            Prediction prediction = predictions.get(0);
            //prediction的score属性代表了与手势的相似程度
            //prediction的name代表手势对应的名称
            //prediction的score属性代表了与gesture得相似程度(通常情况下不考虑score小于1的结果)。
            if (prediction.score >= 1) {
              tv.setText("当前你的手势在手势库中找到最相似的手势:name =" + prediction.name);
            }
          }
        }
      }
    } else {
      tv.setText("匹配手势失败,,当前模拟器没有SD卡 - -。");
    }
  } catch (Exception e) {
    e.printStackTrace();
    tv.setText("由于出现异常,匹配手势失败啦~");
  }
}

那么最后给各位童鞋说一下,其实输入法手势操作很是适合游戏中使用,不管是触摸屏手势操作还是今天讲的输入法手势操作如果加到游戏中那都是相当赞的!

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Android中Activity启动默认不显示输入法解决方法

    checkbox控件时导致Activity启动默认不显示输入法. 网上很多资料说要放一个空的Linearlayout,完全是在误导大众, 正确的方法如下: Android Manifest对Activity做如下设置: 复制代码 代码如下: android:windowSoftInputMode="stateHidden"

  • Android游戏开发 自定义手势--输入法手势技术

    进行软件开发时,通常我们都喜欢使用较新版本的工具,但这里我为什么使用低版本的SDK来开发Android游戏呢?这里介绍下原因: 1.Android SDK 属于向下兼容!那么低版本可以运行的,高版本基本上更是没问题!(当然每次SDK的更新也会带来新功能,或者修改了一些原来的BUG等等,那么其实对于游戏开发来说,如果你的游戏中不需要更高的SDK版本的支持情况下,完全不必去追求最新的SDK!) 2.使用低版本进行游戏开发这样能兼顾更多的机型,获取更多的用户! 3.大家都知道Android SDK 每

  • Android中系统默认输入法设置的方法(输入法的显示和隐藏)

    1.调用显示系统默认的输入法 方法一. InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(m_receiverView(接受软键盘输入的视图(View)),InputMethodManager.SHOW_FORCED(提供当前操作的标记,SHOW_FORCED表示强制显示)); 方法二. InputMethodManager

  • 让Android中RadioGroup不显示在输入法上面的办法

    如果你在开发过程中经常使用 RadioGroup,那你是否遇到过下面这种情况 每当你点击EditText弹出输入法时,RadioGroup总是向上移动到输入法的上面. 你可能会想到需要给RadioGroup添加下面这条属性: android:layout_alignParentBottom="true" 但当你打开xml文件时,很尴尬的发现这个属性已经加上了,那要怎么解决这个小bug呢? 其实很简单,只需要在AndroidManifest.xml文件里给当前类的注册信息中添加一个属性:

  • Android程序打开和对输入法的操作(打开/关闭)

    今天整理了一下Android下对输入法的操作:具体如下 一.打开输入法窗口: 复制代码 代码如下: InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // 接受软键盘输入的编辑文本或其它视图 imm.showSoftInput(submitBt,InputMethodManager.SHOW_FORCED); 二.关闭出入法窗口

  • Android编程实现自定义输入法功能示例【输入密码时防止第三方窃取】

    本文实例讲述了Android编程实现自定义输入法功能.分享给大家供大家参考,具体如下: 对于Android用户而言,一般都会使用第三方的输入法.可是,在输入密码时(尤其是支付相关的密码),使用第三方输入法有极大的安全隐患.目前很多网银类的APP和支付宝等软件在用户输入密码时,都会弹出自定义的输入法而不是直接使用系统输入法. 这里介绍的就是如何实现一个简单的自定义输入法.当然,也可以自己写一个Dialog加上几十个按钮让用户输入,只不过这样显得不够专业. (一)首先上效果图: 1.前面两个输入框使

  • Android中EditText屏蔽第三方输入法表情的方法示例

    前言 众所周知如果用第三方的输入法的表情会给开发带来一定的麻烦,一般地像服务器上传的字符都是普通的字符,但是测试,就是狠狠得拿出搜狗输入法,点几个表情,那么问题就来了,要么是显示异常,要么是直接crash,但是有很少的部分机型,会支持,可能是第三方的ROM对Android 的TextView 做了一定的处理,所以,我们只能干掉该死的表情了. 具体的方法如下: 首先是在EditTextView 里面加入TextWatcher 在判断字符的变化时,就判断字符的类型,是否为字符,否则删除原来的字符,重

  • Android 显示和隐藏输入法实现代码

    复制代码 代码如下: // 隐藏输入法 InputMethodManager imm = (InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE); // 显示或者隐藏输入法 imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); toggleSoftInput 这个方法可以转换软件输入法在窗体中的显示状态.

  • Android自定义View接收输入法输入的内容

    前言 可能对于很多新人来讲,看到这个题目,想到的能接收输入法输入的内容大概只有EditText和TextView这两个控件了,其实不然,只要是View的子类,都可以接收输入法输入的内容. 现在我们一步一步来实现,第一步我们得有一个View的子类. 实现方法 //首先我们得重写View中的一个方法,返回true,就是让这个View变成文本可编辑的状态,默认返回false. @Override public boolean onCheckIsTextEditor() { return true; }

  • Android监听输入法弹窗和关闭的实现方法

    用过ios的都知道ios上输入法关闭的同时会自动关闭输入框,那么在android上如何实现监听输入法弹出和关闭呢?本篇文章就为你提供了一种可靠的实现方式. 演示效果视频地址 首先在AndroidManifest中配置 android:windowSoftInputMode="adjustResize" 这样每次输入法弹出和关闭都会重新计算高度实现把布局顶上去的效果 然后我们要自定义一个布局,监听布局大小变化 public class CheckSoftInputLayout exten

  • Android的文本和输入之创建输入法教程

    输入法编辑器(IME)是让用户输入文本的控件.Android提供了一个可扩展的的输入法的框架,它允许应用程序给用户提供另外的输入法,如软键盘或语音输入.这些输入法一旦安装,用户就可以从系统的设置中选择他们想要使用的IME,并且这个设置对整个系统都是有效的,每次只有一种输入法是可用的. 要在Android系统中添加一种输入法,你就要创建一个包含继承了InputMethodService类的类应用程序.另外,你通常还要创建一个"settings"Activity,把选项传递给IME服务.你

  • Android输入法弹出时覆盖输入框问题的解决方法

    当一个activity中含有输入框时,我们点击输入框,会弹出输入法界面,整个界面的变化效果与manifest中对应设置的android:windowSoftInputMode属性有关,一般可以设置的值如下, <activity android:windowSoftInputMode=[ "stateUnspecified", "stateUnchanged", "stateHidden", "stateAlwaysHidden&q

随机推荐