您的位置:首页 > 其它

dispatchTouchEvent小分析

2015-11-16 13:04 323 查看
对于dispatchTouchEvent一直都处于迷迷糊糊的理解状态,就知道他有分发事件的作用,但是真正每次用起来却又很茫然。今天小小的研究了一下,希望能给大家带来帮助。

下面我一点一点的贴出源码来分析。

public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
/**是否拦截  ||  onInterceptTouchEvent是否返回fasle,注意这里是取反.
*这里引用一下老郭blog中说的一句话"1.disallowIntercept是指是否禁用掉事件拦截的功能,默认是
*false,也可以通过调用requestDisallowInterceptTouchEvent方法对这个值进行修改.2.竟然就是
*对onInterceptTouchEvent方法的返回值取反!也就是说如果我们在onInterceptTouchEvent方法中
*返回false,就会让第二个值为true,从而进入到条件判断的内部,如果我们在nInterceptTouchEvent
*方法中返回true,就*会让第二个值为false,从而跳出了这个条件判断"
*/
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
//然后这里开始循环子view(注意这里是倒序)
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
//如果x,y坐标在子view中这进入if
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev))  {
//赋值给目标对象
mMotionTarget = child;
return true;
}
}
}
}
}
}
.......
return target.dispatchTouchEvent(ev);
}


好的,简单的注释了一下代码。接下来我们写一个小demo来实践一下。RelativeLayout中写2个button,btn1在下层,btn2在上层,分别点击看其点击是否有效。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:id="@+id/btn1"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="#a59a6a"
android:text="btn1"
/>
<Button
android:id="@+id/btn2"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="btn2"
android:background="#aaaaaa"
/>
</RelativeLayout>


然后注册点击事件:

findViewById(R.id.btn1).setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "btn1~~~~~", 1).show();
Log.d("MainActivity", "btn1 on click~~~~~");
}
});
findViewById(R.id.btn2).setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "btn2~~~~~", 1).show();
Log.d("MainActivity", "btn2 on click~~~~~");
}
});




然后分别点击btn1,和btn2.打出log:



当然大伙很容就说了,当然了点击btn1当然打出log,btn2当然打出log。这还用说吗。好的。用源码的角度来解释一下,点击btn1的时候,点击x、y的坐标点在btn1上,不在btn2上,所以btn2不响应。点击btn2的时候x,y的坐标在2个上面,那问题来了,为什么就btn2相应了而btn2没有相应了。

来看看源码,首先进行for循环,然后取mChildrenCount-1的view,也就是btn2后return结束。

相信大家用过bringToFront,说我用代码强制绘制到顶部。那这种分发机制还管用吗。ok,改一下demo。



在xml中btn3放在btn2的上面,然后在代码中将btn2强制置顶。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<Button
android:id="@+id/btn1"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="#a59a6a"
android:text="btn1" />

<com.example.ontouchtext.MyFrameLayout
android:id="@+id/framelayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<com.example.ontouchtext.Btn2
android:id="@+id/btn2"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#aaaaaa"
android:text="btn2" />

<com.example.ontouchtext.Btn3
android:id="@+id/btn3"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="50dp"
android:background="#1babaa"
android:text="btn3" />

</com.example.ontouchtext.MyFrameLayout>

</RelativeLayout>


然后在activity中编写置顶代码。

btn2.bringToFront();


自定义Btn2,Bnt3。

public class Btn2 extends Button{

public Btn2(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.d("Btn2", "btn2 on click~~~~~");
}
return super.onTouchEvent(event);
}
}


public class Btn3 extends Button{

public Btn3(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.d("Btn3", "btn3 on click~~~~~");
}
return super.onTouchEvent(event);
}
}


自定一个MyFrameLayout

public class MyFrameLayout extends FrameLayout {

public MyFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final float xf = ev.getX();
final float yf = ev.getY();
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Rect frame = new Rect();
for (int i = getChildCount() - 1; i >= 0; i--) {
Button v = (Button) getChildAt(i);
Log.d("MainActivity", v.getText().toString());
v.getHitRect(frame);
Log.d("MainActivity", frame.contains((int)xf,(int)yf)+"");
}
}
return super.dispatchTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(" onTouchEvent", "MyFrameLayout onTouchEvent go on~~");
return super.onTouchEvent(event);
}
}




这时候如果我点击btn2和btn3的交集处,到底会触发bt2还是btn3了,我们看看log。



btn2触发了,再看看上面的先打印的是btn2,btn3说明我们调用了btn2.bringToFront()后系统将本来在最后面的btn3放在了btn2的后面,倒序循环后,btn2最先进入到for循环中,然后复制给了target后return。

希望对大家有帮助,如有错误希望大家指出。

附上代码:这里写链接内容
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: