自定义View的几个注意点
View的坐标系
自定义View重写OnTouchEvent(MotionEvent event)方法后Android Studio提示重写performClick(方法)
我们在重写OnTouchEvent(MotionEvent event)方法时,Android Studio总是提示我们“Custom View XXXX overrides onTouchEvent but no performClick more…”, 分析performClick()源码后我们知道:
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
performClick() 方法中实际上会执行CustomView.setOnClickListener(){}中的事件(前提是我们设置了setOnClickListener()),而我们重写OnTouchEvent后,setOnClickListener可能会得不到执行,当然,我们发现自己即使不关心Android Studio给出的这个警告,仍然运行正常,我们仔细想想也符合逻辑:既然自定义View重写了OnTouchEvent()方法,那么就不应该再设置setOnClickListener监听事件,Android Studio给出提示只是为了当开发者重写onTouchEvent方法后,又在外部给这个自定义View设置了监听事件,如何能执行到监听事件(setOnClickListener是事件分发的最后一层)。
其实消不消除这个waring都没什么问题,我们在这里说下如何消除这个waring, 首先在控件中重写performClick方法,然后在onTouchEvent中的某个Action行为中执行performClick(),如下所示,
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
performClick();
break;
}
return false;
}
@Override
public boolean performClick() {
return super.performClick();
}
不过,我们还发现,我们即使重写了onTouchEvent方法,依旧可以执行到setOnClickListener级别(我在上面也说了,它并不是执行不到,只是在某些情况下执行不到), 我们在onTouchEvent方法末尾一般会返回父类该方法执行的结果。
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
我们分析View的onTouchEvent源代码得知,
public boolean onTouchEvent(MotionEvent event) {
switch (action) {
case MotionEvent.ACTION_UP:
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
}
private final class PerformClick implements Runnable {
@Override
public void run() {
recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);
performClickInternal();
}
}
private boolean performClickInternal() {
// Must notify autofill manager before performing the click actions to avoid scenarios where
// the app has a click listener that changes the state of views the autofill service might
// be interested on.
notifyAutofillManagerOnClick();
return performClick();
}
在父类的Up事件中,同样也会执行performClick(),进而我们就看到了,我们设置的自定义View的setOnClickListener也执行到了。
个人总结:自定义的View如果在onTouchEvent(MotionEvent event)中没有使用过super.onTouchEvent(event)代码的话(在一些文章中也见过直接根据情况返回true/false的),这时候如果也在外部(比如Activity)给这个自定义View设置了setOnClickListener,那么我们就需要在onTouchEvent中根据具体情况来决定把performClick()代码放在哪个位置合适(一般也是放在UP事件中),然后在控件中重写performClick()。不建议在onTouchEvent(MotionEvent event)方法中performClick()与super.onTouchEvent(event)都出现。
Paint关于PorterDuffXfermode的几个问题
PorterDuffXfermode类似数学中关于图形之间的交集,并集,差集问题,需要注意的是:dst是下层,先画上去的图形;src是上层,后画上的图形;用句谚语来形容的话就是“后来者居上”。
首先看下google给出的官方样例,
但我们真正使用的时候回发现实际情况与此图并不完全符合,具体情况可以查看此博客:https://blog.youkuaiyun.com/wingichoy/article/details/50534175,
这里我们主要说下结论:
首先PorterDuffXfermode会受硬件加速的影响,建议自定义View如果使用PorterDuffXfermode关闭硬件加速;
其次,google给出的官方图成立的前提是存在两个bitmap。
PorterDuffXfermode的各种模式详解https://www.jianshu.com/p/0f64daf202f2
###ViewGroup 中 onInterceptTouchEvent()的几条结论。
- down事件首先传递到onInterceptTouchEvent()方法;
- 如果该ViewGroup的onInterceptTouchEvent()方法在接收到down事件并处理完成后return false,则内部View也将会获取到down事件,后续的move,up事件也将继续先传递到该ViewGroup中,之后一样传递给最终的目标childView的onTouchEvent()处理。
- 如果该ViewGroup的onInterceptTouchEvent()接收down事件并处理完成后return true,则表明子View不需要接收接收事件,后续的move,up事件也不再传递给onInterceptTouchEvent(), 该事件接着交给ViewGroup自身的onTouchEvent()方法去执行。