Android(六)事件处理与手势

Android 事件处理与手势

在这里插入图片描述

基于监听的事件处理方法与基于回调的事件处理方法的区别:

在这里插入图片描述

物理按键事件处理

在这里插入图片描述
按下而不松开则是onKeyDown()方法

松开手指就是onKeyUp()方法

长按不松开则为onKeyLongPress()

音量键对应的常量为KEYCODE_VOLUME_UP(声音增加)与KEYCODE_VOLUME_DOWN(声音减少)

电源键对应的常量为KWYCODE

返回键对应的常量为KEYCODE_BACK

主屏键对应的常量是KEYCODE_HOME

菜单键对应的常量是KEYCODE_MENU

双击两次退出键退出当前应用思路:

第一步:重写onKeyDown()方法来拦截用户单击后退按钮事件(在代码编辑区点击鼠标右键,选择Generate产生-Override Methods重写方法,选择onkeydown方法;第二步:创建退出方法exit()
if(keyCode==KeyEvent.KEYCODE_BACK){}//用if语句判断按下的是哪一个键
exit();//进行退出操作
return true;//屏蔽返回键
void为无返回值
public void exit(){}//创建退出方法exit()
if((System.currentTimeMillis()-exitTime)>2000){}在退出的方法中判断两次按下返回按键的时间差是否大于两秒,如果是就弹出消息提示框,如果否,就退出当前应用,当前系统时间-第一次按下返回按键的时间相减如果大于两秒
private long exitTime=0;定义一个全局变量,来记录第一次按下返回按键的时间,long为长整类型
定义全局变量:在public class MainActivity extends AppCompatActivity{的下方进行编写
System.currentTimeMillis()//获取当前系统时间
exitTime=System.currentTimeMillis();//将系统当前时间赋值给exitTime,初始化exitTime变量
用else来判断另一种情况,当前时间距离第一次按下返回按键的时间小于二秒
finish();//退出当前应用
System.exit(0);//完成退出应用的操作
代码如下:

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.Toast;
public class MainActivity extends Activity {
  private long exitTime=0;//定义一个exitTime变量


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

  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
      if(keyCode==KeyEvent.KEYCODE_BACK){//判断按下的键是否为返回键
          exit();//实现exit方法
          return true;
      }
      return super.onKeyDown(keyCode, event);
  }
  public void exit(){//重写exit方法
      if((System.currentTimeMillis()-exitTime)>2000){//首先exittime值为0,肯定当前时间与exittime的时间差超过两秒。故会弹出消息提示框,然后当前的时间赋值给exittime,再进行判断,如果按下返回键,再调用exit方法,再进行判断,如果现在当前时间减去之前当前时间少于两秒,就执行退出按钮,如果没按下,就不会调用方法,如果超过两秒按下,再调调用exit方法,再进行判断,如果现在当前时间减去之前当前时间多于两秒,就会弹出消息提示框,简单来说就是exittime用当前的时间表示,前面的时间与后面的时间进行比较
          Toast.makeText(MainActivity.this,"再按一次退出程序",Toast.LENGTH_SHORT).show();
          exitTime=System.currentTimeMillis();}else{
          finish();
          System.exit(0);


          }
      }

  }

触摸屏事件处理

单击事件

在这里插入图片描述

设置单击事件监听器的时候就要重写onclicklistener方法:OnClikListener接口含了一个onClick方法,接口的方法必须要实现,创建接口的实现类的对象的时候,就需要onclick方法,onclick方法是单击对象所触发的,一般情况下onclick方法中编写一些事件处理操作代码

setOnClickListener()传递的参数就是onclicklistener的接口的实现类对象,通过匿名内部类来实现

长按事件

需要长按至少两秒才会触发

setOnLongClickListener();//长按事件监听器

View.OnLongClickListener//接口的实现类对象

public static interface View.OnLongClickListener{//interface为接口,

public boolean onLongClick(Viewv);//声明一个onLongClick();方法,实现接口的时候要重写onLongClick方法,长按事件被触发就被调用

长按会弹出选择项方法:第一步:在MainActivity中重写onCreateContextMenu菜单,为菜单添加选项值(右键-Generate-Override Methods-onCreateContextMenu).。第二步:将长按事件注册到菜单中,并打开菜单

menu.add("")//对象.方法来为菜单添加选项值,

imageView.setOnLongClickListener(new View.OnLongClickListener())//设定一个长按事件监听器
在重写的onLongClick方法中进行编写

registerForContextMenu(v);将长按事件注册到菜单中,参数为上面传的v

openContextMenu(v);//打开菜单

主程序:
 主程序:package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends Activity {



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageView = findViewById(R.id.image);
        imageView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
               registerForContextMenu(v);
               openContextMenu(v);

                return false;
            }
    });

}

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.add("查看");
                menu.add("删除");
    }
}
布局管理器:
<ImageView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="50dp"
    android:src="@drawable/photo"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

触摸事件

setOnTouchListener()//触摸事件监听器

view.OnTouchListener//接口实现类对象

Public interface View.OnTouchListener{
public abstract boolean onTouch(View v,MotionEvent event);
}

实现OnTouchListener接口的话要重写onTouch方法,在onTouch中编写相关的代码,创建触摸事件监听器的时候创建MotionEvent事件对象
MotionEvent:保存发生触摸的位置时间等细节信息,保存触摸的xy坐标

java-com.xxx-右键-new-java class//创建一个自定义的view,用于绘制帽子

类(class)继承(extend)view,出现下划线,在下划线点击,按ait加回车,创建构造方法(create constructor matching super ),选择第一个带一个参数的构造方法(view(Context)))

public float bitmapX//设置一个全局变量,储存x坐标

public float bitmapY//设置一个全局变量,储存y坐标

在构造方法中编写帽子位置的初始值

bitmapX=65;//设定x位置的初始值

bitmapY=0;//设定y坐标的初始值

重写绘画方法(onDraw(canvas:Canvas):void),里面绘制帽子

Paint paint =new Paint();

Bitmap bitmap= BitmapFactory.decodeResource(this.getResources(),R.drawable.hat) //导入帽子图片并创建bitmap对象

canvas.drawBitmap(bitmap,bitmapX,bitmapY,paint);//为画布设置xy和画笔工具

if(bitmap.isReccycled()){
bitmap.recycle();
} //判断图片是否回收,没有回收图片的话,强制回收图片

第一步:创建自定义View,用来绘制帽子的,第二步:创建并实例化帽子类的一个对象,并为帽子添加触摸事件监听器,在重写的触摸方法中根据触摸的位置重绘帽子,第三步:把帽子添加到布局管理器中

HatView hat=new HatView(MainActivity.this);//创建帽子对象,参数是MainActivity.this

hat.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(View v,MotionEvent event){
return ture;
}
})//为帽子设置触摸事件监听器,在重写的onTouch方法中编写改变帽子位置的代码

hat.bitmapX=event.getX()-80;//获取到事件的x坐标赋值给帽子的x坐标,而且要减去帽子一半的宽度,这样帽子才是正中的位置,根据我们手指进行移动

hat.bitmapY=event.getY()-80;//获取到事件的y坐标赋值给帽子的y坐标,而且要减去帽子一半的宽度,这样帽子才是正中的位置,根据我们手指进行移动

hat.invalidate();//重绘帽子组件

RelativeLayout rl=(RelativeLayout)findViewById(R.id.relativeLayout);//获取相对布局管理器

rl.addView(hat);//对象.方法来添加,参数为添加的内容

实例:图片随指尖移动:

主程序

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      final HatView hat =new HatView(MainActivity.this);
      hat.setOnTouchListener(new View.OnTouchListener() {
          @Override
          public boolean onTouch(View v, MotionEvent event) {
              hat.bitmapX=event.getX()-80;
              hat.bitmapY=event.getY()-80;
              hat.invalidate();





              return true;
          }
      });
      RelativeLayout relativeLayout =findViewById(R.id.relativeLayout);
      relativeLayout.addView(hat);
  }
}

布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/relativeLayout"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/man"
    tools:context=".MainActivity"></RelativeLayout>

帽子视图的类

package com.example.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
public class HatView extends View {
 public float bitmapX;
         public float bitmapY;
 public HatView(Context context) {
     super(context);
      bitmapX=65;
      bitmapY=0;

 }

 @Override
 protected void onDraw(Canvas canvas) {
     super.onDraw(canvas);
     Paint paint =new Paint();
     Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(),R.drawable.hat);
     canvas.drawBitmap(bitmap,bitmapX,bitmapY,paint);
     if (bitmap.isRecycled()){
         bitmap.recycle();
     }


 }
}

单击事件与触摸事件的区别

先触发触摸事件,再触发单击事件,如果ontouch事件没有完全消费掉事件,再触发单击事件

消费事件:一次ui操作是否相应,如果重写的方法返回true,那么就是消费了事件。如果返回false,就没消费事件,交给后面的事情进行处理

Log.i("","");//输出日志信息,如果出现错误就按alt+回车导入log类

if(event.getAction()==MotionEvent.ACTION_DOWN){}//判断获取事件动作(event.getAction())是否等于手指按下

else if(){}//符合另一个条件时进行执行

event.getAction()==MotionEvent.ACTION_UP//判断获取事件动作,是否等于手指抬起

实例
package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button =(Button) findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("onClick","单击事件");

            }
        });
               button.setOnTouchListener(new View.OnTouchListener(){
                   @Override
                   public boolean onTouch(View v, MotionEvent event) {
                      if(event.getAction()==MotionEvent.ACTION_DOWN ){
                          Log.i("onTouch","按下");
                      }else if(event.getAction()==MotionEvent.ACTION_UP){
                          Log.i("onTouch","抬起");
                      }
                       return true;
                   }
               });
    }
}

布局

<Button
    android:id="@+id/btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="254dp"
    android:text="触摸事件和单击事件的区别"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

}

单击事件触发一个动作,触摸事件触发两个动作(按下和抬起)

手势检测

GestureDetector,创建类的对象的时候需要创建GestureDetector类的OngestureListener接口的实例,一个接口代表一个监听器,需要重写onDown(触摸时按下触发) onFling(当手指在触摸屏时拖过时触发) onLongPress(当用户在屏幕长按时触发) onScroll onShowPress onSingleTapUp(当用户的手指在触摸屏轻击时触发) 的方法

布局管理器中中的ViewFlipper,是用动画控制多个组件的切换效果
第一步:让mainacitvity实现gesturedetector ongesturelistener接口,并实现其所有方法

public cldss MainActivityextends AppCompatActivity implements GestureDetector.OnGestureListener,在红色的灯泡中点击implement methods实现方法

Animation[] animation= new Animation[4];//设置动画数组,为viewflipper组件指定切换动画,数组的长度为4

final intdistance=50;//定义一个整型的变量,用于记录手势两点动作的最小距离

private int[] images =new int[]{R.drawble.img01}//将图片资源导入到数组中,记录要显示的图片资源

第二步,定义一个全局的手势检测器

定义一个gesturedetector类的对象

detector=new GestureDetector(MainActivity.this,this);自定义一个手势检测器,并初始化

第三步:将要显示的图片加载到ViewFLipper中,并且初始化动画数组
ViewFlipper flipper;定义一个全局的viewflipper对象,不进行初始化,方便之后的调用

flipper=(ViewFlipper)findViewById(R.id.flipper);//获取到布局管理器中的flipper组件

for(int i=0;i<images.length;i++){}//用for循环加载图片数组的图片

ImageView imageView=new ImageView(this);//创建对象并实例化imageview

imageView.setImageResource(images[i]);//指定imageview要显示的图片,参数是图片数组元素

flipper.addView(imageView);//设置flipper要加载的图片,设置flipper加载imageview里面的内容

animation[0]=AnimationUtils.loadAnimation(this.R.anim.slide_in_left )//初始化数组元素,并加载动画资源文件(loadAnimation),第一个参数是指定内容,第二个参数是指定动画资源

第四步:在onFling()方法中通过触摸事件的x左边判断是向左滑动还是向右滑动,并且为其设置动画

if(e1.getX()-e2.getX()>distance){}//判断是否从右向左进行滑动,若第一次触摸获取到的x坐标与第二次触摸获取到的x坐标的差值是自己设置的定值,则可以认定是从右向左滑动

flipper.setInAnimation(animation[2]);//为flipper设置进入动画,设置成第三个数组动画元素(淡入)

flipper.setInAnimation(animation[2]);//为flipper设置进入动画,设置成第三个数组动画元素(淡出)

flipper.showPrevious();显示上一张图片

else if(e2.getX()-e1.getX()>distance){}//判断是否从左向右进行滑动,若第二次触摸获取到的x坐标与第一次触摸获取到的x坐标的差值是自己设置的定值,则可以认定是从左向右滑动

flipper.showNext();显示下一张图片

第五步:将activity上的触摸事件交给gesturedetector处理

重写onTouchEvent 的方法,将super改成detector(手势)

手势添加

打开avd 打开菜单。打开Gestures Bui…

Reload为导入手势

点击Add gesture 添加手势。输入名称。在下面黑色空白处绘制手势,按done 来完成添加绘制的手势

打开android device monitor 双击 logcat打开面板

选中avd,在右边上面的面板选择File Explorer(文件管理器),找到storage,展开,展开由数字和英文组成的文件夹,选择gestures,选择pull a file from the device(红色减号的左边),选择要保存的位置,即可导出自定义手势

模拟微信手写输入实现识别用户输入手势的功能

在android studio-raw-中创建文件夹:在res中右键-new-Directory

将自定义的手势复制粘贴到raw目录

第一步:在布局文件中添加一个编辑框(EditText)和一个手势组件
<android.gesture.GestureOverlayView> 内容 </android.gesture.GestureOverlayView>
android:gestureStrokeType=“multiple”>//设置手指设置为多指设置

第二步:让MainActivity实现GestureOverlayView.onGesturePerformedListener接口,并重写onGesturePerformed()方法

public class MainActivity extends Activity implements GestureOverlayView.OnGesturePerformedListener{}//MainActivity实现GestureOverlayView.onGesturePerformedListener接口,在红色小灯泡中选择Implement methods来实现方法-ok

第三步:加载raw文件夹中的手势文件,如果加载失败退出应用

在onCreate方法上面定义的变量为全局变量

private GestureLibrary library//定义一个私有手势变量

private EditText editText;//定义一个私有编辑框的变量
library=GestureLibraries.fromRawResource(MainActivity.this,R.raw.gestures)//对象。方法来加载raw文件夹中的手势文件,第一个参数是上下文对象,第二个参数是要加载的手势资源,然后赋值给library

if(!library.load()){}//判断加载失败

第四步获得GestureOverlayView组件,并且为其设置属值和事件监听器

获得GestureOverlayView组件//GestureOverlayView gestureOverlayView=(GestureOverlayView)findViewById(R.id.gesture);//获得GestureOverlayView组件

gestureOverlayView.setGestureColor(color.BLACK);//设置手势的颜色为黑色

gestureOverlayView.setFadeOffset(1000);//设置淡出屏幕的间隔事件,为一秒

gestureOverlayView.addOnGesturePerformedListener(this);//为手势组件( gestureOverlayView),添加手势监听器

第五步:在重写的onGesturePerformed方法中获得最佳匹配进行显示,并更新编辑框

ArrayListgestures=library.recognize(gesture);//获得全部的运算结果

int index=0;//保存当前预测的索引号

double score=0.0;//保存当前预测的得分

for(int i=0;i<gestures.size();i++){}//通过for循环来获得匹配最佳匹配结果

Prediction result=gestures.get(i); //获得第一个运算结果

if(result.score>score){}//判断预测的结果是否大于当前的预测得分

index=i;//索引号就等于循环的次数

score=result.score;让分数等于新获得的运算结果

String text=editText.getText().toString();//获取到编辑框原有的内容,转化为字符串资源(.toString),传递给text变量

text+=gestures.get(index).name//将获取到的最佳匹配结果

(gestures.get(index).name)与之前的内容(text)进行连接

editText.setText(text);//更新编辑框内容,设置编辑框的内容为text,为最佳的匹配结果

打开manifests-AndroidManifest.xml,设置应用的主题
android:theme="@style/Theme.AppCompat.Ligth.DarkActionBar"//设置主题为浅色背景,深色Actionbar

猜你喜欢

转载自blog.csdn.net/u013074761/article/details/105184641
今日推荐