Come impedire che l'attività venga caricata due volte premendo il pulsante


93

Sto cercando di impedire il caricamento dell'attività due volte se premo il pulsante due volte immediatamente dopo il primo clic.

Ho un'attività che si carica al clic di un pulsante, ad esempio

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

Ora, poiché l'attività da caricare ha chiamate di rete, il caricamento richiede un po 'di tempo (MVC). Mostra una visualizzazione di caricamento per questo, ma se premo il pulsante due volte prima, posso vedere l'attività caricata due volte.

Qualcuno sa come impedirlo?


È possibile disabilitare il pulsante dopo aver aperto l'attività ... e quando l'attività finisce, riattivarlo ... puoi rilevare la fine della seconda attività chiamando la funzione ActivityResult
Maneesh

Disabilita il pulsante quando viene cliccato per la prima volta e riabilitalo in seguito solo quando desideri che il pulsante venga cliccato di nuovo.
JimmyB

la disabilitazione non funziona in modo semplice se l'istruzione successiva riguarda un processo lungo o l'avvio di attività ... Per disabilitare il pulsante è necessario creare un thread separato ...
Awais Tariq

Se colpisci due volte la stessa API, fai
Shylendra Madda

Possibile duplicato del pulsante Evita più clic rapidi
Arnab Kar

Risposte:


69

Nel listener di eventi del pulsante, disabilita il pulsante e mostra un'altra attività.

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

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

Sostituisci onResume()per riattivare il pulsante.

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

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

1
Questo è l'approccio corretto. Gestirà anche gli stati selezionati del pulsante per te (se li fornisci) e tutte le "chicche" di Material Design che ti aspetteresti da un semplice widget standard. Non posso credere che le persone usino i timer per questo. Poi inizi a vedere strane biblioteche per gestire cose come queste ...
Martin Marconcini

157

Aggiungi questo alla tua Activitydefinizione in AndroidManifest.xml...

android:launchMode = "singleTop"

Per esempio:

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

ok immagino che tu stia facendo un lungo processo dopo aver iniziato una nuova attività .. Ecco perché lo schermo diventa nero. Ora, se vuoi evitare quella schermata nera, dovresti mostrare una finestra di dialogo di avanzamento all'inizio dell'attività ed eseguire la lunga elaborazione in un thread separato (es. Thread dell'interfaccia utente o semplicemente usa la classe asincrona). Una volta completata l'elaborazione, nascondi quella finestra di dialogo. È la soluzione migliore che io sappia e l'ho usata più volte ... :)
Awais Tariq

Ho la finestra di dialogo da mostrare. Ma sì, ho un metodo che punta al web in onCreate. Ma questa è l'unica soluzione? Perché a questo punto, voglio gestire senza cambiare filo e tutto. Quindi conosci altri possibili modi. E ho il pulsante nel mio adattatore di elenco e ho dichiarato il metodo per esso in xml, non a livello di programmazione
tejas

2
cos'altro è possibile ??? In un modo o nell'altro devi implementare il threading per ottenere un'app dall'aspetto fluido ... Provalo amico ..;) Metti tutto il codice corrente in un metodo e chiama quel metodo da un thread separato nello stesso punto in cui hai scritto prima ... Difficilmente aumenterà da cinque a sei righe di codice ..
Awais Tariq

19
Ciò impedisce l'esistenza di due istanze dell'attività, ma non impedisce l'esecuzione del codice due volte, in modo non corretto. La risposta accettata è migliore, nonostante abbia meno voti positivi.
lilbyrdie

18
Questo è sbagliato, fa sì che l'attività non esista mai due volte, anche in compiti diversi. Il modo corretto sarebbe android:launchMode = "singleTop", che raggiunge l'effetto senza interrompere il multitasking di Android. La documentazione afferma che la maggior parte delle app non dovrebbe utilizzare l' singleInstanceopzione.
Nohus

37

Puoi usare i flag di intenti in questo modo.

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

Verrà aperta una sola attività in cima allo stack della cronologia.


4
Questa risposta, combinata con la risposta più votata, sembra funzionare meglio. Usa questo flag nel manifest dell'attività:, in android:launchMode = "singleTop"questo modo viene risolto senza dover aggiungere il flag a ogni Intent.
Nohus

1
questo non è utile quando sono necessarie attività nidificate, perché non è possibile avere due attività dello stesso tipo.
Behnam Heydari

4
Questo non funziona nel caso di startActivityForResult
raj

27

Poiché SO non mi consente di commentare altre risposte, devo inquinare questo thread con una nuova risposta.

Risposte comuni per il problema "l'attività si apre due volte" e le mie esperienze con queste soluzioni (Android 7.1.1):

  1. Pulsante Disabilita che avvia l'attività: funziona ma sembra un po 'goffo. Se hai diversi modi per avviare l'attività nella tua app (ad esempio un pulsante nella barra delle azioni E facendo clic su un elemento in una visualizzazione elenco), devi tenere traccia dello stato abilitato / disabilitato di più elementi della GUI. Inoltre, non è molto comodo disabilitare gli elementi su cui si è fatto clic in una visualizzazione elenco, ad esempio. Quindi, non un approccio molto universale.
  2. launchMode = "singleInstance": non funziona con startActivityForResult (), interrompe la navigazione con startActivity (), non consigliato per le normali applicazioni dalla documentazione di Android manifest.
  3. launchMode = "singleTask": Non funziona con startActivityForResult (), non consigliato per le normali applicazioni dalla documentazione di Android manifest.
  4. FLAG_ACTIVITY_REORDER_TO_FRONT: pulsante Indietro.
  5. FLAG_ACTIVITY_SINGLE_TOP: non funziona, l'attività è ancora aperta due volte.
  6. FLAG_ACTIVITY_CLEAR_TOP: Questo è l'unico che funziona per me.

EDIT: questo era per iniziare le attività con startActivity (). Quando si utilizza startActivityForResult (), è necessario impostare sia FLAG_ACTIVITY_SINGLE_TOP che FLAG_ACTIVITY_CLEAR_TOP.


FLAG_ACTIVITY_CLEAR_TOP: Questo è l'unico che funziona per me su Android 7.1.1
Mingjiang Shi

1
Sto usando "FLAG_ACTIVITY_REORDER_TO_FRONT" e funziona perfettamente e anche il pulsante Indietro funziona normalmente. Che cosa intendevi esattamente con "pulsante di interruzione"? Potresti chiarire su questo?
Mirmuhsin Sodiqov

Ho scoperto che il flag "REORDER" aveva un bug ... e non veniva riordinato in KitKat. Tuttavia, l'ho controllato in Lollipop and Pie, funziona bene.
Mirmuhsin Sodiqov

7

Funzionava solo per me quando startActivity(intent)

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

1
@raj hai provato aggiungendo questo android:launchMode = "singleInstance"nel file manifest del tuo tag attività?
Shylendra Madda

5

Usa singleInstance per evitare attività da invocare due volte.

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

4

Diciamo che @wannik ha ragione, ma se abbiamo più di 1 pulsante che chiama lo stesso ascoltatore di azioni e faccio clic su due pulsanti una volta quasi alla stessa volta prima di iniziare l'attività successiva ...

Quindi è bene se hai campo private boolean mIsClicked = false;e nell'ascoltatore:

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

E onResume()dobbiamo restituire lo stato:

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

    mIsClicked = false;
}

Qual è la differenza tra la mia risposta e quella di @ wannik?

Se si imposta abilitato su falso nel listener della sua chiamata, verrà comunque abilitato un altro pulsante che utilizza lo stesso listener. Quindi, per essere sicuri che l'azione dell'ascoltatore non venga chiamata due volte, devi avere qualcosa di globale che disabiliti tutte le chiamate dell'ascoltatore (non importa se è una nuova istanza o no)

Qual è la differenza tra la mia risposta e le altre?

Stanno pensando nel modo giusto ma non stanno pensando a un futuro ritorno allo stesso esempio dell'attività chiamante :)


servoper, grazie per la tua ricerca. Questa domanda è già stata risolta, tuttavia anche la tua risposta sembra promettente per la situazione che hai detto. Fammi provare e venire con il risultato :)
tejas

1
Ho questo problema in uno dei miei giochi. Ho baloon di "livello di selezione" che hanno lo stesso ascoltatore e le visualizzazioni sono diverse solo per tag. Quindi se scelgo velocemente due baloon, iniziano due attività. Lo so perché la nuova attività inizia il suono .. e in questo caso il suono viene riprodotto due volte ... ma puoi verificarlo facendo clic su Indietro che ti porterà all'attività precedente
Sir NIkolay Cesar The First

1
Questo non è sufficiente. Devi usare anche un synchronized(mIsClicked) {...}per essere sicuro al 100%.
Monstieur

@Monstieur non hai bisogno di un blocco sincronizzato perché questo è tutto il thread principale ...
Martin Marconcini

@MartinMarconcini Solo perché capita di essere al sicuro in un'attività Android non lo rende un buon codice. Se fosse una classe autonoma, dovrebbe essere documentata come non thread-safe.
Monstieur

4

Per questa situazione, sceglierò uno dei due avvicinati, singleTaskin manifest.xml OPPURE un flag in Activity onResume()&onDestroy() rispettivamente nei metodi .

Per la prima soluzione: preferisco utilizzare singleTaskper l'attività nel manifest piuttosto che singleInstance, come da utilizzo, singleInstanceho capito che in alcune occasioni l'attività crea una nuova istanza separata per se stessa che risulta avere una finestra di due applicazioni separate nelle app in esecuzione in bcakground e oltre ad allocazioni di memoria extra che risulterebbero una pessima esperienza utente quando l'utente apre la vista app per scegliere alcune app da riprendere. Quindi, il modo migliore è definire l'attività nel manifest.xml come segue:

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

puoi controllare le modalità di avvio delle attività qui .


Per la seconda soluzione, devi solo definire una variabile statica o una variabile di preferenza, ad esempio:

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;
    }

}

e dall'altro lato quando vuoi avviare questa attività, controlla:

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

3

Penso che tu stia risolvendo il problema nel modo sbagliato. In generale si tratta di una cattiva idea per un'attività per essere fare richieste web a lungo in esecuzione in uno qualsiasi dei suoi metodi di avvio del ciclo di vita ( onCreate(), onResume(), ecc). In realtà questi metodi dovrebbero essere usati semplicemente per istanziare e inizializzare gli oggetti che la tua attività userà e dovrebbero quindi essere relativamente veloci.

Se devi eseguire una richiesta web, fallo in un thread in background dalla tua attività appena avviata (e mostra la finestra di dialogo di caricamento nella nuova attività). Una volta completato il thread di richiesta in background, è possibile aggiornare l'attività e nascondere la finestra di dialogo.

Ciò significa che la tua nuova attività dovrebbe essere avviata immediatamente e impedire che il doppio clic sia possibile.


3

Spero che questo ti aiuti:

 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

Un'altra soluzione molto molto semplice se non vuoi usare onActivityResult()è disabilitare il pulsante per 2 secondi (o per il tempo che vuoi), non è l'ideale, ma può risolvere in parte il problema in alcuni casi e il codice è semplice:

   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...
        }
    });

2

// variabile per tenere traccia del tempo dell'evento

private long mLastClickTime = 0;

2.In onClick controlla che se l'ora corrente e la differenza di orario dell'ultimo clic sono inferiori a 1 secondo, non fare nulla (ritorno) altrimenti vai per l'evento clic

 @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.
         }
      }
 }

1

Mantieni solo una bandiera nel pulsante sul metodo Fare clic come:

public boolean oneTimeLoadActivity = false;

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

0

Se stai usando onActivityResult, puoi usare una variabile per salvare lo stato.

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;
  }
}

Funziona anche con il pulsante Indietro premuto perché il codice di richiesta viene restituito all'evento.


0

aggiungere la modalità di avvio come singola attività nel manifest per evitare che l'attività si apra due volte al clic

<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);
    }
});

Probabilmente non funzionerebbe poiché dovresti dichiararlo come finale.
Re

-1

Usa una flagvariabile per impostarlo to true, controlla se è vero, returnaltrimenti esegui la chiamata di attività.

Puoi anche usare setClickable (false) per eseguire la chiamata di attività

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

perform click; wait; flg = false;per quando torneremo
Xeno Lupus

-1

Potresti semplicemente sovrascrivere startActivityForResult e utilizzare la variabile di istanza:

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

Puoi provare anche questo

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);
            }

        });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.