一、自定义一个MyCustomTextView类继承View的子类TextView。
public class MyCustomTextView extends TextView {
private static final String TAG = "MyCustomTextView";
public MyCustomTextView(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(TAG, "dispatchTouchEvent: ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
}
二、在MinaActivity的布局文件activiy_main.xml中加入自定义的MyCustomTextView。
<com.ray.myviewtouchevent.custom.MyCustomTextView
android:id="@+id/my_custom_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="MissingConstraints"
android:text="触摸我"/>
三、在MinaActivity中实现自定义TextView的OnTouch和OnClick事件的监听。
public class MainActivity extends AppCompatActivity {
private MyCustomTextView my_custom_tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
my_custom_tv = findViewById(R.id.my_custom_tv);
my_custom_tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
my_custom_tv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
}
四、准备就绪,进入View的dispatchTouchEvent方法查看源码(在自定义重写的dispatchTouchEvent方法中,按住ctrl,点击super.dispatchTouchEvent(event)进入)。
copy主要代码
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
boolean result = false; // 定义dispatchTouchEvent方法返回boolean值的结果result
if (onFilterTouchEventForSecurity(event)) { // 符合androiud触摸的安全策略,进入判断。
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
分析关键代码:
1、ListenerInfo li = mListenerInfo;
把View类中的mListenerInfo赋值给局部ListenerInfo的li。
2、查看ListenerInfo代码。
static class ListenerInfo {
@UnsupportedAppUsage
ListenerInfo() {
}
@UnsupportedAppUsage
public OnClickListener mOnClickListener;
@UnsupportedAppUsage
protected OnLongClickListener mOnLongClickListener;
@UnsupportedAppUsage
private OnTouchListener mOnTouchListener;
}
其中我们关心的包括点击的监听mOnClickListener、长按的监听mOnLongClickListener、和触摸的监听mOnTouchListener。
3、在View类中搜索mListenerInfo赋值初始化代码如下。
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
那我触摸屏幕的什么时候调用了getListenerInfo,我们看在MainActivity设置的点击和触摸的监听方法:
private void initView() {
my_custom_tv = findViewById(R.id.my_custom_tv);
my_custom_tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
my_custom_tv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
}
按住ctrl进入点击setOnClickListener进入View的代码如下:
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
红色代码getListenerInfo().mOnClickListener就可以拿到mListenerInfo并且设置mOnClickListener的值为1。
相同原理setOnTouchListener方法就可以拿到mListenerInfo并且设置mOnTouchListener的值为1。
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
4、接着看下面的if判断:
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
当我们在MainActivity中设置了自定义的TextView后,经过上面的分析:
li != null 成立,自定义TextView设置触摸和点击事件的时候已经赋值;
li.mOnTouchListener != null成立,自定义TextView设置触摸事件的时候已经赋值为1;
(mViewFlags & ENABLED_MASK) == ENABLED成立,这句不用管,不是我们今天的关键;
li.mOnTouchListener.onTouch(this, event):这句代码,需要看我们在MainActivity中
setOnTouchListener
设置触摸事件时候怎么处理,默认返回false。如果默认返回false的话,这样li.mOnTouchListener.onTouch(this, event)就不成立,if判断失败。
5、如果上面的if判断失败,result为false:
if (!result && onTouchEvent(event)) {
result = true;
}
if判断中的!result成立。
然后查看简化后的View中的onTouchEvent方法
:
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int action = event.getAction();
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
.........
break;
case MotionEvent.ACTION_DOWN:
........
break;
case MotionEvent.ACTION_CANCEL:
........
break;
case MotionEvent.ACTION_MOVE:
break;
}
return true;
}
return false;
}
这段代码关键的内容就是if (clickable || (viewFlags & TOOLTIP) == TOOLTIP)判断成立,进入到switct,然后根据action,也就是当前事件是按下(Down)、移动(Move)还是抬起(Up)来对事件进行不同的处理,最后返回的true表示该View对事件进行了消费。反之if判断不成立,返回false,对事件不进行处理。
五、总结和验证。
1、自定义MyCustomTextView后,不再MainActivity中进行setOnClickListener和setOnTouchListener。
分析:我们点击MyCustomTextView后,调用MyCustomTextView中重写的dispatchTouchEvent方法,然后调用View中的dispatchTouchEvent方法:
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
因为li为空,所以调用MyCustomTextView中重写的onTouchEvent方法。
结论:
2、自定义MyCustomTextView后,在MainActivity中进行setOnTouchListener并且返回true。
private void initView() { my_custom_tv = findViewById(R.id.my_custom_tv); // my_custom_tv.setOnClickListener(new View.OnClickListener() { // @Override // public void onClick(View v) { // // } // }); // my_custom_tv.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return true; } }); }
分析:我们点击MyCustomTextView后,调用MyCustomTextView中重写的dispatchTouchEvent方法,然后调用View中的dispatchTouchEvent方法:
ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; }
li != null 成立,自定义TextView设置触摸和点击事件的时候已经赋值;
li.mOnTouchListener != null成立,自定义TextView设置触摸事件的时候已经赋值为1;
(mViewFlags & ENABLED_MASK) == ENABLED成立,这句不用管,不是我们今天的关键;
li.mOnTouchListener.onTouch(this, event):这句代码,在MainActivity中
setOnTouchListener时候返回了ture,成立。
所以if中的所有条件都成立,result返回ture,即消费了事件。