Làm cách nào để hiển thị hộp thoại để xác nhận rằng người dùng muốn thoát Hoạt động Android?


274

Tôi đã cố gắng thể hiện "Bạn có muốn thoát không?" loại hộp thoại khi người dùng cố gắng thoát khỏi một Hoạt động.

Tuy nhiên tôi không thể tìm thấy các móc API thích hợp. Activity.onUserLeaveHint()Ban đầu có vẻ đầy hứa hẹn, nhưng tôi không thể tìm cách ngăn chặn Hoạt động kết thúc.


39
Có thực sự cần thiết rằng bạn có lời nhắc này? Khi người dùng muốn hoàn thành một Hoạt động, họ sẽ có thể thực hiện ngay lập tức. Bạn có thể muốn suy nghĩ lại về chiến lược của bạn.
Tom R

4
Hiển thị tùy chọn xác nhận thoát là bắt buộc để niêm yết tại Cửa hàng ứng dụng của Samsung. Một trong những Ứng dụng của tôi đã bị từ chối vì không có điều này.
John Ashmore

6
Thật đáng ghét, @Samsung.
dokkaebi

3
Nó phụ thuộc vào những gì bạn muốn làm khi thoát ra. Nếu trạng thái được giữ nguyên và bạn chỉ có thể điều hướng trở lại, hiển thị hộp thoại có thể không cần thiết. Tuy nhiên, trong một trong các ứng dụng của mình, tôi dọn sạch cookie, dữ liệu và đóng các kết nối tốt với một cam kết dữ liệu cuối cùng. Người dùng nên được biết về sự hữu hạn của sự lựa chọn của họ, đặc biệt vì ngày nay không có ý nghĩa về tính hữu hạn trong một ứng dụng di động.
Eric

15
@ Tom R: Hoàn toàn không đồng ý. Có những ứng dụng mà bạn không muốn hoàn thành một cách tình cờ. Họ làm việc trong môi trường kinh doanh và như vậy.
TomeeNS

Câu trả lời:


390

Trong Android 2.0 trở lên, nó sẽ trông như sau:

@Override
public void onBackPressed() {
    new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle("Closing Activity")
        .setMessage("Are you sure you want to close this activity?")
        .setPositiveButton("Yes", new DialogInterface.OnClickListener()
    {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            finish();    
        }

    })
    .setNegativeButton("No", null)
    .show();
}

Trong các phiên bản trước, nó sẽ trông như:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    //Handle the back button
    if(keyCode == KeyEvent.KEYCODE_BACK) {
        //Ask the user if they want to quit
        new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle(R.string.quit)
        .setMessage(R.string.really_quit)
        .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {

                //Stop the activity
                YourClass.this.finish();    
            }

        })
        .setNegativeButton(R.string.no, null)
        .show();

        return true;
    }
    else {
        return super.onKeyDown(keyCode, event);
    }

}

15
Cũng trong phiên bản 2.0 trở lên, có một sự kiện onBackPression mới được đề xuất trên nhà phát triển onKeyDown.android.com/intl/zh-TW/reference/android/app/. Có một phần ở đây nói về những thay đổi và cách tiếp cận mới được đề xuất. developer.android.com/intl/zh-TW/sdk/android-2.0.html
Patrick Kafka

3
Bài đăng trên blog về việc bắt phím quay lại tại đây: android-developers.blogspot.com/2009/12/ trên Lưu ý rằng điều này không cho phép bạn nắm bắt các cách khác mà người dùng có thể rời khỏi ứng dụng của bạn: nhấn nhà, chọn thông báo, nhận điện thoại gọi, v.v.
hackbod

Điều này hoạt động hoàn hảo nhưng làm thế nào để bạn buộc thực thi mã dừng lại trong khi nó được hiển thị cho người dùng?
anon58192932

tìm thấy nó, đây là một giải pháp tuyệt vời ở đây: stackoverflow.com/questions/4381296/
triệt

1
Đây là phương thức () những gì tôi đang tìm kiếm nhưng tôi không biết nên gọi phương thức này ở đâu ().
dùng2841300

186
@Override
public void onBackPressed() {
    new AlertDialog.Builder(this)
           .setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                   ExampleActivity.super.onBackPressed();
               }
           })
           .setNegativeButton("No", null)
           .show();
}

5
Một nhận xét đã được đưa ra bởi elBradford: Gọi siêu onBackPression sẽ tốt hơn so với giả sử onBackPression sẽ chỉ gọi kết thúc (). Ngay cả khi điều đó là đúng bây giờ, nó có thể không đúng trong các API trong tương laiCustomTabActivity.super.onBackPressed
mplungjan

@mplungjan Bạn có thể làm rõ nhận xét của mình không ... Bạn có nói thay thế finish()mã bằng tốt hơn super.onBackPressed()không?
ban-geengineering

Nhận xét là 3 tuổi. Tôi không biết thế nào là thực hành tốt trong năm 2015
mplungjan

@mplungjan Nhận xét của bạn đề cập đến các API trong tương lai, nhưng sẽ hữu ích nếu bạn có thể làm rõ bằng mọi cách.
ban-geengineering

Không có probs. Vâng, tôi đã thử MyActivity.super.onBackPressed();và nó hoạt động tốt với tôi. Dường như cũng là cách tiếp cận hợp lý nhất - để gọi phương thức bạn đang ghi đè nếu bạn muốn hành vi chuẩn khi người dùng nhấn "Không".
ban-geengineering

33

Đã sửa đổi mã @ user919216 .. và làm cho nó tương thích với WebView

@Override
public void onBackPressed() {
    if (webview.canGoBack()) {
        webview.goBack();

    }
    else
    {
     AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                finish();
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
AlertDialog alert = builder.create();
alert.show();
    }

}

22

Tôi muốn thoát ra bằng cách chạm hai lần vào nút quay lại hơn với Hộp thoại thoát.

Trong giải pháp này, nó hiển thị bánh mì nướng khi quay lại lần đầu tiên, cảnh báo rằng một lần nhấn trở lại khác sẽ đóng Ứng dụng. Trong ví dụ này ít hơn 4 giây.

private Toast toast;
private long lastBackPressTime = 0;

@Override
public void onBackPressed() {
  if (this.lastBackPressTime < System.currentTimeMillis() - 4000) {
    toast = Toast.makeText(this, "Press back again to close this app", 4000);
    toast.show();
    this.lastBackPressTime = System.currentTimeMillis();
  } else {
    if (toast != null) {
    toast.cancel();
  }
  super.onBackPressed();
 }
}

Mã thông báo từ: http://www.androiduipotypes.com/2011/03/back-button-behavior.html


Cái này chắc chắn là yêu thích của tôi vì Hộp thoại có thể cồng kềnh. Tôi thay đổi nó một chút để làm cho nó 3 giây và nó có vẻ tự nhiên hơn một chút với điểm đánh dấu đó. Cảm ơn vì bài đăng.
PGMacDesign

khi quay lại, nhấn ứng dụng lý tưởng để tôi thoát nhưng trong mã của tôi, nó sẽ mở một hoạt động đăng nhập (có nghĩa là hoạt động trở lại với công cụ quay vòng được kích hoạt)
techDigi

21

Nếu bạn không chắc chắn liệu cuộc gọi "quay lại" sẽ thoát khỏi ứng dụng hay sẽ đưa người dùng đến một hoạt động khác, bạn có thể gói các câu trả lời trên trong một kiểm tra, isTaskRoot (). Điều này có thể xảy ra nếu hoạt động chính của bạn có thể được thêm vào ngăn xếp phía sau nhiều lần hoặc nếu bạn đang thao túng lịch sử ngăn xếp phía sau của mình.

if(isTaskRoot()) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                YourActivity.super.onBackPressed;
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
    AlertDialog alert = builder.create();
    alert.show();

} else {
    super.onBackPressed();
}

5

Sử dụng Lambda:

    new AlertDialog.Builder(this).setMessage(getString(R.string.exit_msg))
        .setTitle(getString(R.string.info))
        .setPositiveButton(getString(R.string.yes), (arg0, arg1) -> {
            moveTaskToBack(true);
            finish();
        })
        .setNegativeButton(getString(R.string.no), (arg0, arg1) -> {
        })
        .show();

Bạn cũng cần đặt ngôn ngữ cấp để hỗ trợ java 8 trong gradle.build:

compileOptions {
       targetCompatibility 1.8
       sourceCompatibility 1.8
}

5

tại Trung Quốc, hầu hết các ứng dụng sẽ xác nhận lối ra bằng cách "nhấp hai lần":

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

5

Đầu tiên loại bỏ super.onBackPressed();khỏi onbackPressed()phương thức hơn và dưới mã:

@Override
public void onBackPressed() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    MyActivity.this.finish();
               }
           })
           .setNegativeButton("No", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
               }
           });
    AlertDialog alert = builder.create();
    alert.show();

}

2

Tôi thích cách tiếp cận @GLee và sử dụng nó với đoạn như bên dưới.

@Override
public void onBackPressed() {
    if(isTaskRoot()) {
        new ExitDialogFragment().show(getSupportFragmentManager(), null);
    } else {
        super.onBackPressed();
    }
}

Hộp thoại sử dụng Fragment:

public class ExitDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.exit_question)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getActivity().finish();
                }
            })
            .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getDialog().cancel();
                }
            })
            .create();
    }
}

2
Just put this code in your first activity 

@Override
    public void onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
            drawerLayout.closeDrawer(GravityCompat.END);
        }
        else {
// if your using fragment then you can do this way
            int fragments = getSupportFragmentManager().getBackStackEntryCount();
            if (fragments == 1) {
new AlertDialog.Builder(this)
           .setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    finish();
               }
           })
           .setNegativeButton("No", null)
           .show();


            } else {
                if (getFragmentManager().getBackStackEntryCount() > 1) {
                    getFragmentManager().popBackStack();
                } else {

           super.onBackPressed();
                }
            }
        }
    }

y hoạt động đầu tiên, tôi không thể đặt nó vào hoạt động ở giữa. bởi vì bây giờ nó đã đóng hoạt động hiện tại và nó hiển thị hoạt động trước đó
shareef

1

Một cách khác là hiển thị a Toast/ Snackbartrên lần nhấn đầu tiên, yêu cầu nhấn lại lần nữa để Thoát , điều này ít xâm phạm hơn nhiều so với hiển thị AlertDialogđể xác nhận nếu người dùng muốn thoát khỏi ứng dụng.

Bạn có thể sử dụng DoubleBackPress Android Libraryđể đạt được điều này với một vài dòng mã. Ví dụ GIF cho thấy hành vi tương tự.

Để bắt đầu, hãy thêm phụ thuộc vào ứng dụng của bạn:

dependencies {
    implementation 'com.github.kaushikthedeveloper:double-back-press:0.0.1'
}

Tiếp theo, trong Hoạt động của bạn, thực hiện các hành vi cần thiết.

// set the Toast to be shown on FirstBackPress (ToastDisplay - builtin template)
// can be replaced by custom action (new FirstBackPressAction{...})
FirstBackPressAction firstBackPressAction = new ToastDisplay().standard(this);

// set the Action on DoubleBackPress
DoubleBackPressAction doubleBackPressAction = new DoubleBackPressAction() {
    @Override
    public void actionCall() {
        // TODO : Exit the application
        finish();
        System.exit(0);
    }
};

// setup DoubleBackPress behaviour : close the current Activity
DoubleBackPress doubleBackPress = new DoubleBackPress()
        .withDoublePressDuration(3000)     // msec - wait for second back press
        .withFirstBackPressAction(firstBackPressAction)
        .withDoubleBackPressAction(doubleBackPressAction);

Cuối cùng, thiết lập điều này như hành vi trên báo chí trở lại.

@Override
public void onBackPressed() {
    doubleBackPress.onBackPressed();
}
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.