web-dev-qa-db-fra.com

Comment empêcher l'activité de se charger deux fois en appuyant sur le bouton

J'essaie d'empêcher le chargement de l'activité deux fois si j'appuie deux fois instantanément sur le bouton après le premier clic.

J'ai une activité qui se charge en cliquant sur un bouton, disons

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

Maintenant, comme l'activité à charger a des appels réseau, le chargement prend un peu de temps (MVC). Je montre une vue de chargement pour cela, mais si j'appuie deux fois sur le bouton, l'activité est chargée deux fois. 

Est-ce que quelqu'un sait comment empêcher cela? 

59
tejas

Dans l'écouteur d'événements du bouton, désactivez le bouton et affichez une autre activité.

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

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

Remplacez onResume() pour réactiver le bouton.

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

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

Ajoutez ceci à votre définition Activity dans Androidmanifest.xml...

Android:launchMode = "singleTop"
119
Awais Tariq

Vous pouvez utiliser les indicateurs d'intention comme celui-ci.

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

Une seule activité sera ouverte en haut de la pile d’historique.

30
Carlos EduardoL

Puisque SO ne me permet pas de commenter d'autres réponses, je dois polluer ce fil avec une nouvelle réponse.

Réponses communes au problème "l'activité s'ouvre deux fois" et mes expériences avec ces solutions (Android 7.1.1):

  1. Bouton Désactiver qui démarre l'activité: Fonctionne mais se sent un peu maladroit. Si vous avez plusieurs façons de démarrer l'activité dans votre application (par exemple, un bouton dans la barre d'actions ET en cliquant sur un élément dans une vue de liste), vous devez suivre l'état activé/désactivé de plusieurs éléments de l'interface graphique. De plus, il n'est pas très pratique de désactiver les éléments cliqués dans une liste, par exemple. Donc, pas une approche très universelle.
  2. launchMode = "singleInstance": Ne fonctionne pas avec startActivityForResult (), interrompt la navigation avec startActivity (), déconseillé pour les applications classiques de la documentation du manifeste Android.
  3. launchMode = "singleTask": Ne fonctionne pas avec startActivityForResult (), déconseillé pour les applications standard de la documentation du manifeste Android.
  4. FLAG_ACTIVITY_REORDER_TO_FRONT: bouton Remplacement.
  5. FLAG_ACTIVITY_SINGLE_TOP: Ne fonctionne pas, l'activité est toujours ouverte deux fois.
  6. FLAG_ACTIVITY_CLEAR_TOP: C’est le seul qui fonctionne pour moi.

EDIT: C'était pour démarrer des activités avec startActivity (). Lorsque j'utilise startActivityForResult (), je dois définir à la fois FLAG_ACTIVITY_SINGLE_TOP et FLAG_ACTIVITY_CLEAR_TOP.

11
Andy Roid

Disons que @wannik a raison, mais si nous avons plus d'un bouton appelant le même écouteur d'action et si je clique sur deux boutons une fois presque en même temps avant de commencer l'activité suivante ...

Donc c'est bien si vous avez le champ private boolean mIsClicked = false; et dans le listener:

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

Et onResume() nous devons retourner l'état:

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

    mIsClicked = false;
}

Quelle est la différence entre ma réponse et @ Wannik?

Si vous définissez enabled à false dans l'écouteur de la vue en appelant, un autre bouton utilisant le même écouteur sera toujours activé. Donc, pour être sûr que l'action de l'auditeur n'est pas appelée deux fois, vous devez avoir quelque chose de global qui désactive tous les appels de l'écouteur (ne importe pas si c'est nouvelle instance ou non)

Quelle est la différence entre ma réponse et les autres?

Ils pensent de la bonne manière mais ne pensent pas à un retour futur à la même instance de l'activité d'appel :)

Utilisez singleInstance pour éviter toute activité à invoquer deux fois. 

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

J'espère que cela t'aides: 

 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()
}`
3
thanhbinh84

Cela ne fonctionnait que pour moi quand startActivity(intent)

intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
3
Shylendra Madda

Je pense que vous allez résoudre le problème de la mauvaise façon. En règle générale, il est déconseillé à une activité de faire de longues requêtes Web dans l'une de ses méthodes de cycle de vie au démarrage (onCreate(), onResume(), etc.). En réalité, ces méthodes devraient simplement être utilisées pour instancier et initialiser les objets que votre activité utilisera et devraient donc être relativement rapides. 

Si vous devez exécuter une demande Web, faites-le dans un fil d’arrière-plan de votre activité nouvellement lancée (et affichez la boîte de dialogue de chargement dans la nouvelle activité). Une fois que le thread de demande en arrière-plan est terminé, il peut mettre à jour l'activité et masquer la boîte de dialogue. 

Cela signifie alors que votre nouvelle activité doit être lancée immédiatement et empêcher le double-clic d'être possible. 

3
tomtheguvnor

Pour cette situation, je choisirai l'une des deux approches suivantes: singleTask dans manifest.xml OR un indicateur dans les méthodes onResume() & onDestroy() de l'activité, respectivement.

Pour la solution first: je préfère utiliser singleTask pour l'activité du manifeste plutôt que singleInstance, comme indiqué à l'aide de singleInstance. fenêtre d'applications séparée dans les applications en cours d'exécution dans bcakground et en plus des allocations de mémoire supplémentaires qui entraîneraient une très mauvaise expérience utilisateur lorsque l'utilisateur ouvre la vue des applications afin de choisir une application à reprendre . Le mieux est donc de définir l'activité au fichier manifest.xml comme suit:

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

vous pouvez vérifier les modes de lancement d'activité ici .


Pour la solution second, vous devez simplement définir une variable statique ou une variable de préférence, par exemple:

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

}

et de l’autre côté, lorsque vous souhaitez lancer cette activité, cochez:

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

Autre solution très très simple si vous ne voulez pas utiliser onActivityResult(), désactivez le bouton pendant 2 secondes (ou le temps que vous voulez), n’est pas idéal, mais peut résoudre en partie le problème, mais dans certains cas, le code est simple:

   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
Gilian

une solution plus générique à appliquer une seule fois pour toute l'application consiste à ajouter les deux classes suivantes, comme indiqué ci-dessous 

public class SingleClickButton extends AppCompatButton {
            public SingleClickButton(Context context) {
                super(context);
            }
            public SingleClickButton(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
            public SingleClickButton(Context context, AttributeSet attrs, int defStyleAttr) {
                super(context, attrs, defStyleAttr);
            }
            @Override
            public void setOnClickListener(@Nullable OnClickListener l) {
                if(l!=null){
                    super.setOnClickListener(new DebouncedClickListener() {
                        @Override
                        public void onClicked(View v) {
                            l.onClick(v);
                        }
                    });
                }else{
                    super.setOnClickListener(l);
                }
            }
        }


public abstract class DebouncedClickListener  implements View.OnClickListener {
    private static final long MAX_TIME_INTERVAL = 750L;
    private long lastClickedTime;

    public abstract void onClicked(View v);

    @Override public void onClick(View v) {
        Logger.d("Click Test trying to click");
        long now = SystemClock.elapsedRealtime();
        long diff = now - lastClickedTime;
        if (diff > MAX_TIME_INTERVAL) {
            Logger.d("Click Test allowed to click");
            onClicked(v);
        }else{
            Logger.d("Click Test not allowed to click time ");
        }
        lastClickedTime = now;
    }

}

par conséquent, chaque fois que vous voulez un bouton en un seul clic, vous venez d'ajouter la vue personnalisée au XML, comme indiqué ci-dessous

<yourpackagge.SingleClickButton .... />
0
has19

// variable pour suivre le temps de l'événement

private long mLastClickTime = 0;

2.Dans onClick, vérifiez que si l'heure actuelle et la différence de temps du dernier clic sont inférieures à i seconde, ne faites rien (retour), sinon passez à l'événement de 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.
         }
      }
 }
0
44kksharma

Ajouter:

Android:launchMode="singleTop"

L'intérieur de la balise d'activité dans AndroidManifest.xml a résolu le problème.

0
Fabio Trotta

Si vous utilisez onActivityResult, vous pouvez utiliser une variable pour enregistrer l’état.

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

Fonctionne également sur le bouton Retour car le code de demande est renvoyé à l'événement.

0
Umang

Il suffit de maintenir un indicateur dans la méthode button onClick en tant que:

public boolean oneTimeLoadActivity = false;

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