So verhindern Sie, dass die Aktivität beim Drücken der Taste zweimal geladen wird


91

Ich versuche zu verhindern, dass die Aktivität zweimal geladen wird, wenn ich die Taste unmittelbar nach dem ersten Klick zweimal drücke.

Ich habe eine Aktivität, die beispielsweise auf Knopfdruck geladen wird

 myButton.setOnClickListener(new View.OnClickListener() {
      public void onClick(View view) {
       //Load another activity
    }
});

Da die zu ladende Aktivität Netzwerkanrufe enthält, dauert das Laden (MVC) etwas länger. Ich zeige zwar eine Ladeansicht dafür, aber wenn ich vorher zweimal auf die Schaltfläche drücke, kann ich sehen, dass die Aktivität zweimal geladen wird.

Weiß jemand, wie man das verhindert?


Sie können die Schaltfläche deaktivieren, nachdem Sie die Aktivität geöffnet haben ... und wenn die Aktivität beendet ist, aktivieren Sie sie erneut ... Sie können das Ende der zweiten Aktivität erkennen, indem Sie die Funktion onActivityResult aufrufen
Maneesh

Deaktivieren Sie die Schaltfläche beim ersten Klicken und aktivieren Sie sie später erst wieder, wenn Sie erneut auf die Schaltfläche klicken möchten.
JimmyB

Das Deaktivieren funktioniert nicht auf einfache Weise, wenn die nächste Anweisung für einen langen Prozess- oder Aktivitätsstart ist ... Um die Schaltfläche zu deaktivieren, müssen Sie einen separaten Thread erstellen ...
Awais Tariq

Wenn Sie dieselbe API zweimal treffen,
lesen Sie

Antworten:


68

Deaktivieren Sie im Ereignis-Listener der Schaltfläche die Schaltfläche und zeigen Sie eine andere Aktivität an.

    Button b = (Button) view;
    b.setEnabled(false);

    Intent i = new Intent(this, AnotherActitivty.class);
    startActivity(i);

Überschreiben onResume(), um die Schaltfläche wieder zu aktivieren.

@Override
    protected void onResume() {
        super.onResume();

        Button button1 = (Button) findViewById(R.id.button1);
        button1.setEnabled(true);
    }

1
Dies ist der richtige Ansatz. Es werden sogar ausgewählte Zustände für Schaltflächen für Sie (sofern Sie diese bereitstellen) und alle Material Design-Extras verarbeitet, die Sie von einem einfachen Standard-Widget erwarten. Ich kann nicht glauben, dass die Leute dafür Timer verwenden. Dann sehen Sie seltsame Bibliotheken, um solche Dinge zu erledigen ...
Martin Marconcini

156

Fügen Sie dies Ihrer ActivityDefinition in AndroidManifest.xml...

android:launchMode = "singleTop"

Beispielsweise:

<activity
            android:name=".MainActivity"
            android:theme="@style/AppTheme.NoActionBar"
            android:launchMode = "singleTop"/>

ok, ich denke du machst eine lange Verarbeitung, nachdem du neue Aktivitäten gestartet hast. Deshalb wird der Bildschirm schwarz. Wenn Sie diesen schwarzen Bildschirm vermeiden möchten, sollten Sie zu Beginn der Aktivität einen Fortschrittsdialog anzeigen und die lange Verarbeitung in einem separaten Thread durchführen (z. B. UI-Thread oder einfach asynchrone Klasse verwenden). Sobald Ihre Verarbeitung abgeschlossen ist, verbergen Sie diesen Dialog. Es ist die beste Lösung in meinem Wissen und ich habe es mehrmals verwendet ... :)
Awais Tariq

Ich habe den Dialog zu zeigen. Aber ja, ich habe eine Methode, die auf das Web in onCreate zeigt. Aber ist dies die einzige Lösung? Denn an dieser Stelle möchte ich ohne Threadwechsel und alles umgehen. Kennen Sie andere Möglichkeiten? Und ich habe die Schaltfläche in meinem Listenadapter und ich habe die Methode dafür in der XML deklariert, nicht programmgesteuert
tejas

2
was ist sonst noch möglich ??? Auf die eine oder andere Weise müssen Sie Threading implementieren, um eine reibungslos aussehende App zu erhalten ... Versuchen Sie es, Alter ..;) Fügen Sie einfach den gesamten aktuellen Code in eine Methode ein und rufen Sie diese Methode von einem separaten Thread an derselben Stelle auf, an der Sie geschrieben haben es früher ... Es wird kaum fünf bis sechs Codezeilen erhöhen ..
Awais Tariq

17
Dies verhindert, dass zwei Instanzen der Aktivität vorhanden sind, verhindert jedoch nicht, dass der Code zweimal falsch ausgeführt wird. Die akzeptierte Antwort ist besser, obwohl weniger Stimmen vorhanden sind.
Lilbyrdie

18
Dies ist falsch, da die Aktivität selbst bei verschiedenen Aufgaben niemals zweimal vorhanden ist. Der richtige Weg wäre android:launchMode = "singleTop", den Effekt zu erzielen, ohne das Android-Multitasking zu unterbrechen. In der Dokumentation heißt es, dass die meisten Apps diese singleInstanceOption nicht verwenden sollten .
Nohus

36

Sie können die Absichtsflags wie folgt verwenden.

Intent intent = new Intent(Class.class);    
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
activity.startActivity(intent);

Dadurch wird nur eine Aktivität oben im Verlaufsstapel geöffnet.


4
Diese Antwort, kombiniert mit der am besten bewerteten Antwort, scheint am besten zu funktionieren. Verwenden Sie dieses Flag im Manifest der Aktivität: Auf android:launchMode = "singleTop"diese Weise wird es gelöst, ohne dass das Flag zu jeder Absicht hinzugefügt werden muss.
Nohus

1
Dies ist nicht hilfreich, wenn Sie verschachtelte Aktivitäten benötigen, da Sie nicht zwei Aktivitäten vom gleichen Typ haben können.
Behnam Heydari

4
Dies funktioniert nicht im Fall von startActivityForResult
raj

26

Da SO es mir nicht erlaubt, andere Antworten zu kommentieren, muss ich diesen Thread mit einer neuen Antwort verschmutzen.

Häufige Antworten auf das Problem "Aktivität öffnet zweimal" und meine Erfahrungen mit diesen Lösungen (Android 7.1.1):

  1. Deaktivieren Sie die Schaltfläche, mit der die Aktivität gestartet wird: Funktioniert, fühlt sich jedoch etwas ungeschickt an. Wenn Sie mehrere Möglichkeiten haben, die Aktivität in Ihrer App zu starten (z. B. eine Schaltfläche in der Aktionsleiste UND durch Klicken auf ein Element in einer Listenansicht), müssen Sie den aktivierten / deaktivierten Status mehrerer GUI-Elemente verfolgen. Außerdem ist es nicht sehr praktisch, beispielsweise angeklickte Elemente in einer Listenansicht zu deaktivieren. Also kein sehr universeller Ansatz.
  2. launchMode = "singleInstance": Funktioniert nicht mit startActivityForResult (), unterbricht die Navigation mit startActivity (), was für reguläre Anwendungen in der Android-Manifestdokumentation nicht empfohlen wird.
  3. launchMode = "singleTask": Funktioniert nicht mit startActivityForResult (), wird von der Android-Manifestdokumentation nicht für reguläre Anwendungen empfohlen.
  4. FLAG_ACTIVITY_REORDER_TO_FRONT: Bricht die Zurück-Taste.
  5. FLAG_ACTIVITY_SINGLE_TOP: Funktioniert nicht, die Aktivität wird noch zweimal geöffnet.
  6. FLAG_ACTIVITY_CLEAR_TOP: Dies ist die einzige, die für mich arbeitet.

BEARBEITEN: Dies war zum Starten von Aktivitäten mit startActivity (). Bei Verwendung von startActivityForResult () muss sowohl FLAG_ACTIVITY_SINGLE_TOP als auch FLAG_ACTIVITY_CLEAR_TOP festgelegt werden.


FLAG_ACTIVITY_CLEAR_TOP: Dies ist die einzige, die für mich auf Android 7.1.1
Mingjiang Shi

1
Ich verwende "FLAG_ACTIVITY_REORDER_TO_FRONT" und es funktioniert einwandfrei und die Schaltfläche "Zurück" verhält sich ebenfalls normal. Was genau meintest du mit "Breaks Back Button"? Könnten Sie das klarstellen?
Mirmuhsin Sodiqov

Ich fand , dass „REORDER“ Flagge einen Fehler hatte ... und es war nicht Neuordnen in KitKat. Allerdings habe ich es in Lollipop und Pie überprüft, es funktioniert gut.
Mirmuhsin Sodiqov

7

Es hat nur bei mir funktioniert, wenn startActivity(intent)

intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);

1
@raj Haben Sie versucht, dies durch Hinzufügen android:launchMode = "singleInstance"in der Manifest-Datei Ihres Aktivitäts-Tags hinzuzufügen ?
Shylendra Madda

5

Verwenden Sie singleInstance, um zu vermeiden, dass Aktivitäten zweimal aufgerufen werden.

<activity
            android:name=".MainActivity"
            android:label="@string/activity"
            android:launchMode = "singleInstance" />

4

Nehmen wir an, @wannik ist richtig, aber wenn wir mehr als eine Schaltfläche haben, die denselben Aktionslistener aufruft, klicke ich fast gleichzeitig auf zwei Schaltflächen, bevor ich mit der nächsten Aktivität beginne ...

Es ist also gut, wenn Sie Feld private boolean mIsClicked = false;und im Hörer haben:

if(!mIsClicked)
{
    mIsClicked = true;
    Intent i = new Intent(this, AnotherActitivty.class);
    startActivity(i);
}

Und onResume()wir müssen den Staat zurückgeben:

@Override
protected void onResume() {
    super.onResume();

    mIsClicked = false;
}

Was ist der Unterschied zwischen meiner und @ wanniks Antwort?

Wenn Sie im Listener der aufrufenden Ansicht "enabled" auf "false" setzen, wird die andere Schaltfläche, die denselben Listener verwendet, weiterhin aktiviert. Um sicherzugehen, dass die Aktion des Listeners nicht zweimal aufgerufen wird, benötigen Sie etwas Globales, das alle Aufrufe des Listeners deaktiviert (egal, ob es sich um eine neue Instanz handelt oder nicht).

Was ist der Unterschied zwischen meiner Antwort und anderen?

Sie denken richtig, aber sie denken nicht an eine zukünftige Rückkehr zur gleichen Instanz der aufrufenden Aktivität :)


servoper, danke für deine recherchierung. Diese Frage wurde bereits gelöst, aber Ihre Antwort sieht auch für die von Ihnen erzählte Situation vielversprechend aus. Lassen Sie mich versuchen, mit dem Ergebnis zu kommen :)
Tejas

1
Ich habe dieses Problem in einem meiner Spiele. Ich habe Luftballons mit "Select Level", die denselben Listener haben und deren Ansichten sich nur durch Tags unterscheiden. Wenn ich also schnell zwei Ballons auswähle, werden zwei Aktivitäten gestartet. Ich weiß das, weil die neue Aktivität den Sound startet ... und in diesem Fall wird der Sound zweimal abgespielt ... aber Sie können ihn überprüfen, indem Sie zurück klicken, um zur vorherigen Aktivität zu gelangen
Sir NIkolay Cesar Der erste

1
Das reicht nicht aus. Sie müssen auch ein verwenden synchronized(mIsClicked) {...}, um 100% sicher zu sein.
Monstieur

@Monstieur Sie brauchen keinen synchronisierten Block, weil dies alles Haupt-Thread ist ...
Martin Marconcini

@MartinMarconcini Nur weil es in einer Android-Aktivität sicher ist, ist es kein guter Code. Wenn es sich um eine eigenständige Klasse handelt, muss sie als nicht threadsicher dokumentiert werden.
Monstieur

4

In dieser Situation werde ich mich für eine von zwei Annäherungen entscheiden, singleTaskin manifest.xml ODER für ein Flag im onResume()& der AktivitätonDestroy() Methoden Methoden wählen.

Für die erste Lösung: Ich bevorzuge die Verwendung singleTaskfür die Aktivität im Manifest anstelle der singleInstanceVerwendungsingleInstance herauszufinden, dass die Aktivität in einigen Fällen eine neue separate Instanz für sich selbst erstellt, was dazu führt, dass in den ausgeführten Apps zwei separate Anwendungsfenster vorhanden sind in bcakground und neben zusätzlichen Speicherzuweisungen, die zu einer sehr schlechten Benutzererfahrung führen würden, wenn der Benutzer die Apps-Ansicht öffnet, um eine App zum Fortsetzen auszuwählen. Der bessere Weg ist also, die Aktivität in der manifest.xml wie folgt zu definieren:

<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"</activity>

Sie können Aktivität einen Modus überprüfen hier .


Für die zweite Lösung müssen Sie lediglich eine statische Variable oder eine Präferenzvariable definieren, zum Beispiel:

public class MainActivity extends Activity{
    public static boolean isRunning = false;

    @Override
    public void onResume() {
        super.onResume();
        // now the activity is running
        isRunning = true;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // now the activity will be available again
        isRunning = false;
    }

}

und von der anderen Seite, wenn Sie diese Aktivität starten möchten, überprüfen Sie einfach:

private void launchMainActivity(){
    if(MainActivity.isRunning)
        return;
    Intent intent = new Intent(ThisActivity.this, MainActivity.class);
    startActivity(intent);
}

3

Ich denke, Sie werden das Problem falsch lösen. Im Allgemeinen ist es eine schlechte Idee für eine Aktivität, lang laufende Webanforderungen in einer ihrer Startlebenszyklusmethoden zu stellen ( onCreate(),onResume() usw.). Eigentlich sollten diese Methoden einfach verwendet werden, um Objekte zu instanziieren und zu initialisieren, die Ihre Aktivität verwenden wird, und sollten daher relativ schnell sein.

Wenn Sie eine Webanforderung ausführen müssen, führen Sie dies in einem Hintergrundthread Ihrer neu gestarteten Aktivität aus (und zeigen Sie den Ladedialog in der neuen Aktivität an). Sobald der Hintergrundanforderungsthread abgeschlossen ist, kann er die Aktivität aktualisieren und den Dialog ausblenden.

Dies bedeutet dann, dass Ihre neue Aktivität sofort gestartet werden sollte und der Doppelklick nicht möglich sein sollte.


3

Hoffe das hilft:

 protected static final int DELAY_TIME = 100;

// to prevent double click issue, disable button after click and enable it after 100ms
protected Handler mClickHandler = new Handler() {

    public void handleMessage(Message msg) {

        findViewById(msg.what).setClickable(true);
        super.handleMessage(msg);
    }
};

@Override
public void onClick(View v) {
    int id = v.getId();
    v.setClickable(false);
    mClickHandler.sendEmptyMessageDelayed(id, DELAY_TIME);
    // startActivity()
}`

2

Eine andere sehr sehr einfache Lösung, wenn Sie keine Verwendung wünschen, onActivityResult()ist das Deaktivieren der Schaltfläche für 2 Sekunden (oder die gewünschte Zeit), ist nicht ideal, kann aber teilweise das Problem in einigen Fällen lösen und der Code ist einfach:

   final Button btn = ...
   btn.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            //start activity here...
            btn.setEnabled(false);   //disable button

            //post a message to run in UI Thread after a delay in milliseconds
            btn.postDelayed(new Runnable() {
                public void run() {
                    btn.setEnabled(true);    //enable button again
                }
            },1000);    //1 second in this case...
        }
    });

1

Behalten Sie einfach ein Flag in der Schaltfläche onClick-Methode bei:

public boolean oneTimeLoadActivity = false;

    myButton.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
               if(!oneTimeLoadActivity){
                    //start your new activity.
                   oneTimeLoadActivity = true;
                    }
        }
    });

1

// Variable zur Verfolgung der Ereigniszeit

private long mLastClickTime = 0;

2.In onClick überprüfen Sie, ob, wenn die aktuelle Zeit und der Unterschied zwischen der letzten Klickzeit weniger als 1 Sekunde betragen, nichts unternommen wird (Rückkehr), sonst klicken Sie auf ein Klickereignis

 @Override
public void onClick(View v) {
    // Preventing multiple clicks, using threshold of 1 second
    if (SystemClock.elapsedRealtime() - mLastClickTime < 1000) {
        return;
          }
    mLastClickTime = SystemClock.elapsedRealtime();
            // Handle button clicks
            if (v == R.id.imageView2) {
        // Do ur stuff.
         }
            else if (v == R.id.imageView2) {
        // Do ur stuff.
         }
      }
 }

0

Wenn Sie onActivityResult verwenden, können Sie eine Variable zum Speichern des Status verwenden.

private Boolean activityOpenInProgress = false;

myButton.setOnClickListener(new View.OnClickListener() {
  public void onClick(View view) {
    if( activityOpenInProgress )
      return;

    activityOpenInProgress = true;
   //Load another activity with startActivityForResult with required request code
  }
});

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if( requestCode == thatYouSentToOpenActivity ){
    activityOpenInProgress = false;
  }
}

Funktioniert auch beim Drücken der Zurück-Taste, da der Anforderungscode bei einem Ereignis zurückgegeben wird.


0

Fügen Sie den Startmodus als einzelne Aufgabe im Manifest hinzu, um zu vermeiden, dass die Aktivität beim Klicken zweimal geöffnet wird

<activity
        android:name=".MainActivity"
        android:label="@string/activity"
        android:launchMode = "singleTask" />

-1
myButton.setOnClickListener(new View.OnClickListener() {
      public void onClick(View view) {
      myButton.setOnClickListener(null);
    }
});

Das würde wahrscheinlich nicht funktionieren, da Sie es als endgültig deklarieren müssten.
König

-1

Verwenden Sie eine flagVariable, setzen Sie sie to true. Überprüfen Sie , ob sie wahr ist return, und führen Sie einen Aktivitätsaufruf aus.

Sie können auch setClickable (false) verwenden, um den Aktivitätsaufruf auszuführen

flg=false
 public void onClick(View view) { 
       if(flg==true)
         return;
       else
       { flg=true;
        // perform click}
    } 

perform click; wait; flg = false;denn wenn wir zurückkommen
Xeno Lupus

-1

Sie können einfach startActivityForResult überschreiben und die Instanzvariable verwenden:

boolean couldStartActivity = false;

@Override
protected void onResume() {
    super.onResume();

    couldStartActivity = true;
}

@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
    if (couldStartActivity) {
        couldStartActivity = false;
        intent.putExtra(RequestCodeKey, requestCode);
        super.startActivityForResult(intent, requestCode, options);
    }
}

-4

Sie können dies auch versuchen

Button game = (Button) findViewById(R.id.games);
        game.setOnClickListener(new View.OnClickListener() 
        {
            public void onClick(View view) 
            {
                Intent myIntent = new Intent(view.getContext(), Games.class);
                startActivityForResult(myIntent, 0);
            }

        });
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.