次の操作を行った後、ボタンが強調表示された状態のままになる問題があります。
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
誰かもこれを確認できますか?
このようなカスタマイズには、プログラムによる変更は必要ありません。 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フロー)
Touchイベントがターゲットビューに到達すると、ビューはイベントを処理し、それを前のレイアウト/アクティビティに渡すかどうかを決定できます(false
メソッドでtrue
をonTouch
で返す)。 (上の画像のRTLフロー)
View のソースコードを見て、タッチイベントフローをより深く理解しましょう。 dispatchTouchEvent
の実装を見ると、OnTouchListener
をビューに設定して、true
をonTouch
メソッドで返すと、 onTouchEvent
ofビューは呼び出されません。
_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;
}
_
次に、イベントアクションが_MotionEvent.ACTION_UP
_である onTouchEvent
メソッドを見てください。ここでは、クリック実行アクションが発生していることがわかります。したがって、true
のOnTouchListener
でonTouch
を返し、その結果、onTouchEvent
を呼び出さないと、OnClickListener
のonClick
が呼び出されなくなります。
onTouchEvent
を呼び出さないことには、別の問題があります。これは、押された状態に関連していて、質問で述べました。以下のコードブロックでわかるように、実行時に UnsetPressedState
_(false)
_を呼び出す setPressed
のインスタンスがあります。 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);
}
}
_
上記の説明については、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;
}
});
_
touch
およびfocus
イベントをめちゃくちゃにしています。同じ色で行動を理解することから始めましょう。デフォルトでは、AndroidではSelector
の背景として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を返します)。色の変更を続行できず、同じ色でフリーズしています。そして、それが2番目のバリアント(インターセプトなし)が正常に機能している理由です。
[〜#〜]更新[〜#〜]
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>
ボタンビューの代わりにマテリアルチップを使用できます。参照: https://material.io/develop/Android/components/chip そこで、これらの強化されたイベントを処理し、テーマを適用してカスタマイズできます。
ボタンに背景を割り当てても、クリックしても色は変わりません。
<color name="myColor">#000000</color>
ボタンの背景に設定します
Android:background="@color/myColor"