Nhấp vào nút quay lại hai lần để thoát khỏi một hoạt động


329

Gần đây tôi đã nhận thấy mẫu này trong rất nhiều ứng dụng và trò chơi Android: khi nhấp vào nút quay lại để "thoát" ứng dụng, sẽ Toastxuất hiện một thông báo tương tự như "Vui lòng nhấp lại TRỞ LẠI để thoát".

Tôi đã tự hỏi, khi tôi thấy nó ngày càng thường xuyên hơn, đó có phải là một tính năng tích hợp mà bạn có thể truy cập bằng cách nào đó trong một hoạt động không? Tôi đã xem mã nguồn của nhiều lớp nhưng dường như tôi không thể tìm thấy gì về điều đó.

Tất nhiên, tôi có thể nghĩ về một vài cách để đạt được chức năng tương tự khá dễ dàng (cách dễ nhất có lẽ là giữ một boolean trong hoạt động cho biết người dùng đã nhấp một lần chưa ...) nhưng tôi tự hỏi liệu đã có thứ gì đó ở đây chưa .

EDIT : Như @LAS_VEGAS đã đề cập, tôi không thực sự có nghĩa là "thoát" theo nghĩa truyền thống. (tức là chấm dứt) Tôi có nghĩa là "quay trở lại bất cứ điều gì đã mở trước khi hoạt động bắt đầu ứng dụng được khởi chạy", nếu điều đó có ý nghĩa :)


[Android - Xác nhận thoát ứng dụng bằng bánh mì nướng] [1]: stackoverflow.com/questions/14006461/ đá
resource8218

1
Tôi gặp vấn đề tương tự khi sử dụng Thư viện HoloEverywhere, đơn giản là bạn có thể thêm android: launchMode = "singleTask" vào định nghĩa hoạt động của bạn trong tệp kê khai.
Sohayb Hassoun


Câu trả lời:


947

Trong hoạt động Java:

boolean doubleBackToExitPressedOnce = false;

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;                       
        }
    }, 2000);
} 

Trong hoạt động của Kotlin:

private var doubleBackToExitPressedOnce = false
override fun onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed()
            return
        }

        this.doubleBackToExitPressedOnce = true
        Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show()

        Handler().postDelayed(Runnable { doubleBackToExitPressedOnce = false }, 2000)
    }

Tôi nghĩ rằng trình xử lý này giúp đặt lại biến sau 2 giây.


43
Câu trả lời tốt nhất! Bạn cũng có thể thêm điều kiện if (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount () = 0!) {Trong trường hợp Fragment-based add
Anton Derevyanko

2
Tôi đồng ý, đây chắc chắn là câu trả lời tốt nhất và nên là câu trả lời được chấp nhận.
BruceHill

3
Bạn nên xóa Runnable khi thoát ứng dụng.
Wayne

Kiểm tra câu trả lời của tôi. Tôi đã sửa đổi câu trả lời được đưa ra bằng cách Sudheesh B Nairbao gồm các ý kiến ​​được đề xuất ở trên.
Mehul Joisar

18
Đó là một giải pháp / câu trả lời nhanh đẹp nhưng tôi không đồng ý rằng đó là giải pháp tốt nhất . Và đối với những người nghĩ rằng đây sẽ là câu trả lời tốt nhất một lần nữa, tôi không thể đồng ý. Những giải pháp này gây rò rỉ và sẽ cần thêm nỗ lực để xử lý. Kiểm tra các aswers dưới đây để biết thêm chi tiết.
Saro Taşciyan

226

Sudheesh B Nair có một câu trả lời hay (và được chấp nhận) cho câu hỏi, mà tôi nghĩ nên có một cách thay thế tốt hơn như;

Có gì sai khi đo thời gian trôi qua và kiểm tra xem TIME_INTERVALmiliseconds (giả sử 2000) đã trôi qua kể từ lần nhấn trở lại cuối cùng. Mã mẫu sau đây sử dụng System.currentTimeMillis();để lưu trữ thời gian onBackPressed()được gọi;

private static final int TIME_INTERVAL = 2000; // # milliseconds, desired time passed between two back presses.
private long mBackPressed;

@Override
public void onBackPressed()
{
    if (mBackPressed + TIME_INTERVAL > System.currentTimeMillis()) 
    { 
        super.onBackPressed(); 
        return;
    }
    else { Toast.makeText(getBaseContext(), "Tap back button in order to exit", Toast.LENGTH_SHORT).show(); }

    mBackPressed = System.currentTimeMillis();
}

Trở lại chấp nhận trả lời phê bình ; Sử dụng một flagđể chỉ ra nếu nó được nhấn ở cuối cùng TIME_INTERVAL(nói 2000) mili giây và thiết lập - reset là thông qua Handler's postDelayed()phương pháp là điều đầu tiên đến trong tâm trí tôi. Nhưng postDelayed()hành động nên được hủy bỏ khi hoạt động đóng cửa, loại bỏ Runnable.

Để xóa Runnable, nó không được khai báo ẩn danh và được khai báo là thành viên cùng với Handleraswell. Sau đó, removeCallbacks()phương pháp củaHandler có thể được gọi là thích hợp.

Mẫu sau đây là trình diễn;

private boolean doubleBackToExitPressedOnce;
private Handler mHandler = new Handler();

private final Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
        doubleBackToExitPressedOnce = false;                       
    }
};

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

    if (mHandler != null) { mHandler.removeCallbacks(mRunnable); }
}

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    mHandler.postDelayed(mRunnable, 2000);
}

Cảm ơn @NSouth đã đóng góp; Để ngăn tin nhắn bánh mì nướng xuất hiện ngay cả sau khi ứng dụng bị đóng, Toastcó thể được khai báo là thành viên - nói mExitToast- và có thể bị hủy qua mExitToast.cancel();ngay trước khi super.onBackPressed();gọi.


9
Đối với những người nghĩ rằng nó giống với những gì Sudheesh B Nair đã nói: Chức năng tương tự, hiệu suất tốt hơn. nên +1.
BedirYilmaz

4
Tôi thích câu trả lời này và tôi nghĩ nó là tốt nhất. Ý tôi là tôi không nghĩ rằng, đó là câu trả lời hay nhất, vì những lý do đã nêu ở trên. Tôi hy vọng bạn nhận được nhiều upvote cho cái này. Mặc dù vậy, có một nhận xét: không ai thấy lạ khi bánh mì nướng vẫn tồn tại vài giây sau khi ứng dụng đóng cửa? Không ai quan tâm để hủy bỏ bánh mì nướng? Tôi biết nó có thể là một chi tiết nhỏ nhưng tôi nghĩ điều đó nên xảy ra. các bạn nghĩ sao?
acrespo

1
@joonty cảm ơn vì đã chỉnh sửa, từ khóa int đó đã bị mất một thời gian. Nó sẽ biên dịch ngay bây giờ (:
Saro Taşciyan

2
@NSouth Khối mã thứ hai là một mẫu sử dụng mHandlers để cho thấy nó đòi hỏi nhiều nỗ lực hơn. Tôi khuyên bạn nên cân nhắc sử dụng khối mã đầu tiên, không sử dụng trình xử lý.
Saro Taşciyan

1
@acrespo Tôi cho rằng chúng ta có một giải pháp vững chắc cho tin nhắn bánh mì nướng vẫn tồn tại sau khi ứng dụng đóng ngay bây giờ.
Saro Taşciyan

30

Chỉ cần nghĩ rằng tôi sẽ chia sẻ làm thế nào tôi đã làm điều đó cuối cùng, tôi chỉ thêm vào hoạt động của mình:

private boolean doubleBackToExitPressedOnce = false;

@Override
protected void onResume() {
    super.onResume();
    // .... other stuff in my onResume ....
    this.doubleBackToExitPressedOnce = false;
}

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }
    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, R.string.exit_press_back_twice_message, Toast.LENGTH_SHORT).show();
}

Và nó chỉ hoạt động chính xác như tôi muốn. Bao gồm cả việc thiết lập lại trạng thái bất cứ khi nào hoạt động được nối lại.


15
Với giải pháp này, hai máy ép ngược có thể có một lượng thời gian tùy ý giữa chúng. Vì vậy, bạn có thể nhấn Backmột lần và sau đó nhấn Backlại một phút sau và ứng dụng sẽ thoát. Đây không phải là hành vi mà người dùng sẽ mong đợi.
BruceHill

1
Tôi nghĩ đó là vấn đề của hương vị. Trong trường hợp này, bạn thông báo cho người dùng chỉ một lần, để người dùng biết anh ta đang ở trong hoạt động chính và một lần nhấn lại khác sẽ thoát khỏi ứng dụng (đáng ngạc nhiên là sau khi người dùng nhấn lại vài lần để quay lại màn hình chính). Nếu, sau đó, người dùng nhấn lại lần nữa, chúng ta có thể cho rằng anh ta muốn thoát khỏi ứng dụng (trừ khi anh ta đã điều hướng vào một hoạt động khác và mất dấu vết đáng kinh ngạc về mức độ anh ta nhận được). Trong câu trả lời được chấp nhận ở trên, bạn cho rằng người dùng có thể quên rằng anh ta đã ở trong màn hình chính. Cả hai đều tốt, tùy thuộc vào những gì bạn muốn hoặc xem xét.
Ferran Maylinch

4
@FerranMaylinch - Tôi không đồng ý. Đây không chỉ đơn thuần là vấn đề của hương vị. Nếu thời gian đáng kể đã trôi qua, chúng ta nên cho rằng người dùng đã thực hiện các hành động khác trong thời gian đó và không còn xem xét những gì mình đã làm trước đó và chọn không tiếp tục áp dụng. Thật vậy, tất cả trừ người dùng đặc biệt nhất thậm chí sẽ không có ý nghĩ rằng trước đây anh ta đã làm điều đó. Không giới hạn thời gian, bạn đã để ứng dụng ở chế độ vô hình mà người dùng không có cách nào biết được. Tôi hoàn toàn coi đó là thiết kế giao diện người dùng kém. Người dùng sẽ ngạc nhiên.
ToolmakerSteve

IMHO, các biến thể tốt hơn về điều này ở đâyở đây .
ToolmakerSteve

26

Sơ đồ quy trình: Nhấn một lần nữa để thoát.

Mã Java:

private long lastPressedTime;
private static final int PERIOD = 2000;

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        switch (event.getAction()) {
        case KeyEvent.ACTION_DOWN:
            if (event.getDownTime() - lastPressedTime < PERIOD) {
                finish();
            } else {
                Toast.makeText(getApplicationContext(), "Press again to exit.",
                        Toast.LENGTH_SHORT).show();
                lastPressedTime = event.getEventTime();
            }
            return true;
        }
    }
    return false;
}

1
Tôi ban đầu đã bỏ phiếu này là hữu ích. Rắc rối là, vì một số lý do, điều này không hoạt động trên một số thiết bị cầm tay. Phương thức onBackPression hoạt động tốt nhất, nhưng sau đó bạn không có dấu thời gian, vì vậy bạn cần các trình xử lý như trạng thái câu trả lời được chấp nhận.
ravemir

23

Có một cách rất đơn giản trong số tất cả những câu trả lời này.

Đơn giản chỉ cần viết mã sau đây bên trong onBackPressed()phương thức.

long back_pressed;

@Override
public void onBackPressed() {
    if (back_pressed + 1000 > System.currentTimeMillis()){
        super.onBackPressed();
    }
    else{
        Toast.makeText(getBaseContext(),
                "Press once again to exit!", Toast.LENGTH_SHORT)
                .show();
    }
    back_pressed = System.currentTimeMillis();
}

Bạn cần xác định back_pressedđối tượng như longtrong hoạt động.


16

Giải pháp của tôi bằng cách sử dụng snackbar:

Snackbar mSnackbar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final LinearLayout layout = findViewById(R.id.layout_main);
    mSnackbar = Snackbar.make(layout, R.string.press_back_again, Snackbar.LENGTH_SHORT);
}

@Override
public void onBackPressed() {
    if (mSnackbar.isShown()) {
        super.onBackPressed();
    } else {
        mSnackbar.show();
    }
}

Đơn giản và phong cách.


2
Cảm ơn giải pháp này. Đơn giản, hiệu quả, không có rủi ro.
ping li

2
Giải pháp ra khỏi hộp. (vỗ tay)
Hitesh Dhamshaniya

13

Dựa trên câu trả lời và gợi ý chính xác trong các bình luận, tôi đã tạo ra một bản demo hoạt động hoàn toàn tốt và loại bỏ các cuộc gọi lại xử lý sau khi được sử dụng.

MainActivity.java

package com.mehuljoisar.d_pressbacktwicetoexit;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.widget.Toast;

public class MainActivity extends Activity {

    private static final long delay = 2000L;
    private boolean mRecentlyBackPressed = false;
    private Handler mExitHandler = new Handler();
    private Runnable mExitRunnable = new Runnable() {

        @Override
        public void run() {
            mRecentlyBackPressed=false;   
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onBackPressed() {

        //You may also add condition if (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount() != 0) // in case of Fragment-based add
        if (mRecentlyBackPressed) {
            mExitHandler.removeCallbacks(mExitRunnable);
            mExitHandler = null;
            super.onBackPressed();
        }
        else
        {
            mRecentlyBackPressed = true;
            Toast.makeText(this, "press again to exit", Toast.LENGTH_SHORT).show();
            mExitHandler.postDelayed(mExitRunnable, delay);
        }
    }

}

Tôi hy vọng nó sẽ hữu ích !!


Bạn có chắc chắn loại bỏ xử lý trong onBackPressed()sửa chữa vấn đề rò rỉ bộ nhớ ?
Saro Taşciyan

@Zefnus: Nó sẽ sửa theo như tôi biết. Đúng nếu tôi đã sai lầm. Làm thế nào bạn theo dõi vấn đề bộ nhớ với mã trên?
Mehul Joisar

11
 public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;
        }
    }, 2000);

Khai báo biếnprivate boolean doubleBackToExitPressedOnce = false;

Dán cái này vào Hoạt động chính của bạn và điều này sẽ giải quyết vấn đề của bạn


10

Không nên sử dụng Runnable khi thoát khỏi ứng dụng, gần đây tôi đã tìm ra một cách đơn giản hơn nhiều để ghi lại và so sánh khoảng thời gian giữa hai lần nhấp nút BACK. Mã mẫu như sau:

private static long back_pressed_time;
private static long PERIOD = 2000;

@Override
public void onBackPressed()
{
        if (back_pressed_time + PERIOD > System.currentTimeMillis()) super.onBackPressed();
        else Toast.makeText(getBaseContext(), "Press once again to exit!", Toast.LENGTH_SHORT).show();
        back_pressed_time = System.currentTimeMillis();
}

Điều này sẽ thực hiện thủ thuật để thoát khỏi ứng dụng bằng cách nhấp vào nút BACK kép trong một khoảng thời gian trì hoãn nhất định là 2000 mili giây trong mẫu.


8

Câu trả lời được chấp nhận là Câu trả lời hay nhất nhưng nếu bạn đang sử dụng Android Design Support Librarythì bạn có thể sử dụng SnackBarcho Chế độ xem tốt hơn.

   boolean doubleBackToExitPressedOnce = false;

    @Override
    public void onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            return;
        }

        this.doubleBackToExitPressedOnce = true;

        Snackbar.make(findViewById(R.id.photo_album_parent_view), "Please click BACK again to exit", Snackbar.LENGTH_SHORT).show();

        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                doubleBackToExitPressedOnce=false;
            }
        }, 2000);
    }

7

Nó không phải là một chức năng được xây dựng. Tôi nghĩ rằng nó thậm chí không phải là hành vi được đề nghị. Các ứng dụng Android không có nghĩa là để thoát:

Tại sao các ứng dụng Android không cung cấp tùy chọn "Thoát"?


Điểm lấy. Khi thoát ra, tôi có nghĩa là "quay lại màn hình chính"
Guillaume

3
Tuy nhiên, nó không phải là một chức năng được xây dựng. Tuy nhiên tôi không biết về bất kỳ hướng dẫn nào chống lại điều này. Là một người dùng Android, tôi thích chức năng như vậy.
Caner

6
  1. Khai báo một biến Toast toàn cầu cho Lớp MainActivity. ví dụ: Toast exit Toast;
  2. Khởi tạo nó trong phương thức xem onCreate. ví dụ: exitToast = Toast.makeText (getApplicationContext (), "Nhấn lại lần nữa để thoát", Toast.LENGTH_SHORT);
  3. Cuối cùng, tạo một onBackPressionMethod dưới dạng Follows:

    @Override
    public void onBackPressed() {
    
        if (exitToast.getView().isShown()) {
            exitToast.cancel();
            finish();
        } else {
            exitToast.show();
        }
    }

Điều này hoạt động chính xác, tôi đã thử nghiệm. và tôi nghĩ rằng điều này là đơn giản hơn nhiều.


5

Câu trả lời của Zefnus khi sử dụng System.civerseTimeMillis () là câu trả lời hay nhất (+1). Cách tôi làm không phải tốt hơn thế, nhưng vẫn đăng nó để thêm vào các ý tưởng trên.

Nếu bánh mì nướng không hiển thị khi nhấn nút quay lại, bánh mì nướng được hiển thị, trong khi đó, nếu nó hiển thị (đã được nhấn một lần trong lần cuối cùng Toast.LENGTH_SHORT), thì nó sẽ thoát.

exitToast = Toast.makeText(this, "Press again to exit", Toast.LENGTH_SHORT);
.
.
@Override
public void onBackPressed() {
   if (exitToast.getView().getWindowToken() == null) //if toast is currently not visible
      exitToast.show();  //then show toast saying 'press againt to exit'
   else {                                            //if toast is visible then
      finish();                                      //or super.onBackPressed();
      exitToast.cancel();
   }
}

1
Là một cách tốt để làm điều này, nhưng nếu bạn có nhiều tin nhắn bánh mì nướng thì không.
Boldijar Paul

@BoldijarPaul Không, mã này chỉ kiểm tra trạng thái của bánh mì nướng cụ thể đó. Vì vậy, bất kỳ bánh mì nướng khác sẽ không ảnh hưởng đến hành vi.
khẩn cấp

5

Gần đây, tôi cần triển khai tính năng nút quay lại này trong một ứng dụng của tôi. Các câu trả lời cho câu hỏi ban đầu rất hữu ích, nhưng tôi phải cân nhắc thêm hai điểm:

  1. Tại một số thời điểm, nút quay lại bị vô hiệu hóa
  2. Hoạt động chính là sử dụng các đoạn kết hợp với ngăn xếp phía sau

Dựa trên các câu trả lời và nhận xét, tôi đã tạo mã sau đây:

private static final long BACK_PRESS_DELAY = 1000;

private boolean mBackPressCancelled = false;
private long mBackPressTimestamp;
private Toast mBackPressToast;

@Override
public void onBackPressed() {
    // Do nothing if the back button is disabled.
    if (!mBackPressCancelled) {
        // Pop fragment if the back stack is not empty.
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            super.onBackPressed();
        } else {
            if (mBackPressToast != null) {
                mBackPressToast.cancel();
            }

            long currentTimestamp = System.currentTimeMillis();

            if (currentTimestamp < mBackPressTimestamp + BACK_PRESS_DELAY) {
                super.onBackPressed();
            } else {
                mBackPressTimestamp = currentTimestamp;

                mBackPressToast = Toast.makeText(this, getString(R.string.warning_exit), Toast.LENGTH_SHORT);
                mBackPressToast.show();
            }
        }
    }
}

Mã ở trên giả định rằng thư viện hỗ trợ được sử dụng. Nếu bạn sử dụng các đoạn nhưng không phải thư viện hỗ trợ, bạn muốn thay thế getSupportFragmentManager()bằng getFragmentManager().

Hủy bỏ đầu tiên if, nếu nút quay lại không bao giờ bị hủy bỏ. Xóa cái thứ hai if, nếu bạn không sử dụng các đoạn hoặc ngăn xếp lại đoạn

Ngoài ra, điều quan trọng cần biết là phương pháp onBackPressednày được hỗ trợ kể từ Android 2.0. Kiểm tra trang này cho một mô tả công phu. Để làm cho tính năng báo chí hoạt động trở lại trên các phiên bản cũ hơn, hãy thêm phương pháp sau vào hoạt động của bạn:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)  {
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR
            && keyCode == KeyEvent.KEYCODE_BACK
            && event.getRepeatCount() == 0) {
        // Take care of calling this method on earlier versions of
        // the platform where it doesn't exist.
        onBackPressed();
    }

    return super.onKeyDown(keyCode, event);
}

5

Trong java

private Boolean exit = false; 

if (exit) {
onBackPressed(); 
}

 @Override
public void onBackPressed() {
    if (exit) {
        finish(); // finish activity
    } else {
        Toast.makeText(this, "Press Back again to Exit.",
                Toast.LENGTH_SHORT).show();
        exit = true;
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                exit = false;
            }
        }, 3 * 1000);

    }
}

Ở kotlin

 private var exit = false

 if (exit) {
        onBackPressed()
         }

 override fun onBackPressed(){
           if (exit){
               finish() // finish activity
           }else{
            Toast.makeText(this, "Press Back again to Exit.",
                    Toast.LENGTH_SHORT).show()
            exit = true
            Handler().postDelayed({ exit = false }, 3 * 1000)

        }
    }

4

Tôi biết đây là một câu hỏi rất cũ, nhưng đây là cách dễ nhất để làm những gì bạn muốn.

@Override
public void onBackPressed() {
   ++k; //initialise k when you first start your activity.
   if(k==1){
      //do whatever you want to do on first click for example:
      Toast.makeText(this, "Press back one more time to exit", Toast.LENGTH_LONG).show();
   }else{
      //do whatever you want to do on the click after the first for example:
      finish(); 
   }
}

Tôi biết đây không phải là phương pháp tốt nhất, nhưng nó hoạt động tốt!


3
Đây không phải là hành vi chung của "nhấp vào nút quay lại hai lần để thoát". Giống như nhận xét của BruceHill về câu trả lời được chấp nhận chỉ ra, câu trả lời của bạn cũng không xử lý vấn đề thời gian
inankupeli

nhưng với điều này, bạn có thể nhấp lại, nó sẽ hiển thị thông báo và sau đó bạn có thể đợi thêm một chút nữa, quay lại lần nữa và nó sẽ đóng ứng dụng và nó sẽ không xử lý hành vi quay ngược thời gian
Gastón Saillén

3

Với mục đích này, tôi đã thực hiện các chức năng sau:

private long onRecentBackPressedTime;
@Override
public void onBackPressed() {
    if (System.currentTimeMillis() - onRecentBackPressedTime > 2000) {
       onRecentBackPressedTime = System.currentTimeMillis();
       Toast.makeText(this, "Please press BACK again to exit", Toast.LENGTH_SHORT).show();
       return;
     }
   super.onBackPressed();
}

3

Điều này cũng giúp khi bạn có hoạt động ngăn xếp trước đó được lưu trữ trong ngăn xếp.

Tôi đã sửa đổi câu trả lời của Sudheesh

boolean doubleBackToExitPressedOnce = false;

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        //super.onBackPressed();

  Intent intent = new Intent(Intent.ACTION_MAIN);
                    intent.addCategory(Intent.CATEGORY_HOME);
                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);//***Change Here***
                    startActivity(intent);
                    finish();
                    System.exit(0);
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;                       
        }
    }, 2000);
} 

2
@Override public void onBackPressed() {
   Log.d("CDA", "onBackPressed Called");
   Intent intent = new Intent();
   intent.setAction(Intent.ACTION_MAIN);
   intent.addCategory(Intent.CATEGORY_HOME);

   startActivity(intent);
}

1
Làm thế nào điều này thậm chí xử lý các kịch bản báo chí đôi? Ngay khi tôi quay trở lại, điều này khởi động một hoạt động.
kilokahn

2

Một phương pháp tốt hơn một chút so với Zefnus tôi nghĩ. Gọi System.cienTimeMillis () chỉ một lần và bỏ qua return;:

long previousTime;

@Override
public void onBackPressed()
{
    if (2000 + previousTime > (previousTime = System.currentTimeMillis())) 
    { 
        super.onBackPressed();
    } else {
        Toast.makeText(getBaseContext(), "Tap back button in order to exit", Toast.LENGTH_SHORT).show();
    }
}

2

Đây là mã làm việc đầy đủ. Và cũng đừng quên xóa các cuộc gọi lại để nó không gây rò rỉ bộ nhớ trong ứng dụng. :)

private boolean backPressedOnce = false;
private Handler statusUpdateHandler;
private Runnable statusUpdateRunnable;

public void onBackPressed() {
        if (backPressedOnce) {
            finish();
        }

        backPressedOnce = true;
        final Toast toast = Toast.makeText(this, "Press again to exit", Toast.LENGTH_SHORT);
        toast.show();

        statusUpdateRunnable = new Runnable() {
            @Override
            public void run() {
                backPressedOnce = false;
                toast.cancel();  //Removes the toast after the exit.
            }
        };

        statusUpdateHandler.postDelayed(statusUpdateRunnable, 2000);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (statusUpdateHandler != null) {
        statusUpdateHandler.removeCallbacks(statusUpdateRunnable);
    }
}

2

Ở đây, tôi đã khái quát viết mã cho số lượng N tap. Mã này được viết tương tự cho tùy chọn Bật nhà phát triển trong điện thoại thiết bị Android. Thậm chí bạn có thể sử dụng điều này để kích hoạt các tính năng trong khi nhà phát triển thử nghiệm ứng dụng.

 private Handler tapHandler;
 private Runnable tapRunnable;
 private int mTapCount = 0;
 private int milSecDealy = 2000;

onCreate(){
 ...
tapHandler = new Handler(Looper.getMainLooper());

 }

Gọi askToExit () trên tùy chọn backpress hoặc logout.

private void askToExit() {
   if (mTapCount >= 2) {
    releaseTapValues();
    /* ========= Exit = TRUE  =========  */
   }

   mTapCount++;
   validateTapCount();
  }


  /* Check with null to avoid create multiple instances of the runnable */
  private void validateTapCount() {
   if (tapRunnable == null) {
    tapRunnable = new Runnable() {
     @Override
     public void run() {
      releaseTapValues();
      /* ========= Exit = FALSE  =========  */
     }
    };
    tapHandler.postDelayed(tapRunnable, milSecDealy);
   }
  }

  private void releaseTapValues() {
   /* Relase the value  */
   if (tapHandler != null) {
    tapHandler.removeCallbacks(tapRunnable);
    tapRunnable = null; /* release the object */
    mTapCount = 0; /* release the value */
   }
  }


  @Override
  protected void onDestroy() {
   super.onDestroy();
   releaseTapValues();
  }

2

Tôi dùng cái này

import android.app.Activity;
import android.support.annotation.StringRes;
import android.widget.Toast;

public class ExitApp {

    private static long lastClickTime;

    public static void now(Activity ctx, @StringRes int message) {
        now(ctx, ctx.getString(message), 2500);
    }

    public static void now(Activity ctx, @StringRes int message, long time) {
        now(ctx, ctx.getString(message), time);
    }

    public static void now(Activity ctx, String message, long time) {
        if (ctx != null && !message.isEmpty() && time != 0) {
            if (lastClickTime + time > System.currentTimeMillis()) {
                ctx.finish();
            } else {
                Toast.makeText(ctx, message, Toast.LENGTH_SHORT).show();
                lastClickTime = System.currentTimeMillis();
            }
        }
    }

}

sử dụng trong sự kiệnonBackPressed

@Override
public void onBackPressed() {
   ExitApp.now(this,"Press again for close");
}

hoặc là ExitApp.now(this,R.string.double_back_pressed)

để thay đổi giây cần đóng, chỉ định milisecond

ExitApp.now(this,R.string.double_back_pressed,5000)


2

Khi HomeActivity chứa ngăn điều hướng và chức năng nhân đôi backPression () để thoát ứng dụng. (Đừng quên khởi tạo biến toàn cục boolean doubleBackToExitPressionOnce = false;) trình xử lý mới sau 2 giây đặt biến doubleBackPressionOnce thành false

@Override
public void onBackPressed() {
    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.END)) {
        drawer.closeDrawer(GravityCompat.END);
    } else {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            moveTaskToBack(true);
            return;
        } else {
            this.doubleBackToExitPressedOnce = true;
            Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    doubleBackToExitPressedOnce = false;
                }
            }, 2000);
        }
    }
}

1
boolean doubleBackToExitPressedOnce = false;

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }

    this.doubleBackToExitPressedOnce = true;

    Snackbar.make(findViewById(R.id.photo_album_parent_view), "Please click BACK again to exit", Snackbar.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;
        }
    }, 2000);
}

1

Một số cải tiến trong câu trả lời của Sudheesh B Nair, tôi đã nhận thấy rằng nó sẽ đợi người xử lý ngay cả khi nhấn lại hai lần ngay lập tức, vì vậy hãy hủy trình xử lý như hình dưới đây. Tôi cũng đã hủy bỏ bánh mì nướng để ngăn nó hiển thị sau khi thoát ứng dụng.

 boolean doubleBackToExitPressedOnce = false;
        Handler myHandler;
        Runnable myRunnable;
        Toast myToast;

    @Override
        public void onBackPressed() {
            if (doubleBackToExitPressedOnce) {
                myHandler.removeCallbacks(myRunnable);
                myToast.cancel();
                super.onBackPressed();
                return;
            }

            this.doubleBackToExitPressedOnce = true;
            myToast = Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT);
            myToast.show();

            myHandler = new Handler();

            myRunnable = new Runnable() {

                @Override
                public void run() {
                    doubleBackToExitPressedOnce = false;
                }
            };
            myHandler.postDelayed(myRunnable, 2000);
        }

1

Đây là cùng một câu trả lời được chấp nhận và được bình chọn nhiều nhất nhưng điều này đã được sử dụng Snackbar thay vì Toast.

boolean doubleBackToExitPressedOnce = false;

    @Override
    public void onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            return;
        }

        this.doubleBackToExitPressedOnce = true;
        Snackbar.make(content, "Please click BACK again to exit", Snackbar.LENGTH_SHORT)
                .setAction("Action", null).show();


        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                doubleBackToExitPressedOnce=false;
            }
        }, 2000);
    }

1

Trong trường hợp của tôi, tôi đã phụ thuộc vào Snackbar#isShown()tốt hơn UX.

private Snackbar exitSnackBar;

@Override
public void onBackPressed() {
    if (isNavDrawerOpen()) {
        closeNavDrawer();
    } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        if (exitSnackBar != null && exitSnackBar.isShown()) {
            super.onBackPressed();
        } else {
            exitSnackBar = Snackbar.make(
                    binding.getRoot(),
                    R.string.navigation_exit,
                    2000
            );
            exitSnackBar.show();
        }
    } else {
        super.onBackPressed();
    }
}

1

Đối với hoạt động đang có Ngăn kéo Điều hướng , Sử dụng mã sau đây cho OnBackPression ()

boolean doubleBackToExitPressedOnce = false;

@Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            if (doubleBackToExitPressedOnce) {
                if (getFragmentManager().getBackStackEntryCount() ==0) {
                    finishAffinity();
                    System.exit(0);
                } else {
                    getFragmentManager().popBackStackImmediate();
                }
                return;
            }

            if (getFragmentManager().getBackStackEntryCount() ==0) {
                this.doubleBackToExitPressedOnce = true;
                Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

                new Handler().postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        doubleBackToExitPressedOnce = false;
                    }
                }, 2000);
            } else {
                getFragmentManager().popBackStackImmediate();
            }
        }
    }

1

Đây là một cách khác ... sử dụng phương thức CountDownTimer

private boolean exit = false;
@Override
public void onBackPressed() {
        if (exit) {
            finish();
        } else {
            Toast.makeText(this, "Press back again to exit",
                    Toast.LENGTH_SHORT).show();
            exit = true;
            new CountDownTimer(3000,1000) {

                @Override
                public void onTick(long l) {

                }

                @Override
                public void onFinish() {
                    exit = false;
                }
            }.start();
        }

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