执行以下操作后,我的按钮保持在突出显示状态时出现问题:
public class MainActivity extends AppCompatActivity {
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatButton button = (AppCompatButton) findViewById(R.id.mybutton);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("Test", "calling onClick");
}
});
button.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP);
v.invalidate();
break;
}
case MotionEvent.ACTION_UP: {
v.getBackground().clearColorFilter();
v.invalidate();
v.performClick();
Log.d("Test", "Performing click");
return true;
}
}
return false;
}
});
}
}
关于上面的代码,在使用它时,我希望按钮单击由触摸来处理,并且通过返回“ true”,处理应在touchListener处停止。
但这种情况并非如此。即使正在调用单击,按钮仍保持突出显示状态。
我得到的是:
Test - calling onClick Test - Performing click
另一方面,如果我使用以下代码,则单击该按钮,并打印相同的内容,但该按钮最终不会停留在突出显示状态:
Test - calling onClick Test - Performing click
我对触摸事件的响应者链有些困惑。我的猜测是:
1)TouchListener
2)ClickListener
3)父视图
有人也可以确认吗?
这样的自定义不需要程序上的修改。您可以简单地在xml
文件中进行操作。首先,完全删除setOnTouchListener
您提供的方法onCreate
。接下来,在res/color
目录中定义选择器颜色,如下所示。(如果目录不存在,请创建它)
分辨率/颜色/button_tint_color.xml
现在,将其设置为按钮的app:backgroundTint
属性:
从总体上看,触摸事件的流程从开始Activity
,然后向下到布局(从父布局到子布局),然后到视图。(下图为LTR流程)
当触摸事件到达目标视图,该视图可以处理随后事件决定将它传递给现有的布局/活性或不(返回false
的true
在onTouch
法)。(上图中的RTL流程)
现在,让我们看一下View的源代码,以更深入地了解触摸事件流。通过查看的实现dispatchTouchEvent
,我们将看到,如果将设置OnTouchListener
为视图,然后返回true
其onTouch
方法,onTouchEvent
则不会调用该视图的。
public boolean dispatchTouchEvent(MotionEvent event) { // removed lines for conciseness... boolean result = false; // removed lines for conciseness... if (onFilterTouchEventForSecurity(event)) { // removed lines for conciseness... ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { // <== right here! result = true; } if (!result && onTouchEvent(event)) { result = true; } } // removed lines for conciseness... return result; }
现在,查看onTouchEvent
事件操作为的方法MotionEvent.ACTION_UP
。我们看到执行点击操作就在那里发生。所以,回到true
在OnTouchListener
的onTouch
,因此没有要求的onTouchEvent
,将导致不调用OnClickListener
的onClick
。
还有一个不调用的问题onTouchEvent
,它与按下状态和您在问题中提到的有关。正如我们在下面的代码块中看到的那样,它在运行时有一个UnsetPressedState
调用实例。不调用的结果是视图卡在了按下状态,并且其可绘制状态不变。 setPressed
(false)
setPressed(false)
public boolean onTouchEvent(MotionEvent event) { // removed lines for conciseness... if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) { switch (action) { case MotionEvent.ACTION_UP: // removed lines for conciseness... if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { // removed lines for conciseness... if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) { // removed lines for conciseness... if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClickInternal(); } } } if (mUnsetPressedState == null) { mUnsetPressedState = new UnsetPressedState(); } if (prepressed) { postDelayed(mUnsetPressedState, ViewConfiguration.getPressedStateDuration()); } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } // removed lines for conciseness... } // removed lines for conciseness... break; // removed lines for conciseness... } return true; } return false; }
UnsetPressedState:
private final class UnsetPressedState implements Runnable { @Override public void run() { setPressed(false); } }
关于以上描述,您可以通过调用setPressed(false)
自己来更改事件动作为的可绘制状态来更改代码MotionEvent.ACTION_UP
:
button.setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { v.getBackground().setColorFilter(0xe0f47521,PorterDuff.Mode.SRC_ATOP); v.invalidate(); break; } case MotionEvent.ACTION_UP: { v.getBackground().clearColorFilter(); // v.invalidate(); v.setPressed(false); v.performClick(); Log.d("Test", "Performing click"); return true; } } return false; } });