Bagaimana mencegah aktivitas memuat dua kali dengan menekan tombol


96

Saya mencoba mencegah aktivitas memuat dua kali jika saya menekan tombol dua kali secara instan setelah klik pertama.

Saya memiliki aktivitas yang memuat dengan mengklik tombol, misalnya

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

Sekarang karena aktivitas yang akan dimuat memiliki panggilan jaringan, dibutuhkan sedikit waktu untuk memuat (MVC). Saya memang menunjukkan tampilan pemuatan untuk ini, tetapi jika saya menekan tombol dua kali sebelumnya, saya dapat melihat aktivitas dimuat dua kali.

Apakah ada yang tahu bagaimana mencegahnya?


Anda dapat menonaktifkan tombol setelah membuka aktivitas ... dan saat aktivitas selesai ,, aktifkan kembali ... Anda dapat mendeteksi penyelesaian aktivitas kedua dengan memanggil fungsi onActivityResult
Maneesh

Nonaktifkan tombol saat pertama kali diklik dan aktifkan kembali nanti hanya jika Anda ingin tombol tersebut diklik lagi.
JimmyB

penonaktifan tidak bekerja dengan cara yang sederhana jika pernyataan berikutnya adalah untuk beberapa proses panjang atau aktivitas mulai ... Untuk menonaktifkan tombol Anda harus membuat utas terpisah ...
Awais Tariq

Jika mengenai API yang sama dua kali, lihat di sini: techstricks.com/avoid-multiple-requests-when-using-volley
Shailendra Madda

Jawaban:


69

Di pemroses acara tombol, nonaktifkan tombol dan tampilkan aktivitas lain.

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

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

Ganti onResume()untuk mengaktifkan kembali tombol.

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

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

1
Ini adalah pendekatan yang benar. Ia bahkan akan menangani Status Pilihan Tombol untuk Anda (jika Anda menyediakannya) dan semua "barang" Desain Material yang Anda harapkan dari Widget standar sederhana. Saya tidak percaya orang menggunakan timer untuk ini. Kemudian Anda mulai melihat perpustakaan aneh untuk menangani hal-hal seperti ini…
Martin Marconcini

158

Tambahkan ini ke Activitydefinisi Anda di AndroidManifest.xml...

android:launchMode = "singleTop"

Sebagai contoh:

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

ok saya rasa Anda melakukan beberapa proses lama setelah memulai aktivitas baru .. Karena itulah layar menjadi hitam. Sekarang jika Anda ingin menghindari layar hitam itu, Anda harus menampilkan dialog kemajuan di awal aktivitas dan melakukan pemrosesan lama di utas terpisah (yaitu UI Thread atau Cukup gunakan kelas async). Setelah pemrosesan Anda selesai, sembunyikan dialog itu. Ini adalah solusi terbaik menurut pengetahuan saya dan saya telah menggunakannya beberapa kali ... :)
Awais Tariq

Saya memiliki dialog untuk ditampilkan. Tapi ya saya punya satu metode menunjuk web di onCreate. Tapi apakah ini satu-satunya solusi? Karena pada titik ini, saya ingin menangani tanpa mengubah utas dan semuanya. Jadi, apakah Anda tahu cara lain yang mungkin. Dan saya memiliki tombol di adaptor daftar saya dan saya telah menyatakan metode untuk itu di xml, bukan secara terprogram
tejas

2
apa lagi yang mungkin ??? Dengan satu atau lain cara Anda harus menerapkan threading untuk mendapatkan aplikasi yang tampak mulus ... Cobalah bung ..;) Cukup letakkan semua kode saat ini dalam sebuah metode dan panggil metode itu dari utas terpisah di tempat yang sama tempat Anda menulis itu sebelumnya ... Ini hampir tidak akan meningkatkan lima hingga enam baris kode ..
Awais Tariq

19
Ini mencegah dua kejadian aktivitas dari yang ada, tetapi tidak mencegah kode berjalan dua kali, secara tidak benar. Jawaban yang diterima lebih baik, meski memiliki lebih sedikit suara.
lilbyrdie

18
Ini salah, membuat aktivitas tidak pernah ada dua kali, bahkan dalam tugas yang berbeda. Cara yang benar adalah android:launchMode = "singleTop", yang mencapai efek tanpa merusak multitasking Android. Dokumentasi menyatakan bahwa sebagian besar aplikasi tidak boleh menggunakan singleInstanceopsi ini.
Nohus

37

Anda bisa menggunakan flag maksud seperti ini.

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

Ini akan membuat hanya satu aktivitas yang terbuka di bagian atas tumpukan riwayat.


4
Jawaban ini, digabungkan dengan jawaban yang paling banyak disukai tampaknya berfungsi paling baik. Gunakan tanda ini dalam manifes Aktivitas:, android:launchMode = "singleTop"dengan cara ini diselesaikan tanpa harus menambahkan tanda ke setiap Intent.
Nohus

1
ini tidak berguna saat Anda membutuhkan aktivitas bertingkat, karena Anda tidak bisa memiliki dua jenis aktivitas yang sama.
Behnam Heydari

5
Ini tidak berfungsi dalam kasus startActivityForResult
raj

27

Karena SO tidak mengizinkan saya mengomentari jawaban lain, saya harus mencemari utas ini dengan jawaban baru.

Jawaban umum untuk masalah "aktivitas membuka dua kali" dan pengalaman saya dengan solusi ini (Android 7.1.1):

  1. Tombol Nonaktifkan yang memulai aktivitas: Berfungsi tetapi terasa sedikit canggung. Jika Anda memiliki beberapa cara untuk memulai aktivitas di aplikasi Anda (mis. Tombol di bilah tindakan DAN dengan mengklik item dalam tampilan daftar), Anda harus melacak status aktif / nonaktif dari beberapa elemen GUI. Selain itu, sangat tidak nyaman untuk menonaktifkan item yang diklik dalam tampilan daftar, misalnya. Jadi, bukan pendekatan yang sangat universal.
  2. launchMode = "singleInstance": Tidak bekerja dengan startActivityForResult (), memecah navigasi dengan startActivity (), tidak disarankan untuk aplikasi biasa oleh dokumentasi manifes Android.
  3. launchMode = "singleTask": Tidak bekerja dengan startActivityForResult (), tidak disarankan untuk aplikasi biasa oleh dokumentasi manifes Android.
  4. FLAG_ACTIVITY_REORDER_TO_FRONT: Tombol pemutusan kembali.
  5. FLAG_ACTIVITY_SINGLE_TOP: Tidak bekerja, kegiatan masih dibuka dua kali.
  6. FLAG_ACTIVITY_CLEAR_TOP: Ini adalah satu-satunya yang bekerja untuk saya.

EDIT: Ini untuk memulai aktivitas dengan startActivity (). Saat menggunakan startActivityForResult () saya perlu menyetel FLAG_ACTIVITY_SINGLE_TOP dan FLAG_ACTIVITY_CLEAR_TOP.


FLAG_ACTIVITY_CLEAR_TOP: Ini adalah satu-satunya yang berfungsi untuk saya di Android 7.1.1
Mingjiang Shi

1
Saya menggunakan "FLAG_ACTIVITY_REORDER_TO_FRONT" dan berfungsi dengan baik dan tombol Kembali juga berfungsi normal. Apa sebenarnya yang Anda maksud dengan "tombol Breaks back"? Bisakah Anda menjelaskan itu?
Mirmukhsin Sodikov

Saya menemukan bahwa flag "REORDER" memiliki bug ... dan tidak menyusun ulang di KitKat. Namun, saya memeriksanya di Lollipop dan Pie, itu berfungsi dengan baik.
Mirmukhsin Sodikov

9

Itu hanya bekerja untuk saya ketika startActivity(intent)

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

1
@raj sudahkah Anda mencoba dengan menambahkan ini android:launchMode = "singleInstance"dalam file Manifest dari tag aktivitas Anda?
Shailendra Madda

5

Gunakan singleInstance untuk menghindari aktivitas memanggil dua kali.

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

4

Katakanlah @wannik benar tetapi jika kita memiliki lebih dari 1 tombol yang memanggil pendengar tindakan yang sama dan saya mengklik dua tombol sekali pada waktu yang hampir bersamaan sebelum memulai aktivitas berikutnya ...

Jadi alangkah baiknya jika Anda memiliki field private boolean mIsClicked = false;dan di listener:

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

Dan onResume()kita perlu mengembalikan keadaan:

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

    mIsClicked = false;
}

Apa perbedaan antara jawaban saya dan @ wannik?

Jika Anda menyetel diaktifkan ke false di pemrosesnya memanggil lihat, tombol lain yang menggunakan pemroses yang sama akan tetap diaktifkan. Jadi untuk memastikan bahwa tindakan listener tidak dipanggil dua kali, Anda harus memiliki sesuatu yang global yang menonaktifkan semua pemanggilan listener (tidak masalah apakah itu instance baru atau tidak)

Apa perbedaan antara jawaban saya dan orang lain?

Mereka berpikir dengan cara yang benar tetapi mereka tidak berpikir untuk masa depan kembali ke contoh yang sama dari aktivitas panggilan :)


servoper, terima kasih atas penelitian Anda. Pertanyaan ini sudah terpecahkan, namun jawaban Anda juga terlihat menjanjikan untuk situasi yang Anda ceritakan. Biarkan saya mencoba dan datang dengan hasilnya :)
tejas

1
Saya memiliki masalah ini di salah satu permainan saya. Saya memiliki balon "tingkat pilihan" yang memiliki pendengar yang sama dan tampilan hanya berbeda menurut tag. Jadi jika saya cepat memilih dua balon, itu memulai dua aktivitas. Saya tahu itu karena aktivitas baru mulai berbunyi .. dan dalam hal ini suara diputar dua kali ... tetapi Anda dapat memeriksanya dengan mengklik kembali yang akan mengarahkan Anda ke aktivitas sebelumnya
Sir NIkolay Cesar The First

1
Ini tidak cukup. Anda perlu menggunakan synchronized(mIsClicked) {...}sumur agar 100% aman.
Monstieur

@Monstieur Anda tidak memerlukan blok tersinkronisasi karena ini semua Thread Utama…
Martin Marconcini

@MartinMarconcini Hanya karena kebetulan aman dalam aktivitas Android tidak menjadikannya kode yang baik. Jika itu adalah kelas yang berdiri sendiri, itu harus didokumentasikan sebagai tidak aman untuk thread.
Monstieur

4

Untuk situasi ini, saya akan memilih salah satu dari dua pendekatan, singleTaskdi manifest.xml ATAU sebuah tanda di Aktivitas onResume()&onDestroy() masing-masing metode .

Untuk solusi pertama : Saya lebih suka menggunakan singleTaskuntuk aktivitas dalam manifes daripada singleInstance, sesuai penggunaan, singleInstancesaya menemukan bahwa dalam beberapa kesempatan aktivitas membuat instance terpisah baru untuk dirinya sendiri yang menghasilkan dua jendela aplikasi terpisah di aplikasi yang sedang berjalan di bcakground dan di samping alokasi memori tambahan yang akan menghasilkan Pengalaman Pengguna yang sangat buruk saat pengguna membuka tampilan aplikasi untuk memilih beberapa aplikasi untuk dilanjutkan. Jadi, cara yang lebih baik adalah dengan menetapkan aktivitas di manifest.xml seperti berikut:

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

Anda dapat memeriksa mode peluncuran aktivitas di sini .


Untuk solusi kedua , Anda hanya perlu menentukan variabel statis atau variabel preferensi, misalnya:

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

}

dan dari sisi lain ketika Anda ingin meluncurkan aktivitas ini, cukup centang:

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

3

Saya pikir Anda akan menyelesaikan masalah dengan cara yang salah. Umumnya itu adalah ide yang buruk bagi suatu kegiatan untuk membuat permintaan web lama berjalan di salah metode startup siklus hidup ( onCreate(), onResume(), dll). Sungguh, metode ini hanya boleh digunakan untuk membuat instance dan menginisialisasi objek yang akan digunakan aktivitas Anda dan karenanya harus relatif cepat.

Jika Anda perlu melakukan permintaan web, lakukan ini di thread latar belakang dari aktivitas yang baru Anda luncurkan (dan tampilkan dialog pemuatan di aktivitas baru). Setelah thread permintaan latar belakang selesai, thread tersebut dapat memperbarui aktivitas dan menyembunyikan dialog.

Ini berarti aktivitas baru Anda harus segera diluncurkan dan mencegah kemungkinan klik dua kali.


3

Semoga ini membantu:

 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

Solusi lain yang sangat sederhana jika Anda tidak ingin menggunakan onActivityResult()adalah menonaktifkan tombol selama 2 detik (atau waktu yang Anda inginkan), tidak ideal, tetapi dapat menyelesaikan sebagian masalah adalah beberapa kasus dan kodenya sederhana:

   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

// variabel untuk melacak waktu acara

private long mLastClickTime = 0;

2. Di onClick periksa bahwa jika waktu saat ini dan waktu klik terakhir perbedaan kurang dari i detik maka jangan lakukan apa-apa (kembali) lagi pergi untuk acara klik

 @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

Cukup pertahankan satu tanda dalam metode onClick tombol sebagai:

public boolean oneTimeLoadActivity = false;

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

1

tambahkan Mode Peluncuran sebagai tugas tunggal dalam manifes untuk menghindari aktivitas dibuka dua kali saat mengklik

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

0

Jika Anda menggunakan onActivityResult, Anda dapat menggunakan variabel untuk menyimpan status.

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

Bekerja pada tombol kembali yang ditekan juga karena kode permintaan dikembalikan pada acara.


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

Itu mungkin tidak akan berhasil karena Anda harus menyatakannya sebagai final.
Raja

-1

Gunakan flagvariabel set itu to true, Periksa apakah benar hanya returnmelakukan Panggilan Aktivitas.

Anda juga bisa menggunakan setClickable (false) satu yang menjalankan Panggilan Aktivitas

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

perform click; wait; flg = false;untuk saat kita kembali
Xeno Lupus

-1

Anda bisa mengganti startActivityForResult dan menggunakan variabel instance:

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

Anda juga bisa mencobanya

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

        });
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.