คลิกปุ่มย้อนกลับสองครั้งเพื่อออกจากกิจกรรม


330

ฉันสังเกตเห็นรูปแบบนี้ในแอพและเกม Android จำนวนมากเมื่อไม่นานมานี้: เมื่อคลิกปุ่มย้อนกลับเพื่อ "ออก" แอปพลิเคชันToastจะมีข้อความคล้ายกับ "โปรดคลิกที่ BACK อีกครั้งเพื่อออก"

ฉันสงสัยว่าเมื่อฉันเห็นบ่อยขึ้นเป็นคุณสมบัติที่มีอยู่ในตัวที่คุณสามารถเข้าถึงกิจกรรมได้หรือไม่? ฉันได้ดูซอร์สโค้ดของหลาย ๆ คลาสแล้ว แต่ฉันไม่สามารถหาอะไรได้เลย

แน่นอนฉันสามารถคิดได้สองสามวิธีเพื่อให้ได้ฟังก์ชั่นเดียวกันค่อนข้างง่าย (ที่ง่ายที่สุดน่าจะเป็นบูลีนในกิจกรรมที่ระบุว่าผู้ใช้คลิกครั้งเดียวแล้ว ... ) แต่ฉันสงสัยว่ามีบางอย่างอยู่ที่นี่แล้ว .

แก้ไข : ตามที่ @LAS_VEGAS พูดถึงฉันไม่ได้หมายถึง "ทางออก" ตามความหมายดั้งเดิม (เช่นถูกยกเลิก) ฉันหมายถึง "กลับไปยังสิ่งที่เปิดอยู่ก่อนที่กิจกรรมการเริ่มต้นแอปพลิเคชันจะเปิดตัว" ถ้านั่นสมเหตุสมผล :)


[Android - ยืนยันการออกจากแอพด้วยขนมปังปิ้ง] [1]: stackoverflow.com/questions/14006461/…
resource8218

1
ฉันมีปัญหาเดียวกันเมื่อใช้ HoloEverywhere Library เช่นกันคุณสามารถเพิ่ม android: launchMode = "singleTask" ให้กับนิยามกิจกรรมของคุณในไฟล์รายการ
Sohayb Hassoun


คำตอบ:


947

ในกิจกรรม 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);
} 

ในกิจกรรม 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)
    }

ฉันคิดว่าตัวจัดการนี้ช่วยในการรีเซ็ตตัวแปรหลังจาก 2 วินาที


43
คำตอบที่ดีที่สุด! นอกจากนี้คุณยังสามารถเพิ่มเงื่อนไขได้หาก (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount ()! = 0) {ในกรณีของ Add-based ที่ใช้ชิ้นส่วน
Anton Derevyanko

2
ฉันเห็นด้วยนี่เป็นคำตอบที่ดีที่สุดแน่นอนและควรเป็นคำตอบที่ยอมรับได้
BruceHill

3
คุณควรลบ Runnable เมื่อออกจากแอปพลิเคชัน
Wayne

ตรวจสอบคำตอบของฉันฉันได้แก้ไขคำตอบที่ได้รับจากSudheesh B Nairเพื่อครอบคลุมความคิดเห็นที่แนะนำข้างต้น
Mehul Joisar

18
มันเป็นความสุขอย่างรวดเร็วแก้ปัญหา / คำตอบ แต่ผมไม่เห็นว่ามันเป็นทางออกที่ดีที่สุด และสำหรับคนที่คิดว่านี่จะเป็นคำตอบที่ดีที่สุด อีกครั้งฉันก็ไม่เห็นด้วย วิธีแก้ปัญหาเหล่านี้ทำให้เกิดการรั่วไหลและต้องใช้ความพยายามเป็นพิเศษในการจัดการ ตรวจสอบaswersด้านล่างสำหรับรายละเอียดเพิ่มเติม
Saro Taşciyan

226

Sudheesh B Nairมีคำตอบที่ดี (และยอมรับ) ในคำถามซึ่งฉันคิดว่าควรมีทางเลือกที่ดีกว่าเช่น;

มีอะไรผิดปกติกับการวัดเวลาที่ผ่านไปและการตรวจสอบว่ามีTIME_INTERVALหน่วยเป็นมิลลิวินาที (บอกว่า 2000) ผ่านไปนับตั้งแต่การกดครั้งสุดท้าย โค้ดตัวอย่างต่อไปนี้ใช้System.currentTimeMillis();เพื่อเก็บเวลาที่onBackPressed()เรียกว่า

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

กลับไปที่คำตอบที่ได้รับการยอมรับคำวิจารณ์ ; ใช้flagเพื่อแสดงให้เห็นว่ามันถูกกดในช่วงTIME_INTERVAL(พูด 2000) มิลลิวินาทีและชุด - การตั้งค่าผ่านทางHandler's postDelayed()วิธีเป็นสิ่งแรกที่จะมาในใจของฉัน แต่การpostDelayed()ดำเนินการควรถูกยกเลิกเมื่อกิจกรรมปิดทำการลบRunnableการดำเนินการควรจะยกเลิกเมื่อมีกิจกรรมที่กำลังจะปิดลบ

เพื่อที่จะลบRunnableมันจะต้องไม่ถูกเปิดเผยโดยไม่ระบุตัวตนและจะถูกประกาศให้เป็นสมาชิกพร้อมด้วยHandlerเช่นกัน removeCallbacks()วิธีการนั้นHandlerสามารถเรียกได้อย่างเหมาะสม

ตัวอย่างต่อไปนี้เป็นการสาธิต;

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

ขอบคุณ @NSouth ที่ให้ความช่วยเหลือ เพื่อป้องกันไม่ให้ข้อความของขนมปังปิ้งปรากฏขึ้นแม้ว่าจะปิดแอปพลิเคชันแล้วToastคุณสามารถประกาศเป็นสมาชิก - พูดmExitToast- และสามารถยกเลิกได้mExitToast.cancel();ก่อนการsuper.onBackPressed();โทร


9
สำหรับคนที่คิดว่ามันเป็นสิ่งที่ Sudheesh B Nair กล่าวไว้: ฟังก์ชั่นเดียวกันประสิทธิภาพที่ดีขึ้น ดังนั้น +1
BedirYilmaz

4
ฉันชอบคำตอบนี้และฉันคิดว่ามันดีที่สุด ฉันหมายความว่าฉันไม่คิดว่ามันเป็นคำตอบที่ดีที่สุดด้วยเหตุผลที่กล่าวไว้ข้างต้น ฉันหวังว่าคุณจะได้รับ upvotes มากขึ้นสำหรับคนนี้ แม้ว่าความคิดเห็นหนึ่ง: ไม่มีใครพบว่ามันแปลกที่ขนมปังยังคงอยู่สองสามวินาทีหลังจากที่แอปปิด? ไม่มีใครใส่ใจที่จะยกเลิกขนมปัง? ฉันรู้ว่ามันอาจเป็นรายละเอียดเล็ก ๆ แต่ฉันคิดว่าน่าจะเกิดขึ้น พวกคุณคิดอย่างไร
acrespo

1
@ joonty ขอบคุณสำหรับการแก้ไขคำหลักintนั้นขาดหายไประยะหนึ่ง ตอนนี้จะรวบรวม (:
Saro Taşciyan

2
@NSouth บล็อคโค้ดที่สองเป็นตัวอย่างโดยใช้ mHandlers เพื่อแสดงว่าต้องใช้ความพยายามมากขึ้น ฉันขอแนะนำให้คุณพิจารณาใช้บล็อกโค้ดแรกซึ่งไม่ใช้ตัวจัดการ
Saro Taşciyan

1
@acrespo ฉันคิดว่าเรามีโซลูชันที่มั่นคงสำหรับข้อความของขนมปังปิ้งที่ยังคงมีอยู่หลังจากที่ปิดแอปพลิเคชันในขณะนี้
Saro Taşciyan

30

แค่คิดว่าฉันจะแบ่งปันวิธีที่ฉันทำในท้ายที่สุดฉันเพิ่งเพิ่มในกิจกรรมของฉัน:

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

และมันก็ใช้งานได้ตามที่ฉันต้องการ รวมถึงการรีเซ็ตสถานะเมื่อใดก็ตามที่กิจกรรมกลับมาทำงานต่อ


15
ด้วยวิธีการนี้การกดกลับสองครั้งสามารถมีระยะเวลาได้โดยพลการ ดังนั้นคุณสามารถกดBackหนึ่งครั้งแล้วกดBackอีกครั้งในภายหลังและแอปพลิเคชันจะออก นี่ไม่ใช่พฤติกรรมที่ผู้ใช้คาดหวัง
BruceHill

1
ฉันคิดว่ามันเป็นเรื่องของรสนิยม ในกรณีนี้คุณแจ้งผู้ใช้เพียงครั้งเดียวเพื่อให้ผู้ใช้รู้ว่าเขาอยู่ในกิจกรรมหลักและการกดกลับอีกครั้งจะออกจากแอป (น่าจะเป็นหลังจากที่ผู้ใช้กดสองสามครั้งเพื่อกลับไปที่หน้าจอหลัก) หากในภายหลังผู้ใช้กดกลับมาอีกครั้งเราสามารถสมมติว่าเขาต้องการออกจากแอพ (เว้นแต่เขาจะสำรวจไปยังกิจกรรมอื่นและอาจสูญเสียการติดตามว่าเขาลึกแค่ไหน) ในคำตอบที่ยอมรับข้างต้นคุณพิจารณาว่าผู้ใช้อาจลืมว่าเขาอยู่ในหน้าจอหลักแล้ว ทั้งคู่ใช้ได้ดีขึ้นอยู่กับสิ่งที่คุณต้องการหรือพิจารณา
Ferran Maylinch

4
@FerranMaylinch - ฉันไม่เห็นด้วย นี่ไม่ใช่แค่เรื่องของรสนิยม หากเวลาผ่านไปอย่างมีนัยสำคัญเราควรสมมติว่าผู้ใช้ได้กระทำการอื่นในระหว่างนี้และไม่ได้พิจารณาสิ่งที่เขาทำก่อนหน้านี้อีกต่อไปและเลือกที่จะไม่ดำเนินการสมัครต่อไป อันที่จริงทั้งหมด แต่ผู้ใช้ที่ยอดเยี่ยมที่สุดจะยังไม่ได้มีในใจของเขาว่าเขาทำอย่างนั้น โดยไม่ จำกัด เวลาคุณได้ออกจากแอพในโหมดล่องหนที่ผู้ใช้ไม่มีทางรู้ ฉันคิดว่าจะออกแบบส่วนต่อประสานกับผู้ใช้ไม่ดี ผู้ใช้จะประหลาดใจ
ToolmakerSteve

IMHO ดีกว่าสายพันธุ์เกี่ยวกับเรื่องนี้ที่นี่และที่นี่
ToolmakerSteve

26

แผนภาพผังกระบวนการ: กดอีกครั้งเพื่อออก

รหัส 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
ฉันเริ่มโหวตว่าสิ่งนี้มีประโยชน์ ปัญหาคือด้วยเหตุผลบางอย่างมันไม่สามารถใช้ได้กับมือถือบางรุ่น วิธีการ onBackPressed ทำงานได้ดีที่สุด แต่คุณไม่มีเวลาประทับดังนั้นคุณต้องใช้ตัวจัดการเนื่องจากสถานะคำตอบที่ยอมรับ
ravemir

23

มีวิธีที่ง่ายที่สุดในบรรดาคำตอบทั้งหมด

เพียงแค่เขียนรหัสต่อไปนี้ภายในonBackPressed()วิธีการ

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

คุณต้องกำหนดback_pressedวัตถุเช่นเดียวกับlongในกิจกรรม


16

วิธีแก้ปัญหาของฉันโดยใช้สแน็คบาร์:

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

เรียบง่ายและมีสไตล์


2
ขอบคุณสำหรับการแก้ปัญหานี้ ง่ายมีประสิทธิภาพไม่มีความเสี่ยง
ping li

2
ทางออกของกล่อง (ตบมือ)
Hitesh Dhamshaniya

13

ตามคำตอบและคำแนะนำที่ถูกต้องในความคิดเห็นฉันได้สร้างตัวอย่างที่ทำงานได้ดีอย่างสมบูรณ์และลบการเรียกกลับตัวจัดการหลังจากใช้งาน

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

}

ฉันหวังว่ามันจะเป็นประโยชน์ !!


คุณแน่ใจว่าจะลบตัวจัดการในการonBackPressed()แก้ไขปัญหาการรั่วไหลของหน่วยความจำหรือไม่?
Saro Taşciyan

@ Zefnus: มันจะแก้ไขเท่าที่ฉันรู้ ถูกต้องฉันถ้าฉันผิด คุณติดตามปัญหาหน่วยความจำด้วยรหัสด้านบนได้อย่างไร
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);

ประกาศตัวแปรprivate boolean doubleBackToExitPressedOnce = false;

วางสิ่งนี้ลงในกิจกรรมหลักของคุณและจะช่วยแก้ปัญหาของคุณ


10

ไม่ใช่ความคิดที่ดีที่จะใช้ Runnable เมื่อออกจากแอปพลิเคชันเมื่อเร็ว ๆ นี้ฉันคิดหาวิธีที่ง่ายกว่ามากในการบันทึกและเปรียบเทียบช่วงเวลาระหว่างการคลิกปุ่ม BACK สองครั้ง โค้ดตัวอย่างดังต่อไปนี้:

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

วิธีนี้จะทำเคล็ดลับในการออกจากแอปพลิเคชันด้วยการคลิกสองครั้งที่ปุ่มย้อนกลับภายในระยะเวลาหน่วงเวลาที่แน่นอนซึ่งเป็น 2,000 มิลลิวินาทีในตัวอย่าง


8

คำตอบที่ได้รับการยอมรับนั้นดีที่สุด แต่ถ้าคุณใช้อยู่Android Design Support Libraryคุณสามารถใช้SnackBarเพื่อการชมที่ดีกว่าได้

   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

มันไม่ใช่ฟังก์ชั่นในตัว ฉันคิดว่ามันไม่ได้เป็นพฤติกรรมที่แนะนำ แอพ Android ไม่ได้มีไว้เพื่อออก:

ทำไมแอปพลิเคชัน Android ไม่ให้ตัวเลือก "ออก"


จุดที่ถ่าย เมื่อออกจากฉันหมายถึง "กลับไปที่หน้าจอหลัก"
Guillaume

3
แต่ถึงกระนั้นมันก็ไม่ได้เป็นฟังก์ชั่นในตัว อย่างไรก็ตามฉันไม่รู้แนวทางใด ๆ ในเรื่องนี้ ในฐานะผู้ใช้ Android ฉันชอบฟังก์ชั่นดังกล่าว
Caner

6
  1. ประกาศตัวแปร Toast ทั่วโลกสำหรับ MainActivity Class ตัวอย่าง: Toast exitToast;
  2. เริ่มต้นได้ในวิธีการสร้างมุมมอง onCreate ตัวอย่าง: exitToast = Toast.makeText (getApplicationContext (), "กดกลับอีกครั้งเพื่อออก", Toast.LENGTH_SHORT);
  3. ในที่สุดสร้าง onBackPressedMethod ดังต่อไปนี้:

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

ทำงานได้ถูกต้องฉันได้ทดสอบแล้ว และฉันคิดว่านี่ง่ายกว่ามาก


5

คำตอบของZefnus ที่ใช้ System.currentTimeMillis () เป็นคำตอบที่ดีที่สุด (+1) วิธีที่ฉันทำไม่ดีไปกว่านั้น แต่ก็ยังโพสต์ไว้เพื่อเพิ่มไปยังแนวคิดข้างต้น

หากมองไม่เห็นขนมปังปิ้งเมื่อกดปุ่มย้อนกลับขนมปังจะปรากฏขึ้นในขณะที่หากมองเห็นได้ (กลับถูกกดหนึ่งครั้งภายในระยะToast.LENGTH_SHORTเวลาที่ผ่านมา) จากนั้นจะออก

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
เป็นวิธีที่ดีในการทำเช่นนี้ แต่ถ้าคุณมีขนมปังปิ้งข้อความเพิ่มเติมไม่ได้
Boldijar Paul

@BoldijarPaul ไม่รหัสนี้จะตรวจสอบสถานะของขนมปังเฉพาะนั้นเท่านั้น ดังนั้นขนมปังอื่น ๆ จะไม่ส่งผลกระทบต่อพฤติกรรม
เร่งด่วน

5

เมื่อเร็ว ๆ นี้ฉันต้องการใช้คุณลักษณะปุ่มย้อนกลับนี้ในแอปของฉัน คำตอบของคำถามเดิมนั้นมีประโยชน์ แต่ฉันต้องพิจารณาอีกสองประเด็น:

  1. ในบางจุดปุ่มย้อนกลับถูกปิดใช้งาน
  2. กิจกรรมหลักคือการใช้ชิ้นส่วนร่วมกับกองหลัง

ฉันสร้างรหัสต่อไปนี้ตามคำตอบและความคิดเห็น

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

รหัสด้านบนจะถือว่ามีการใช้ไลบรารีการสนับสนุน หากคุณใช้แฟรกเมนต์ แต่ไม่ใช่ไลบรารีการสนับสนุนคุณต้องการแทนที่getSupportFragmentManager()โดยgetFragmentManager()โดย

ลบปุ่มแรกifหากปุ่มย้อนกลับไม่เคยถูกยกเลิก ลบที่สองifหากคุณไม่ได้ใช้แฟรกเมนต์หรือสแต็กแฟรกเมนต์ด้านหลัง

นอกจากนี้ยังเป็นสิ่งสำคัญที่จะต้องทราบว่าวิธีการonBackPressedรองรับตั้งแต่ Android 2.0 ตรวจสอบหน้านี้เพื่อดูคำอธิบายอย่างละเอียด ในการทำให้คุณสมบัติการกดย้อนกลับทำงานบนเวอร์ชันเก่าเช่นกันให้เพิ่มวิธีการต่อไปนี้ในกิจกรรมของคุณ:

@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

ในภาษาจาวา

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

ฉันรู้ว่านี่เป็นคำถามที่เก่ามาก แต่นี่เป็นวิธีที่ง่ายที่สุดในการทำสิ่งที่คุณต้องการ

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

ฉันรู้ว่านี่ไม่ใช่วิธีที่ดีที่สุด แต่ใช้งานได้ดี!


3
นี่ไม่ใช่พฤติกรรมทั่วไปของ "คลิกปุ่มย้อนกลับสองครั้งเพื่อออก" เช่นเดียวกับความคิดเห็นของ BruceHill เกี่ยวกับคำตอบที่ได้รับการยอมรับชี้ให้เห็นว่าคำตอบของคุณไม่ได้จัดการกับปัญหาเวลาเช่นกัน
inankupeli

แต่ด้วยสิ่งนี้คุณสามารถคลิกย้อนกลับมันจะแสดงข้อความและจากนั้นคุณสามารถรออีกเล็กน้อยกลับไปอีกครั้งและมันจะปิดแอพและจะไม่จัดการกับพฤติกรรมของการย้อนเวลากลับ
GastónSaillén

3

เพื่อจุดประสงค์นี้ฉันได้ใช้งานฟังก์ชั่นต่อไปนี้:

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

สิ่งนี้จะช่วยเมื่อคุณมีกิจกรรมสแต็กก่อนหน้านี้ถูกเก็บไว้ในสแต็ก

ฉันแก้ไขคำตอบของ 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
สิ่งนี้จะจัดการกับสถานการณ์กดสองครั้งได้อย่างไร ทันทีที่ฉันกลับมาสิ่งนี้จะเปิดตัวกิจกรรม
kilokahn

2

วิธีที่ดีกว่าเล็กน้อยฉันคิดว่าZefnus โทร System.currentTimeMillis () เพียงครั้งเดียวและละเว้น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

นี่คือรหัสการทำงานที่สมบูรณ์ และอย่าลืมลบการโทรกลับเพื่อไม่ให้หน่วยความจำรั่วในแอพ :)

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

ที่นี่ฉันมีการเขียนรหัสทั่วไปสำหรับการนับ N แตะ รหัสนี้เขียนขึ้นสำหรับตัวเลือก Enable Developer ในโทรศัพท์ Android แม้คุณสามารถใช้สิ่งนี้เพื่อเปิดใช้งานฟีเจอร์ในขณะที่นักพัฒนาทำการทดสอบแอพ

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

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

 }

โทรหาaskToExit ()จากตัวเลือก backpress หรือ 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

ฉันใช้สิ่งนี้

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

}

ใช้เพื่อในกรณีonBackPressed

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

หรือ ExitApp.now(this,R.string.double_back_pressed)

สำหรับวินาทีการเปลี่ยนแปลงจำเป็นต้องปิดและระบุเป็นมิลลิวินาที

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


2

เมื่อ HomeActivity มีลิ้นชักการนำทางและ funtion backPressed () สองครั้งเพื่อออกจากแอป (อย่าลืมที่จะเริ่มต้นตัวแปรส่วนกลางบูลีน doubleBackToExitPressedOnce = false;) ตัวจัดการใหม่หลังจาก 2 วินาทีตั้งค่าตัวแปร doubleBackPressedOnce เป็นเท็จ

@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

การปรับปรุงบางอย่างในคำตอบของ Sudheesh B Nair ฉันได้สังเกตเห็นว่ามันจะรอผู้จัดการแม้ในขณะที่กดปุ่มกลับสองครั้งในทันทีดังนั้นยกเลิกตัวจัดการดังที่แสดงด้านล่าง ฉันได้ยกเลิกขนมปังปิ้งด้วยเพื่อป้องกันไม่ให้แสดงหลังจากแอปออก

 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

นี่เป็นสิ่งเดียวกับที่ได้รับการยอมรับและได้รับการโหวตมากที่สุด แต่ snipped นี้ใช้ Snackbar แทน 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

ในกรณีของฉันฉันได้ขึ้นอยู่กับการที่ดีขึ้นSnackbar#isShown()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

สำหรับกิจกรรมที่มีการนำทางลิ้นชักให้ใช้รหัสต่อไปนี้สำหรับ OnBackPressed ()

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

นี่เป็นอีกวิธีหนึ่ง ... โดยใช้วิธีการ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();
        }

    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.