执行以下操作后,我的Button保持在突出显示状态时出现问题:
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
另一方面,如果我使用以下代码,则单击该按钮,并打印相同的内容,但该按钮最终不会停留在突出显示状态:
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 false;
}
}
return false;
}
});
}
}
对于触摸事件的响应者链是什么,我有点困惑。我的猜测是:
1)TouchListener
2)ClickListener
3)ParentViews
有人也可以确认吗?
答案 0 :(得分:10)
此类自定义无需进行程序修改。您可以简单地在xml
文件中进行操作。首先,完全删除您在setOnTouchListener
中提供的onCreate
方法。接下来,在res/color
目录中定义选择器颜色,如下所示。 (如果目录不存在,请创建它)
res / color / button_tint_color.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#e0f47521" android:state_pressed="true" />
<item android:color="?attr/colorButtonNormal" android:state_pressed="false" />
</selector>
现在,将其设置为按钮的app:backgroundTint
属性:
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:backgroundTint="@color/button_tint_color" />
从总体上看,触摸事件的流程从Activity
开始,然后流到布局(从父布局到子布局),再到视图。 (下图为LTR流程)
当触摸事件到达目标视图时,该视图可以处理该事件,然后决定是否将其传递到先前的布局/活动(返回false
中true
的{{1}}方法)。 (上图中的RTL流程)
现在,让我们看一下View的源代码,以更深入地了解触摸事件流。通过查看dispatchTouchEvent
的实现,我们可以看到,如果您为视图设置了onTouch
,然后在其OnTouchListener
方法中返回了true
,该视图的onTouchEvent
将不会被调用。
onTouch
现在,请查看事件操作为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;
}
的{{3}}方法。我们看到执行点击操作就在那里发生。因此,在MotionEvent.ACTION_UP
的{{1}}中返回true
并因此不调用OnTouchListener
,导致不调用onTouch
的{{1}}。
还有一个不调用onTouchEvent
的问题,这与按下状态和您在问题中提到的有关。正如我们在下面的代码块中看到的,有一个onTouchEvent
实例在运行时调用UnsetPressedState
OnClickListener
。不调用onClick
的结果是视图卡在了按下状态,其可绘制状态不变。
onTouchEvent
(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;
}
的可绘制状态:
private final class UnsetPressedState implements Runnable {
@Override
public void run() {
setPressed(false);
}
}
答案 1 :(得分:2)
您正在弄乱touch
和focus
事件。让我们从了解相同颜色的行为开始。默认情况下,将Selector
作为背景分配给Android中的Button
。因此,只需更改背景颜色,make就是静态的(颜色不会改变)。但这不是本机行为。
Selector
可能看起来像这样。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_focused="true"
android:state_pressed="true"
android:drawable="@drawable/bgalt" />
<item
android:state_focused="false"
android:state_pressed="true"
android:drawable="@drawable/bgalt" />
<item android:drawable="@drawable/bgnorm" />
</selector>
如您在上面看到的,有状态focused
和状态pressed
。通过设置onTouchListener
,您将处理与focus
无关的触摸事件。
Selector
应将focus
事件替换为touch
。但是在代码的第一部分中,您截获了touch
的事件(从回调返回true)。颜色更改无法继续进行,并冻结相同的颜色。这就是为什么第二个变体(无拦截)运行良好的原因,这就是您的困惑。
更新
您要做的就是更改Selector
的行为和颜色。对于前。通过为Button
使用下一个背景。 AND 完全从您的实施中删除onTouchListener
。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@color/color_pressed" />
<item android:drawable="@color/color_normal" />
</selector>
答案 2 :(得分:0)
如果为按钮分配背景,则单击时不会改变颜色。
<color name="myColor">#000000</color>
并将其设置为按钮的背景
android:background="@color/myColor"
答案 3 :(得分:0)
您可以仅使用材料芯片代替按钮视图。 参考:https://material.io/develop/android/components/chip 在那里,他们可以处理那些隐蔽的事件,您可以通过应用主题进行自定义。