Ich habe ein Problem damit, dass mein Button in einem hervorgehobenen Zustand bleibt, nachdem ich Folgendes getan habe:
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;
}
});
}
}
In Bezug auf den obigen Code erwarte ich bei der Verwendung, dass der Klick auf die Schaltfläche von der Berührung verarbeitet wird, und durch die Rückgabe von "true" sollte die Behandlung beim touchListener beendet werden.
Dies ist jedoch nicht der Fall. Die Schaltfläche bleibt in einem hervorgehobenen Zustand, obwohl der Klick aufgerufen wird.
Was ich bekomme ist:
Test - calling onClick
Test - Performing click
Wenn ich dagegen den folgenden Code verwende, wird auf die Schaltfläche geklickt, es werden dieselben Ausdrucke gedruckt, aber die Schaltfläche bleibt nicht in einem hervorgehobenen Zustand hängen:
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;
}
});
}
}
Ich bin etwas verwirrt darüber, was die Antwortkette für das Touch-Ereignis ist. Ich vermute, dass es ist:
1) TouchListener
2) ClickListener
3) ParentViews
Kann das auch jemand bestätigen?
quelle
Antworten:
Solche Anpassungen erfordern keine programmgesteuerten Änderungen. Sie können es einfach in
xml
Dateien tun . Löschen Sie zunächst die vonsetOnTouchListener
Ihnen angegebene MethodeonCreate
vollständig. Definieren Sie als Nächstes eine Auswahlfarbe imres/color
Verzeichnis wie folgt. (Wenn das Verzeichnis nicht existiert, erstellen Sie es)res / color / button_tint_color.xml
Stellen Sie es nun auf das
app:backgroundTint
Attribut der Schaltfläche ein :Visuelles Ergebnis:
BEARBEITET: (um das Problem mit Berührungsereignissen zu beheben)
Insgesamt betrachtet beginnt der Ablauf des Berührungsereignisses mit dem
Activity
, fließt dann zum Layout (vom übergeordneten zu den untergeordneten Layouts) und dann zu den Ansichten. (LTR-Fluss im folgenden Bild)Wenn das Berührungsereignis die Zielansicht erreicht, kann die Ansicht das Ereignis verarbeiten und dann entscheiden, ob es an die vorherigen Layouts / Aktivitäten übergeben werden soll oder nicht (Rückgabe
false
dertrue
In-onTouch
Methode). (RTL-Fluss im obigen Bild)Schauen wir uns nun den Quellcode der Ansicht an, um einen tieferen Einblick in die Berührungsereignisflüsse zu erhalten.
dispatchTouchEvent
Wenn Sie sich die Implementierung von ansehen, werden Sie feststellen, dass die Ansicht nicht aufgerufen wird , wenn Sie eineOnTouchListener
für die Ansicht festlegen und danntrue
in ihreronTouch
Methode zurückkehrenonTouchEvent
.Schauen Sie sich nun die
onTouchEvent
Methode an , bei der sich die Ereignisaktion befindetMotionEvent.ACTION_UP
. Wir sehen, dass dort eine Perform-Click-Aktion stattfindet. Wenn Sie alsotrue
in dieOnTouchListener
's zurückkehrenonTouch
und folglich die ' s nicht aufrufenonTouchEvent
, rufen Sie dieOnClickListener
's nicht aufonClick
.Es gibt ein weiteres Problem beim Nichtaufrufen von
onTouchEvent
, das mit dem Druckzustand zusammenhängt und den Sie in der Frage erwähnt haben. Wie wir im folgenden Codeblock sehen können, gibt es eine InstanzUnsetPressedState
dieser Aufrufe, wenn sie ausgeführt werden. Wenn Sie nicht aufrufen, bleibt die Ansicht im gedrückten Zustand hängen und ihr zeichnbarer Zustand ändert sich nicht.setPressed
(false)
setPressed(false)
UnsetPressedState :
In Bezug auf die obigen Beschreibungen können Sie den Code ändern, indem Sie sich
setPressed(false)
selbst aufrufen , um den Zeichenstatus zu ändern, in dem sich die Ereignisaktion befindetMotionEvent.ACTION_UP
:quelle
Now, look at the onTouchEvent method where the event action is MotionEvent.ACTION_UP. We see that perform-click action happens there. So, returning true in the OnTouchListener's onTouch and consequently not calling the onTouchEvent, causes not calling the OnClickListener's onClick.
Hinweise zum Ändern: In meinem Fall wird der onClick aufgerufen. mUnsetPressedState prüft, ob es null ist, bevor es auf false gesetzt wird, und auch die ausführbare Datei kann nicht ausgeführt werden, wenn wir unter Druck stehen. Ich verstehe nicht ganz, wie Sie daraus schließen, dass es auf false gesetzt werden sollteonClick
wird aufgerufen, weil Sie anrufenv.performClick();
. Bitte überprüfen Sie den obigen Code imMotionEvent.ACTION_UP
Abschnitt noch einmal,setPressed(false)
wird trotzdem aufgerufen, obmUnsetPressedState
null ist oder nicht, obprepressed
wahr ist oder nicht. Der Unterschied liegt in der Art des AnrufssetPressed(false)
, der überpost
/postDelayed
oder direkt erfolgen kann.Sie spielen herum
touch
undfocus
Ereignisse. Beginnen wir mit dem Verständnis des Verhaltens mit derselben Farbe. Standardmäßig wirdSelector
demButton
in Android als Hintergrund zugewiesen . Wenn Sie also einfach die Hintergrundfarbe ändern, ist make statisch (die Farbe ändert sich nicht). Aber es ist kein natives Verhalten.Selector
könnte so aussehen.Wie Sie oben sehen können, gibt es Zustand
focused
und Zustandpressed
. Durch die EinstellungonTouchListener
werden Berührungsereignisse behandelt, die nichts damit zu tun habenfocus
.Selector
of the Button sollte dasfocus
Ereignistouch
während des Klickereignisses auf der Schaltfläche ersetzen . Aber im ersten Teil Ihres Codes haben Sie Ereignisse für die abgefangentouch
(Rückgabe true vom Rückruf). Der Farbwechsel kann nicht weiter fortgesetzt werden und friert mit derselben Farbe ein. Und deshalb funktioniert die zweite Variante (ohne Abfangen) einwandfrei, und das ist Ihre Verwirrung.AKTUALISIEREN
Alles, was Sie tun müssen, ist, Verhalten und Farbe für die zu ändern
Selector
. Zum Beispiel. durch Verwendung des nächsten Hintergrunds für dieButton
. UND entfernen Sie überhauptonTouchListener
aus Ihrer Implementierung.quelle
onTouchListener
s verwenden, wie Sie möchten. Sie müssen einfach kein Ereignis verbrauchenreturn true
.backgroundColor
.Wenn Sie der Schaltfläche einen Hintergrund zuweisen, ändert sich die Farbe beim Klicken nicht.
und setzen Sie es als Hintergrund für Ihre Schaltfläche
quelle
Sie können nur Materialchips für anstelle der Schaltflächenansicht verwenden. Weitere Informationen finden Sie unter : https://material.io/develop/android/components/chip. Dort werden diese hilfreichen Ereignisse behandelt, und Sie können sie durch Anwenden der Themen anpassen.
quelle