本文共 12686 字,大约阅读时间需要 42 分钟。
概述:
当用户触摸屏幕的时候,会产生许多手势,如 down、up 、scroll 、fling等。
GestureDetector(手势检测)类,通过这个类可以识别很多手势。在识别出于势之后,具体的事务处理则交 由程序员自己来实现。
GestureDetector.OnGestureListener接口:
1.基本讲解
如果我们写一个类并继承自OnGestureListener,则会提示有几个必须重写的函数。
private class gesturelistener implements GestureDetector.OnGestureListener { @Override public boolean onDown(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // TODO Auto-generated method stub return false; } @Override public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO Auto-generated method stub return false; }}
这里重写 了 6 个函数,这些函数在什么情况下才会被触发呢?
• onDown(MotionEvent e):用户按下屏幕就会触发该函数。 • onShowPress(MotionEvent e):如果按下的时间超过瞬间,而且在按下的时候没有松开或者是拖动的,该函数就会被触发。 • onLongPress(MotionEvent e):长接触摸屏,超过一定时长,就会触发这个函数。触发顺序:onDown→onShowPress→onLongPress
• onSingleTapUp(MotionEvent e): 从名字中也可 以看出,一次单独的轻击抬起操作,也就是轻击一下屏幕,立刻抬起来,才会触发这个函数。当然,如果除down以外还有其他操作,就不再算是单独操作了,也就不会触发这个函数了。
单击一下非常快的(不滑动)Touchup,触发顺序:onDown→onSingleTapUp→onSingleTapConfirmed单击一下稍微慢一点的(不滑动) Touchup,触发顺序 :onDown→onShowPress→onSingleTapUp→onSingleTapConfirmed
• onFling(MotionEvent el, MotionEvent e2, float velocityX, float velocityY):滑屏,用户按下触摸屏、快速移动后松开,由一个MotionEvent ACTION_DOWN、多个ACTION_MOVE、一个ACTION_UP触发。
• onScroll(MotionEvent el, MotionEvent e2,float distanceX, float distanceY):在屏幕上拖动事件。无论是用手拖动View,还是以抛的动作滚动,都会多次触发这个函数,在ACTION_MOVE动作发生时就会触发该函数。滑屏,即手指触动屏幕后,稍微滑动后立即松开,触发顺序:onDown→onScroll→onScroll→onScroll→...→onFling
拖动,触发顺序:onDown→onScroll→onScroll→onFling
可见,无论是滑屏还是拖动,影响的只是中间onScroll被触发的数量而己,最终都会触发onFling事件。
2.示例
要使用 GestureDetector,有四步要走 。
(1) 创建 OnGestureListener()监听函数。GestureDetector.OnGestureListener listener = new GestureDetector.OnGestureListener() {};或构造类:private class gestureListener implements GestureDetector.OnGestureListener {}
(2) 创建GestureDetector实例mGestureDetector。
GestureDetector gestureDetector=new GestureDetector(GestureDetector.OnGestureListener listener};GestureDetector gestureDetector=new GestureDetector(Context context, GestureDetector.OnGestureListener listener};GestureDetector gestureDetector=new GestureDetector(Context context, GestureDetector.SimpleOnGestureListener listener);
(3) 在onTouch(View v, MotionEvent event)中进行拦截。
public boolean onTouch(View v, MotionEvent event} { return mGestureDetector.onTouchEvent(event);}
(4) 绑定控件。
TextView tv = (TextView) findViewByid(R.id.tv);tv.setOnTouchListener(this);
示例:
public class MainActivity extends Activity implements View.OnTouchListener { private GestureDetector mGestureDetector; @Override protected void onCreate(Bundle savedinstanceState) { super.onCreate(savedinstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.tv); tv.setOnTouchListener(this); tv.setFocusable(true); tv.setClickable(true); tv.setLongClickable(true); mGestureDetector = new GestureDetector(new MyGestureListener()); } @Override public boolean onTouch(View v, MotionEvent event) { return mGestureDetector.onTouchEvent(event); } private class MyGestureListener implements GestureDetector.OnGestureListener { @Override public boolean onDown(MotionEvent e) { Log.d("TAG", "onDown"); return false; } @Override public void onShowPress(MotionEvent e) { Log.d("TAG", "onShowPress"); } @Override public boolean onSingleTapUp(MotionEvent e) { Log.d("TAG", "onSingleTapUp"); return true; } @Override public boolean onScroll(MotionEvent el, MotionEvent e2, float distanceX, float distanceY) { Log.d("TAG2", "onScroll:" + (e2.getX() - el.getX()) + "" + distanceX); return true; } @Override public void onLongPress(MotionEvent e) { Log.d("TAG", "onLongPress"); } @Override public boolean onFling(MotionEvent el, MotionEvent e2, float velocityX, float velocityY) { Log.d("TAG", "onFling"); return true; } }}
GestureDetector.OnDoubleTapListener接口:
1.构建
方法一:新建一个类,同时派生OnGestureListener和OnDoubleTapListener
private class gestureListener implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {}
方法二:使用GestureDetector.setOnDoubleTapListener()函数设置双击监听
// 构建 GestureDetector 实例mGestureDetector = new GestureDetector(new MyGestureListener());private class MyGestureListener implements GestureDetector.OnGestureListener {}// 设置双击监听mGestureDetector.setOnDoubleTapListener(new MyDoubleTapListener());private class MyDoubleTapListener implements GestureDetector.OnDoubleTapListener {}
可以看到,无论是在方法一还是在方法二中,都需要派生自GestureDetector.OnGestureListener。
2.函数讲解
先来看一下OnDoubleTapListener接口必须重写的三个函数。
private class doubleTapListener implements GestureDetector.OnDoubleTapListener { @Override public boolean onSingleTapConfirmed(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public boolean onDoubleTap(MotionEvent e) { // TODO Auto-generated method stub return false; } @Override public boolean onDoubleTapEvent(MotionEvent e) { // TODO Auto-generated method stub return false; }}
• onSingleTapConfirmed(MotionEvente):单击事件,用来判定该次单击是SingleTap,而不是DoubleTap。如果连续单击两次,就是DoubleTap手势;如果只单击一次,系统等待一段时间后没有收到第二次单击,则判定该次单击为SingleTap,而不是DoubleTap,然后触发SingleTapConfinned事件。触发顺序:onDown→onSingleTapUp→onSingleTapConfinned。OnGestureListener有这样一个函数onSingleTapUp(),它和onSingleTapConfinned()函数容易混淆。二者的区别是:对于onSingleTapUp()函数来说,只要手抬起就会被触发;而对于onSingleTapConfinned0函数来说,如果双击,则该函数就不会被触发。
• onDoubleTap(MotionEvente):双击事件。 • onDoubleTapEvent(MotionEvente):双击间隔中发生的动作。指在触发onDoubleTap以后,在双击之间发生的其他动作,包含down、up和move事件。GestureDetector.SimpleOnGestureListener类:
SimpleOnGestureListener类与OnG巳stureListener和OnDoubleTapListener接口的不同之处在于:
(1)这是一个类,在它的基础上新建类,要用巳xtends派生,而不能用implements继承。 (2)OnGestureListener和OnDoubleTapListen巳r接口里的函数都是被强制重写的,即使用不到也要重写出来一个空函数:而在SimpleOnGestureListener类的实例或派生类中不必如此,可以根据情况,用到哪个函数就重写哪个函数,因为SimpleOnGestureListener类本身已经实现了这两个接口中的所有函数,只是里面全是空的而已。SimpleOnGestureListener类内部的函数与OnGestureListener和OnDoubleTapListener接口中的函数是完全相同的。唯一不同的就是 SimpleOnGestureListener 类内部的函数不必被强制全部重写,用到哪个函数就重写哪个函 数;而OnGestureListener和OnDoubleTapListener是接口,它们内部的函数是必须被重写的。
onFling()函数的应用——识别是向左滑还是向右滑
boolean onFling(MotionEvent el, MotionEvent e2, float velocityX, float velocityY)• el:第一个ACTION_DOWN MotionEvent• e2:最后一个ACTION_MOVE MotionEvent• velocityX:X轴上的移动速度,单位为像素/秒• velocityY:Y轴上的移动速度,单位为像素/秒
实现的功能:当用户向左滑动距离超过100像素,且滑动速度超过100像素/秒时,即判断为向左滑动:向右同理。核心代码是在onFling()函数中判断当前的滑动方向及滑动速度是不是达到指定值。代码如下:
private class simpleGestureListener extends GestureDetector.SimpleOnGestureListener { /* ******OnGestureListener的函数****** */ final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200; // 触发条件: // X轴的坐标位移大于FLING_MIN_DISTANCE,且移动速度大于FLING_MIN_VELOCITY像素/秒 public boolean onFling(MotionEvent el, MotionEvent e2, float velocityX, float velocityY) { if (el.getX() - e2.getX() > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) {// 向左滑 Log.d("TAG", "Fling left"); } else if (e2.getX() - el.getX () > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) {// 向右滑 Log.i("TAG", "Fling right" ); } return true; }}
Window表示窗口,在某些特殊的时候,比如需要在桌面或者锁屏上显示一些类似悬浮窗的效果,就需要用到Window。Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的。而WindowManager则提供了对这些Window的统一管理功能。
Window与WindowManager的联系:
使用WindowManager来添加一个Window:
WindowManager manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(width, height, type, flags, format);manager.addView(btn, layoutParams);
◆ flags:public static final int FLAG_NOT_FOCUSABLE = 0x00000008;表示此Window不需要获取焦点,不接收各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层具有焦点的Window。public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;自己Window区域内的事件自己处理;自己Window区域外的事件传递给底层Window处理。一般这个选项会默认开启,否则其他Window无法接收事件。public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;可以让此Window显示在锁屏上。◆ type:type参数也是int类型的,表示Window的类型。Window有三种类型:应用Window、子Window、系统Window。应用Window:对应着一个Activity。子Window:不能独立存在,它需要附属在特定的父Window中,比如Dialog就是一个子Window。系统Window:需要声明权限才能创建,比如Toast和系统状态栏都是系统Window。Window是分层的,层级大的Window会覆盖在层级小的Window上面。● 应用Window层级范围:1~99● 子Window层级范围:1000~1999● 系统Window层级范围:2000~2999type参数就对应以上这些数字。如果想让Window置于顶层,则采用较大的层级即可。
如果是系统类型的Window,则需要在AndroidManifest.xml中配置如下权限声明:
WindowManager提供的功能很简单,常用的只有三个方法,即添加View、更新View和删除View。这三个方法定义在ViewManager中,而 WindowManager继承自ViewManager。
public interface WindowManager extends ViewManager {}public interface ViewManager { public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view);}
WindowManager操作Window的过程更像在操作Window中的View。
示例:腾讯手机管家悬浮窗的小火箭效果
public class MainActivity extends Activity implements View.OnTouchListener, View.OnClickListener { private Button mCreateWndBtn, mRmvWndBtn; private ImageView mImageView; private WindowManager.LayoutParams mLayoutParams; private WindowManager mWindowManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Intent myIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivityForResult(myIntent, 100); } else { initView(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 100) { initView(); } } private void initView() { mCreateWndBtn = (Button) findViewById(R.id.add_btn); mRmvWndBtn = (Button) findViewById(R.id.rmv_btn); mCreateWndBtn.setOnClickListener(this); mRmvWndBtn.setOnClickListener(this); mWindowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE); } @Override public boolean onTouch(View v, MotionEvent event) { int rawX = (int) event.getRawX(); int rawY = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_MOVE: { mLayoutParams.x = rawX; mLayoutParams.y = rawY; mWindowManager.updateViewLayout(mImageView, mLayoutParams); break; } } return false;// return false:会执行onClick方法 } @Override protected void onDestroy() { try { mWindowManager.removeView(mImageView); } catch (IllegalArgumentException e) { e.printStackTrace(); } super.onDestroy(); } @Override public void onClick(View v) { if (v.getId() == R.id.add_btn) { mImageView = new ImageView(this); mImageView.setBackgroundResource(R.mipmap.ic_launcher); mLayoutParams = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, 2099, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED , PixelFormat.TRANSPARENT); mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; mLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; mLayoutParams.x = 0; mLayoutParams.y = 300; mImageView.setOnTouchListener(this); mWindowManager.addView(mImageView, mLayoutParams); } else if (v.getId() == R.id.rmv_btn) { mWindowManager.removeViewImmediate(mImageView); } }}
需要注意的是,在 SDK API>=23(Android6.0)时,不仅需要在AndroidManifest.xml中添加权限申请,也需要在代码中动态申请。
转载地址:http://wsmqz.baihongyu.com/