Düğmeye iki kez basıldığında aktivitenin yüklenmesi nasıl engellenir


96

İlk tıklamadan hemen sonra düğmeye iki kez basarsam etkinliğin iki kez yüklenmesini engellemeye çalışıyorum.

Bir düğmeye tıklandığında yüklenen bir etkinliğim var.

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

Artık yüklenecek etkinliğin ağ çağrıları olması nedeniyle, yüklenmesi biraz zaman alıyor (MVC). Bunun için bir yükleme görünümü gösteriyorum, ancak bundan önce düğmeye iki kez basarsam, etkinliğin iki kez yüklendiğini görebilirim.

Bunu nasıl önleyeceğini bilen var mı?


Etkinliği açtıktan sonra düğmeyi devre dışı bırakabilirsiniz ... ve etkinlik bittiğinde, yeniden etkinleştirin ... ikinci etkinliğin bitişini onActivityResult işlevini çağırarak tespit edebilirsiniz
Maneesh

İlk tıklandığında düğmeyi devre dışı bırakın ve daha sonra yalnızca düğmenin tekrar tıklanmasını istediğinizde yeniden etkinleştirin.
JimmyB

Bir sonraki ifade uzun bir süreç veya aktivite başlangıcı ise devre dışı bırakma basit bir şekilde çalışmaz ... Düğmeyi devre dışı bırakmak için ayrı bir iş parçacığı oluşturmanız gerekir ...
Awais Tariq

Aynı API'ye iki kez basarsanız,
Shailendra Madda

Yanıtlar:


69

Düğmenin olay dinleyicisinde düğmeyi devre dışı bırakın ve başka bir etkinlik gösterin.

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

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

onResume()Düğmeyi yeniden etkinleştirmek için geçersiz kıl .

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

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

1
Doğru yaklaşım budur. Hatta sizin için Düğme Seçilmiş Durumları (eğer sağlarsanız) ve basit bir standart Widget'tan bekleyeceğiniz tüm Materyal Tasarım “güzelliklerini” de işleyecektir. İnsanların bunun için zamanlayıcı kullandığına inanamıyorum. Sonra bunun gibi şeylerin üstesinden gelmek için garip kütüphaneler görmeye başlıyorsunuz…
Martin Marconcini

158

Bunu Activitytanımınıza ekleyin AndroidManifest.xml...

android:launchMode = "singleTop"

Örneğin:

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

tamam sanırım yeni aktiviteye başladıktan sonra uzun bir işlem yapıyorsunuz .. Bu yüzden ekran kararıyor. Şimdi bu siyah ekrandan kaçınmak istiyorsanız, faaliyetin başlangıcında bir ilerleme iletişim kutusu göstermeli ve uzun işlemi ayrı bir iş parçacığında yapmalısınız (yani UI Thread veya Simply use async class). İşleminiz bittiğinde bu iletişim kutusunu gizleyin. Bildiğim kadarıyla en iyi çözüm bu ve birkaç kez kullandım ... :)
Awais Tariq

Göstermem gereken diyalog var. Ama evet, onCreate'te web'i işaret eden bir yöntemim var. Ama tek çözüm bu mu? Çünkü bu noktada, ipliği falan değiştirmeden halletmek istiyorum. Öyleyse başka olası yollar biliyor musunuz? Ve listem bağdaştırıcımda düğme var ve bunun yöntemini programlı olarak değil
xml'de

2
başka ne mümkün ??? Öyle ya da böyle pürüzsüz görünen bir uygulama elde etmek için iş parçacığı uygulamanız gerekir ... Deneyin dostum ..;) Sadece mevcut tüm kodu bir yönteme koyun ve bu yöntemi yazdığınız yerdeki ayrı bir iş parçacığından çağırın daha erken ... Kodun beş ila altı satırını pek artırmayacak ..
Awais Tariq

19
Bu, etkinliğin iki örneğinin var olmasını engeller, ancak kodun iki kez hatalı çalışmasını engellemez. Kabul edilen cevap, daha az oy almasına rağmen daha iyidir.
lilbyrdie

18
Bu yanlıştır, aktivitenin farklı görevlerde bile iki kez var olmamasını sağlar. Doğru yol android:launchMode = "singleTop", Android çoklu görevini bozmadan etkiyi gerçekleştiren olacaktır . Belgeler, çoğu uygulamanın bu singleInstanceseçeneği kullanmaması gerektiğini belirtir .
Nohus

37

Bunun gibi niyet bayraklarını kullanabilirsiniz.

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

Geçmiş yığınının en üstünde yalnızca bir etkinliği açık hale getirecektir.


4
Bu cevap, en çok oy verilen cevapla birleştirildiğinde en iyi sonucu veriyor gibi görünüyor. Aktivitenin bildiriminde bu bayrağı kullanın: android:launchMode = "singleTop"bu şekilde, her Niyete bayrak eklemeye gerek kalmadan çözülür.
Nohus

1
Bu, iç içe etkinliklere ihtiyaç duyduğunuzda kullanışlı değildir, çünkü aynı tür iki etkinliğe sahip olamazsınız.
Behnam Heydari

5
Bu, startActivityForResult durumunda çalışmıyor
raj

27

SO diğer cevaplar hakkında yorum yapmama izin vermediğinden, bu konuyu yeni bir cevapla kirletmem gerekiyor.

"Etkinlik iki kez açılır" sorunu için ortak yanıtlar ve bu çözümlerle ilgili deneyimlerim (Android 7.1.1):

  1. Aktiviteyi başlatan devre dışı bırak düğmesi: Çalışır ancak biraz sakar hissedilir. Uygulamanızda etkinliği başlatmak için birden fazla yolunuz varsa (örneğin, işlem çubuğundaki bir düğme VE bir liste görünümündeki bir öğeye tıklayarak), birden çok GUI öğesinin etkin / devre dışı durumunu izlemeniz gerekir. Ayrıca, örneğin bir liste görünümünde tıklanan öğeleri devre dışı bırakmak pek uygun değildir. Yani çok evrensel bir yaklaşım değil.
  2. launchMode = "singleInstance": startActivityForResult () ile çalışmıyor, startActivity () ile geriye doğru gezinmeyi kesiyor, Android bildirim belgelerine göre normal uygulamalar için önerilmez.
  3. launchMode = "singleTask": startActivityForResult () ile çalışmıyor, Android bildirim belgelerine göre normal uygulamalar için önerilmez.
  4. FLAG_ACTIVITY_REORDER_TO_FRONT: Geri Al düğmesi.
  5. FLAG_ACTIVITY_SINGLE_TOP: Çalışmıyor, etkinlik hala iki kez açık.
  6. FLAG_ACTIVITY_CLEAR_TOP: Benim için çalışan tek kişi bu.

DÜZENLE: Bu, startActivity () ile etkinlikleri başlatmak içindi. StartActivityForResult () kullanırken hem FLAG_ACTIVITY_SINGLE_TOP hem de FLAG_ACTIVITY_CLEAR_TOP ayarlamam gerekiyor.


FLAG_ACTIVITY_CLEAR_TOP: Bu, Android 7.1.1'de benim için çalışan tek program
Mingjiang Shi

1
"FLAG_ACTIVITY_REORDER_TO_FRONT" kullanıyorum ve gayet iyi çalışıyor ve Geri düğmesi de normal davranıyor. "Geri ara düğmesi" ile tam olarak ne demek istediniz? Bunu açıklayabilir misin?
Mirmukhsin Sodikov

O "REORDER" bayrak bir hata vardı buldum ... ve o değildi yeniden sıralama KitKat'ta. Ancak, Lollipop ve Pie'de kontrol ettim, iyi çalışıyor.
Mirmukhsin Sodikov

9

Sadece benim için çalışıyordu startActivity(intent)

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

1
@raj bunu android:launchMode = "singleInstance"etkinlik etiketinizin Manifest dosyasına ekleyerek denediniz mi?
Shailendra Madda

5

Aktivitenin iki kez çağrılmasını önlemek için singleInstance kullanın.

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

4

Diyelim ki @wannik haklı ama aynı eylem dinleyicisini çağıran birden fazla butonumuz varsa ve bir sonraki aktiviteye başlamadan önce hemen hemen aynı anda iki butona tıklarsam ...

Dolayısıyla, alanınız varsa private boolean mIsClicked = false;ve dinleyicide varsa iyi olur :

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

Ve onResume()eyalete geri dönmemiz gerekiyor:

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

    mIsClicked = false;
}

Benim ve @ wannik'in cevabı arasındaki fark nedir?

Görünümü çağıran dinleyicide etkinleştirildiğini false olarak ayarlarsanız, aynı dinleyiciyi kullanan diğer düğme yine de etkinleştirilecektir. Bu nedenle, dinleyicinin eyleminin iki kez çağrılmadığından emin olmak için, dinleyicinin tüm çağrılarını devre dışı bırakan genel bir şeye sahip olmanız gerekir (yeni bir örnek olup olmadığını boşverin)

Cevabımla diğerleri arasındaki fark nedir?

Doğru düşünüyorlar ama gelecekte aynı çağrı etkinliğine geri dönmeyi düşünmüyorlar :)


servoper, araştırmanız için teşekkür ederim. Bu soru zaten çözüldü, cevabınız anlattığınız durum için ne kadar umut verici görünse de. Bir deneyeyim ve sonucu ile geleyim :)
tejas

1
Oyunlarımdan birinde bu sorunu yaşıyorum. Aynı dinleyiciye sahip "seviye seçme" balonlarım var ve görünümler etiketlere göre farklı. Yani hızlıca iki balon seçersem, iki aktivite başlatır. Biliyorum, çünkü yeni etkinlik ses
çıkarmaya

1
Bu yeterli değil. synchronized(mIsClicked) {...}% 100 güvenli olmak için bir de kullanmanız gerekir .
Monstieur

@Monstieur senkronize bir bloğa ihtiyacınız yok çünkü bunların hepsi Ana Konu…
Martin Marconcini

@MartinMarconcini Bir Android etkinliğinde güvenli olması onu iyi kod yapmaz. Bağımsız bir sınıf olsaydı, iş parçacığı açısından güvenli olmadığı belgelenmesi gerekirdi.
Monstieur

4

Bu durum için, singleTaskmanifest.xml'de yaklaşılan ikisinden birini VEYA Activity's onResume()& onDestroy()method'larında sırasıyla bir bayrak seçeceğim.

İçin ilk çözümü: Ben kullanmayı tercih singleTaskziyade Manifestte aktivite için singleInstancekullanma gereği, singleInstancebazı durumlarda aktivite çalışan uygulamalardaki iki ayrı uygulama penceresini olması sonucu kendisi için yeni bir ayrı bir örneğini oluşturarak anladım bcakground'da ve kullanıcı devam ettirmek için bazı uygulamaları seçmek için uygulamalar görünümünü açtığında çok kötü bir Kullanıcı Deneyimi ile sonuçlanacak ekstra bellek ayırmalarının yanı sıra. Bu nedenle, daha iyi bir yol, aşağıdaki gibi manifest.xml'de tanımlanmış aktiviteye sahip olmaktır:

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

etkinlik başlatma modlarını buradan kontrol edebilirsiniz .


İçin ikinci çözümü, sadece örneğin statik bir değişken veya bir tercih değişkeni tanımlamak zorunda:

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

}

ve diğer taraftan bu aktiviteyi başlatmak istediğinizde şunları kontrol edin:

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

3

Sanırım sorunu yanlış şekilde çözeceksiniz. Genellikle bir etkinlik onun başlangıç yaşam döngüsü yöntemlerinden (herhangi uzun çalışan web isteklerini yapma gibi için kötü bir fikirdir onCreate(), onResume()vs.). Gerçekten bu yöntemler, etkinliğinizin kullanacağı nesneleri başlatmak ve başlatmak için kullanılmalı ve bu nedenle nispeten hızlı olmalıdır.

Bir web isteği gerçekleştirmeniz gerekiyorsa, bunu yeni başlatılan etkinliğinizden bir arka planda yapın (ve yeni etkinlikte yükleme iletişim kutusunu gösterin). Arka plan istek dizisi tamamlandığında, etkinliği güncelleyebilir ve iletişim kutusunu gizleyebilir.

Bu, yeni etkinliğinizin hemen başlatılması ve çift tıklamanın mümkün olmasını engellemesi gerektiği anlamına gelir.


3

Bu yardımcı olur umarım:

 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

Kullanmak istemiyorsanız diğer çok çok basit çözüm onActivityResult()düğmeyi 2 saniye (veya istediğiniz süre) devre dışı bırakmaktır, ideal değildir, ancak sorunu kısmen çözebilir bazı durumlarda ve kod basittir:

   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

// etkinlik zamanını izlemek için değişken

private long mLastClickTime = 0;

2. onClick'te, geçerli saat ve son tıklama zamanı farkı saniyeden azsa hiçbir şey yapmayın (geri dönün) yoksa tıklama olayına gidin

 @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

Düğme onClick yönteminde şu şekilde bir bayrak bulundurmanız yeterlidir:

public boolean oneTimeLoadActivity = false;

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

1

Tıklandığında etkinliğin iki kez açılmasını önlemek için Başlatma Modu'nu manifest'e tek görev olarak ekleyin

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

0

OnActivityResult kullanıyorsanız, durumu kaydetmek için bir değişken kullanabilirsiniz.

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

Geri düğmesine basıldığında da çalışır çünkü olayda istek kodu döndürülür.


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

Nihai olarak ilan etmeniz gerekeceği için muhtemelen işe yaramayacaktır.
Kral

-1

Bir flagdeğişken ayarlayın to true, onun gerçek olup olmadığını kontrol edin, sadece returnAktivite Çağrısı gerçekleştirin.

Aktivite Çağrısını yürütmek için setClickable (yanlış) kullanabilirsiniz.

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

perform click; wait; flg = false;geri döndüğümüz zaman için
Xeno Lupus

-1

Sadece startActivityForResult'u geçersiz kılabilir ve örnek değişkenini kullanabilirsiniz:

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

Bunu da deneyebilirsin

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

        });
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.