Cách ngăn hoạt động tải hai lần khi nhấn nút


93

Tôi đang cố gắng ngăn hoạt động tải hai lần nếu tôi nhấn nút hai lần ngay sau lần nhấp đầu tiên.

Tôi có một hoạt động tải khi nhấp vào một nút, nói

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

Bây giờ vì hoạt động cần tải có các cuộc gọi mạng nên sẽ mất một chút thời gian để tải (MVC). Tôi hiển thị chế độ xem đang tải cho điều này nhưng nếu tôi nhấn nút hai lần trước đó, tôi có thể thấy hoạt động được tải hai lần.

Có ai biết làm thế nào để ngăn chặn điều này?


Bạn có thể vô hiệu hóa các nút sau khi mở hoạt động ... và khi hoạt động kết thúc ,, kích hoạt lại nó ... u có thể phát hiện ra sự kết thúc của hoạt động thứ hai bằng cách gọi onActivityResult chức năng
Maneesh

Tắt nút khi nó được nhấp lần đầu và chỉ bật lại sau khi bạn muốn nút được nhấp lại.
JimmyB

vô hiệu không làm việc một cách đơn giản nếu tuyên bố rất tiếp theo là đối với một số quá trình dài hoặc bắt đầu hoạt động ... Để nút disable bạn phải có để tạo ra một chủ đề riêng biệt ...
Awais Tariq

Nếu đánh API cùng một hai lần tham khảo ở đây: techstricks.com/avoid-multiple-requests-when-using-volley
Shylendra Madda

Có thể xảy ra sự trùng lặp của nút Tránh với nhiều lần nhấp nhanh
Arnab Kar

Câu trả lời:


69

Trong trình nghe sự kiện của nút, hãy tắt nút và hiển thị một hoạt động khác.

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

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

Ghi đè onResume()để bật lại nút.

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

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

1
Đây là cách tiếp cận chính xác. Nó thậm chí sẽ xử lý các Quốc gia đã Chọn Nút cho bạn (nếu bạn cung cấp chúng) và tất cả các “món quà” của Material Design mà bạn mong đợi từ một Widget tiêu chuẩn đơn giản. Tôi không thể tin rằng mọi người sử dụng bộ đếm thời gian cho việc này. Sau đó, bạn bắt đầu thấy các thư viện lạ để xử lý những thứ như thế này ...
Martin Marconcini

157

Thêm điều này vào Activityđịnh nghĩa của bạn trong AndroidManifest.xml...

android:launchMode = "singleTop"

Ví dụ:

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

Được rồi, tôi đoán bạn đang thực hiện một số quá trình xử lý lâu sau khi bắt đầu hoạt động mới .. Đó là lý do tại sao màn hình chuyển sang màu đen. Bây giờ nếu bạn muốn tránh màn hình đen đó, bạn nên hiển thị hộp thoại tiến trình khi bắt đầu hoạt động và thực hiện quá trình xử lý dài trong một chuỗi riêng (tức là Chuỗi giao diện người dùng hoặc Đơn giản là sử dụng lớp không đồng bộ). Sau khi xử lý xong, hãy ẩn hộp thoại đó. Đây là giải pháp tốt nhất trong kiến thức của tôi và tôi đã sử dụng nó nhiều lần ... :)
Awais Tariq

Tôi có hộp thoại để hiển thị. Nhưng vâng, tôi có một phương pháp trỏ web trong onCreate. Nhưng đây có phải là giải pháp duy nhất? Bởi vì tại thời điểm này, tôi muốn xử lý mà không thay đổi luồng và tất cả. Vậy bạn có biết những cách khả thi khác. Và tôi đang có nút trong bộ điều hợp danh sách của mình và tôi đã khai báo phương thức cho nó trong xml, không phải theo chương trình
tejas 10/11/11

2
những gì khác là có thể ??? Bằng cách này hay cách khác, bạn phải triển khai phân luồng để có được một ứng dụng trông mượt mà ... Hãy thử đi bạn ..;) Chỉ cần đặt tất cả mã hiện tại vào một phương thức và gọi phương thức đó từ một luồng riêng biệt tại cùng một nơi mà bạn đã viết nó trước đó ... nó khó sẽ tăng 5-6 dòng mã ..
Awais Tariq

19
Điều này ngăn không cho hai trường hợp hoạt động tồn tại, nhưng nó không ngăn mã chạy hai lần, không chính xác. Câu trả lời được chấp nhận là tốt hơn, mặc dù có ít phiếu bầu hơn.
lilbyrdie

18
Điều này là sai, nó làm cho hoạt động không bao giờ tồn tại hai lần, ngay cả trong các nhiệm vụ khác nhau. Cách chính xác sẽ android:launchMode = "singleTop"đạt được hiệu quả mà không phá vỡ tính năng đa nhiệm của Android. Tài liệu nói rằng hầu hết các ứng dụng không nên sử dụng singleInstancetùy chọn này.
Nohus

37

Bạn có thể sử dụng các cờ ý định như thế này.

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

Nó sẽ chỉ mở một hoạt động ở đầu ngăn xếp lịch sử.


4
Câu trả lời này, kết hợp với câu trả lời được ủng hộ nhiều nhất dường như hoạt động tốt nhất. Sử dụng cờ này trong tệp kê khai của Hoạt động:, android:launchMode = "singleTop"theo cách này, nó được giải quyết mà không cần phải thêm cờ vào mọi Ý định.
Nohus

1
điều này không hữu ích khi bạn cần các hoạt động lồng nhau, bởi vì bạn không thể có hai hoạt động cùng loại.
Behnam Heydari

4
Đây không phải là làm việc trong trường hợp của startActivityForResult
raj

27

Vì SO không cho phép tôi bình luận về các câu trả lời khác, nên tôi phải bỏ qua chủ đề này bằng một câu trả lời mới.

Các câu trả lời phổ biến cho vấn đề "hoạt động mở hai lần" và trải nghiệm của tôi với các giải pháp này (Android 7.1.1):

  1. Nút vô hiệu hóa bắt đầu hoạt động: Hoạt động nhưng cảm thấy hơi vụng về. Nếu bạn có nhiều cách để bắt đầu hoạt động trong ứng dụng của mình (ví dụ: một nút trong thanh tác vụ VÀ bằng cách nhấp vào một mục trong chế độ xem danh sách), bạn phải theo dõi trạng thái đã bật / tắt của nhiều phần tử GUI. Thêm vào đó, không thuận tiện lắm khi tắt các mục đã nhấp trong chế độ xem danh sách. Vì vậy, không phải là một cách tiếp cận rất phổ biến.
  2. khởi chạyMode = "singleInstance": Không hoạt động với startActivityForResult (), ngắt điều hướng trở lại với startActivity (), không được khuyến nghị cho các ứng dụng thông thường theo tài liệu kê khai Android.
  3. khởi chạyMode = "singleTask": Không hoạt động với startActivityForResult (), không được khuyến nghị cho các ứng dụng thông thường theo tài liệu kê khai Android.
  4. FLAG_ACTIVITY_REORDER_TO_FRONT: Ngắt nút quay lại.
  5. FLAG_ACTIVITY_SINGLE_TOP: Không hoạt động, hoạt động vẫn được mở hai lần.
  6. FLAG_ACTIVITY_CLEAR_TOP: Đây là cái duy nhất làm việc cho tôi.

CHỈNH SỬA: Đây là để bắt đầu các hoạt động với startActivity (). Khi sử dụng startActivityForResult (), tôi cần đặt cả FLAG_ACTIVITY_SINGLE_TOP và FLAG_ACTIVITY_CLEAR_TOP.


FLAG_ACTIVITY_CLEAR_TOP: Đây là ứng dụng duy nhất hoạt động với tôi trên Android 7.1.1
Mingjiang Shi

1
Tôi đang sử dụng "FLAG_ACTIVITY_REORDER_TO_FRONT" và nó đang hoạt động tốt và nút Quay lại cũng hoạt động bình thường. Chính xác thì ý bạn là gì khi nói "Nút quay lại"? Bạn có thể làm rõ về điều đó?
Mirmuhsin Sodiqov

Tôi thấy rằng cờ "REORDER" có lỗi ... và nó không được sắp xếp lại trong KitKat. Tuy nhiên, tôi đã kiểm tra nó trong Lollipop và Pie, nó đang hoạt động tốt.
Mirmuhsin Sodiqov

7

Nó chỉ hoạt động cho tôi khi startActivity(intent)

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

1
@raj bạn đã thử bằng cách thêm cái này android:launchMode = "singleInstance"vào tệp kê khai của thẻ hoạt động của mình chưa?
Shylendra Madda

5

Sử dụng singleInstance để tránh hoạt động gọi hai lần.

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

4

Giả sử @wannik đúng nhưng nếu chúng tôi có nhiều hơn 1 nút gọi cùng một trình nghe hành động và tôi nhấp vào hai nút gần như cùng một lúc trước khi bắt đầu hoạt động tiếp theo ...

Vì vậy, thật tốt nếu bạn có trường private boolean mIsClicked = false;và người nghe:

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

onResume()chúng ta cần trả lại trạng thái:

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

    mIsClicked = false;
}

Sự khác biệt giữa câu trả lời của tôi và @ wannik là gì?

Nếu bạn đặt bật thành false trong trình nghe của chế độ xem đang gọi, nút khác sử dụng cùng một trình nghe sẽ vẫn được bật. Vì vậy, để đảm bảo rằng hành động của người nghe không được gọi hai lần, bạn cần phải có một thứ gì đó toàn cục vô hiệu hóa tất cả các lệnh gọi của người nghe (đừng bận tâm nếu đó là phiên bản mới hoặc không)

Sự khác biệt giữa câu trả lời của tôi và những người khác là gì?

Họ đang suy nghĩ theo cách đúng đắn nhưng họ không nghĩ đến việc quay trở lại cùng một trường hợp của hoạt động gọi trong tương lai :)


Servoper, cảm ơn bạn đã nghiên cứu. Câu hỏi này đã được giải quyết rồi, bao giờ câu trả lời của bạn cũng có vẻ hứa hẹn cho tình huống bạn đã kể. Hãy để tôi thử và đi kèm với kết quả :)
Tejas

1
Tôi gặp sự cố này trong một trò chơi của mình. Tôi có baloons "chọn cấp độ" có cùng người nghe và các lượt xem chỉ khác nhau theo các thẻ. Vì vậy, nếu tôi nhanh chóng chọn hai balo, nó sẽ bắt đầu hai hoạt động. Tôi biết điều đó bởi vì hoạt động mới bắt đầu phát ra âm thanh .. và trong trường hợp này âm thanh được phát hai lần ... nhưng bạn có thể kiểm tra nó bằng cách nhấp vào trở lại để đưa bạn về hoạt động trước đó
Sir NIkolay Cesar The First

1
Điều này là không đủ. Bạn cũng cần phải sử dụng synchronized(mIsClicked) {...}để được an toàn 100%.
Monstieur

@Monstieur bạn không cần một khối đồng bộ vì đây là tất cả các chủ đề chính ...
Martin Marconcini

@MartinMarconcini Chỉ vì nó an toàn trong một hoạt động của Android không khiến nó trở thành mã tốt. Nếu nó là một lớp độc lập, nó sẽ phải được ghi lại là không an toàn cho luồng.
Monstieur

4

Đối với tình huống này, tôi sẽ đi đến một trong hai cách được tiếp cận, singleTasktrong tệp manifest.xml HOẶC một cờ trong các phương thức onResume()& của Hoạt động onDestroy()tương ứng.

Đối với giải pháp đầu tiên : Tôi thích sử dụng singleTaskcho hoạt động trong tệp kê khai hơn là singleInstance, theo cách sử dụng, singleInstancetôi phát hiện ra rằng trong một số trường hợp, hoạt động tạo ra một phiên bản riêng biệt mới cho chính nó dẫn đến có hai cửa sổ ứng dụng riêng biệt trong các ứng dụng đang chạy trong bcakground và bên cạnh việc phân bổ thêm bộ nhớ sẽ dẫn đến Trải nghiệm người dùng rất tệ khi người dùng mở chế độ xem ứng dụng để chọn một số ứng dụng để tiếp tục. Vì vậy, cách tốt hơn là xác định hoạt động tại tệp kê khai.xml như sau:

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

bạn có thể kiểm tra các chế độ khởi chạy hoạt động tại đây .


Đối với giải pháp thứ hai , bạn chỉ cần xác định một biến tĩnh hoặc một biến tùy chọn, ví dụ:

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

}

và từ phía bên kia khi bạn muốn khởi chạy hoạt động này, chỉ cần kiểm tra:

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

3

Tôi nghĩ rằng bạn đang giải quyết vấn đề một cách sai lầm. Nói chung đó là một ý tưởng tồi cho một hoạt động được thực hiện lâu dài các yêu cầu web trong bất kỳ phương pháp vòng đời khởi động của nó ( onCreate(), onResume(), vv). Thực sự những phương pháp này chỉ nên được sử dụng để khởi tạo và khởi tạo các đối tượng mà hoạt động của bạn sẽ sử dụng và do đó sẽ tương đối nhanh chóng.

Nếu bạn cần thực hiện một yêu cầu web thì hãy thực hiện việc này trong một chuỗi nền từ hoạt động mới khởi chạy của bạn (và hiển thị hộp thoại tải trong hoạt động mới). Khi chuỗi yêu cầu nền hoàn thành, nó có thể cập nhật hoạt động và ẩn hộp thoại.

Khi đó, điều này có nghĩa là hoạt động mới của bạn sẽ được khởi chạy ngay lập tức và ngăn không cho nhấp đúp chuột vào.


3

Hi vọng điêu nay co ich:

 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

Giải pháp rất đơn giản khác nếu bạn không muốn sử dụng onActivityResult()là vô hiệu hóa nút trong 2 giây (hoặc thời gian bạn muốn), không phải là lý tưởng, nhưng có thể giải quyết một phần vấn đề là một số trường hợp và mã rất đơn giản:

   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

// biến để theo dõi thời gian sự kiện

private long mLastClickTime = 0;

2. Trong onClick, hãy kiểm tra xem nếu thời gian hiện tại và thời gian nhấp chuột cuối cùng chênh lệch nhỏ hơn tôi giây thì không làm gì cả (quay lại) nếu không hãy chuyển sang sự kiện nhấp chuột

 @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

Chỉ cần duy trì một cờ trong phương pháp onClick button như:

public boolean oneTimeLoadActivity = false;

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

0

Nếu bạn đang sử dụng onActivityResult, bạn có thể sử dụng một biến để lưu trạng thái.

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

Hoạt động trên nút quay lại được nhấn vì mã yêu cầu được trả lại trong sự kiện.


0

thêm Chế độ khởi chạy làm tác vụ đơn lẻ trong tệp kê khai để tránh hoạt động mở hai lần khi nhấp

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

Điều đó có thể sẽ không hoạt động vì bạn sẽ phải tuyên bố nó là cuối cùng.
King

-1

Sử dụng một flagbiến đặt nó to true, Kiểm tra xem nó có đúng returnkhông nếu không thì thực hiện Activity Call.

Bạn cũng có thể sử dụng setClickable (false) để thực hiện Cuộc gọi hoạt động

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

perform click; wait; flg = false;khi chúng tôi nhận lại
Xeno Lupus

-1

Bạn chỉ có thể ghi đè startActivityForResult và sử dụng biến phiên bản:

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

Bạn cũng có thể thử cái này

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

        });
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.