Touch事件分发中只有两个主角:ViewGroup和View。Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。
View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。
ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。
而dispatchTouchEvent是用来分发触摸事件的,onTouchEvent是用来处理触摸事件的()
第一步、触摸事件的产生:
触摸事件由用户触摸屏幕,系统会生成一个MotionEvent触摸事件对象,里面封装了此次动作的所用信息,如时间,位置坐标等。
然后系统会将当前这个触摸事件交给当前的Activity来处理。
第二步、触摸事件的分发与处理:
1、系统将触摸事件交给Activity处理后,会调用Activity的dispatchTouchEvent方法进行事件的分发,Activity首先会找到活动对应的布局中的
父ViewGroup,将触摸事件交给他处理。
2、然后这个ViewGroup又会调用自身的dispatchTouchEvent方法对事件进行分发:
这里又分为两种情况:
第一种是ViewGroup分发事件找到了对应的子View进行处理。
交给它的子孩子View来处理,子孩子View得到事件后,也会调用自身的dispatchTouchEvent方法,但是View的分发则是调用View自身的onTouchEvent或者View设置的
OnTouchListener的onTouch方法对事件进行处理(这里值得一提的是,onTouch方法的优先级是比onTouchEvent方法的优先级高,即先执行onTouch方法)。
在onTouchEvent和onTouch方法中,都需要返回一个布尔类型的值,如果返回true,则说明触摸事件已经消费,不在往下传递,有拦触摸截掉事件的意义,其他子
View就不能响应到这个触摸事件,如果返回false,则说明触摸事件没有被消费,需要传递给其他子View处理。如果所有的子View都不消费这个触摸事件
,最后这个触摸事件就会交由Activity的onTouchEvent处理。
一个完整触摸事件由down、move、up组成。
这里值得我注意的是:Activity分发事件是根据该View是否消费了动作为Down的触摸事件,如果没有消费,则不会向它分发动作为UP和MOVE的触摸事件。
第二种是ViewGroup分发事件没找到对应的子View进行处理。
这时事件就由ViewGroup自己处理,它会调用自身的onTouchEvent返回false,说明事件没有找到消费者,触摸事件没有被消费,move,up 动作不需要再往下分发了,
直接调用当前Activity的onTouchEvent(ev)方法处理。
只有View消费了动作为Down的触摸事件,这个View才能响应其他两个动作的触摸事件。
说完安卓事件的分发,接下来我们就来说说图片拖动效果的实现。
第一步、为ImageView设置一个OnTouchListener,并实现其onTouch方法
第二步、拖动逻辑的实现:
1、手指按到imageView上会有一个初始位置(startX,startY)
2、手指在屏幕上移动,移动到一个新的位置(nowX,nowY)
3、计算手指在屏幕的偏移量
dx=nowX-startX
dy=nowY-startY
4、重新计算图片的位置,并控制范围,使图片不会超出边框
int left=miv_show.getLeft()+dx; int top=miv_show.getTop()+dy; int right=miv_show.getRight()+dx; int buttom=miv_show.getBottom()+dy;
if(left<=0){ right+=-left; left=0; } if(top<=0){ buttom+=-top; top=0; } if(right>=mRight){ left+=-(right-mRight); right=mRight; } if(buttom>=mButtom){ top+=-(buttom-mButtom); buttom=mButtom; }
5、通过调用imageView.layout(left,top,right,bottom)立即更新imageView的位置
(left指的是图片左上角的x坐标,top指的是图片左上角的y坐标,right指的是右下角的x坐标,bottom知道是图片右下角的y坐标)
6、重新初始化手指的开始位置(startX=nowX,startY=nowY)
7、当手指离开图片的时候记录当前图片的位置,在下次imageView加载完成后回显。
这里问题就来了,imageView什么时候会加载完成呢?当活动创建调用onCreate方法的时候
imageView是还没有加载完成的,如果在onCreate方法中调用imageView.layout方法是不起作用的
,故我们要设置一个监听,当图片加载完成后回调该监听的方法,在这个方法里面调用layout回显图片的位置,
代码如下:
miv_show.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { System.out.println("view加载完成"); int lastLeft=sp.getInt("lastLeft", 0); int lastTop=sp.getInt("lastTop", 0); int lastRight=sp.getInt("lastRight", 0); int lastButtom=sp.getInt("lastButtom", 0); if(!(lastLeft==0&&lastTop==0&&lastRight==0&&lastButtom==0)){ //说明保存了位置 miv_show.layout(lastLeft, lastTop, lastRight, lastButtom); } } });
代码实现如下:
miv_show.setOnTouchListener(new OnTouchListener() { private int startX; private int startY; @Override public boolean onTouch(View v, MotionEvent event){ int mRight=rl.getRight(); int mButtom=rl.getBottom(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN: startX=(int) event.getRawX(); startY=(int) event.getRawY(); break; case MotionEvent.ACTION_MOVE: int nowX=(int) event.getRawX(); int nowY=(int) event.getRawY(); int dx=nowX-startX; int dy=nowY-startY; int left=miv_show.getLeft()+dx; int top=miv_show.getTop()+dy; int right=miv_show.getRight()+dx; int buttom=miv_show.getBottom()+dy; if(left<=0){ right+=-left; left=0; } if(top<=0){ buttom+=-top; top=0; } if(right>=mRight){ left+=-(right-mRight); right=mRight; } if(buttom>=mButtom){ top+=-(buttom-mButtom); buttom=mButtom; } miv_show.layout(left,top, right, buttom); startX=nowX; startY=nowY; break; case MotionEvent.ACTION_UP: int lastLeft=miv_show.getLeft(); int lastTop=miv_show.getTop(); int lastRight=miv_show.getRight(); int lastButtom=miv_show.getBottom(); Editor editor=sp.edit(); editor.putInt("lastLeft", lastLeft); editor.putInt("lastTop", lastTop); editor.putInt("lastRight", lastRight); editor.putInt("lastButtom", lastButtom); editor.commit(); break; } //返回true代表事件已经消费了,不需要再往下传递 //代表所有触摸事件都在这里被消费了,不再往下传递 return true; } });