วิธีการใช้งาน onBackPressed () ในแฟรกเมนต์


459

มีวิธีที่เราสามารถนำไปใช้onBackPressed()ใน Android Fragment คล้ายกับวิธีที่เราใช้ใน Android Activity หรือไม่?

onBackPressed()ในฐานะที่เป็นวงจรชีวิตส่วนไม่ได้ มีวิธีอื่นในการขี่มากกว่าonBackPressed()ใน Android 3.0 แฟรกเมนต์หรือไม่?


13
IMHO ชิ้นส่วนไม่ควรรู้และไม่สนใจปุ่ม BACK กิจกรรมสามารถสนใจปุ่ม BACK ได้แม้ว่าจะมีแฟรกเมนต์ที่ FragmentManager จัดการอยู่ก็ตาม แต่เนื่องจากแฟรกเมนต์ไม่ทราบเกี่ยวกับสภาพแวดล้อมที่มีขนาดใหญ่ขึ้น (เช่นไม่ว่าจะเป็นหนึ่งในหลาย ๆ กิจกรรม) จึงไม่ปลอดภัยสำหรับชิ้นส่วนที่จะพยายามกำหนดฟังก์ชันการทำงานของแบ็คที่เหมาะสม
CommonsWare

61
แล้วเมื่อไหร่ที่แฟรกเมนต์ทั้งหมดแสดงเป็น WebView และคุณต้องการให้ WebView "ย้อนกลับ" ไปยังหน้าก่อนหน้าเมื่อกดปุ่มย้อนกลับ?
mharper

คำตอบ Michael Herbig นั้นสมบูรณ์แบบสำหรับฉัน แต่สำหรับผู้ที่ไม่สามารถใช้ getSupportFragmentManager () ใช้ getFragmentManager () แทน
Dantalian

คำตอบของ Dmitry Zaitsev สำหรับ [คำถามที่คล้ายกัน] [1] ใช้ได้ดี [1]: stackoverflow.com/a/13450668/1372866
ashakirov

6
เพื่อตอบ mharper คุณสามารถทำอะไรเช่น webview.setOnKeyListener (ใหม่ OnKeyListener () {@Override บูลีสาธารณะสาธารณะ onKey (ดู v, int keyCode, KeyEvent event) {ถ้า (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack () {webview.goBack (); return true;} return false;}});
Omid Aminiva

คำตอบ:


307

ฉันแก้ไขด้วยวิธีนี้แทนที่onBackPressedในกิจกรรม ทั้งหมดFragmentTransactionเป็นaddToBackStackก่อนที่จะกระทำการ:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();
        //additional code
    } else {
        getSupportFragmentManager().popBackStack();
    }

}


4
count == 1 จะอนุญาตให้ปิดส่วนแรกในการกดปุ่มหลังเดียว แต่ไม่อย่างนั้นทางออกที่ดีที่สุด
Kunalxigxag

2
หากคุณใช้ไลบรารีสนับสนุน v7 และกิจกรรมของคุณขยายจากFragmentActivity (หรือคลาสย่อยเช่น AppCompatActivity) สิ่งนี้จะเกิดขึ้นตามค่าเริ่มต้น ดูFragmentActivity # onBackPressed
เซียว

3
แต่คุณไม่สามารถใช้ตัวแปรคลาสและฟังก์ชั่นจากคลาสของ
แฟรกเมน

2
@Prabs หากคุณใช้การสนับสนุนแฟรกเมนต์ v4 ดังนั้นให้แน่ใจว่าคุณใช้ getSupportFragmentManager (). getBackStackEntryCount ();
Veer3383

151

ในความคิดของฉันทางออกที่ดีที่สุดคือ:

โซลูชั่น JAVA

สร้างส่วนต่อประสานที่เรียบง่าย:

public interface IOnBackPressed {
    /**
     * If you return true the back press will not be taken into account, otherwise the activity will act naturally
     * @return true if your processing has priority if not false
     */
    boolean onBackPressed();
}

และในกิจกรรมของคุณ

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    } ...
}

ในที่สุดในส่วนของคุณ:

public class MyFragment extends Fragment implements IOnBackPressed{
   @Override
   public boolean onBackPressed() {
       if (myCondition) {
            //action not popBackStack
            return true; 
        } else {
            return false;
        }
    }
}

โซลูชั่น KOTLIN

1 - สร้างส่วนต่อประสาน

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2 - เตรียมกิจกรรมของคุณ

class MyActivity : AppCompatActivity() {
    override fun onBackPressed() {
        val fragment =
            this.supportFragmentManager.findFragmentById(R.id.main_container)
        (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let {
            super.onBackPressed()
        }
    }
}

3 - ใช้งานในชิ้นส่วนเป้าหมายของคุณ

class MyFragment : Fragment(), IOnBackPressed {
    override fun onBackPressed(): Boolean {
        return if (myCondition) {
            //action not popBackStack
            true
        } else {
            false
        }
    }
}

1
คือR.id.main_containerอะไร นั่นคือ ID สำหรับ FragmentPager หรือไม่
นาธานเอฟ

1
@MaximeJallu ในโซลูชัน Kotlin การผสมการโทรที่ปลอดภัย ( .?) และการบังคับใช้ non-nulls ( !!) สามารถนำไปสู่ ​​NPEs - นักแสดงไม่ได้ปลอดภัยเสมอไป ฉันอยากเขียนif ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }kotliney หรือมากกว่านั้น(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
Antek

1
@ Antek สวัสดีขอบคุณสำหรับการกลับมาของคุณคุณสามารถแก้ไขโพสต์เพื่อทำการแก้ไข อย่าตัดสินโซลูชันโดยรวมอย่างรุนแรง
Maxime Jallu

4
ไม่ควร.onBackPressed()?.takeIf { !it }?.let{...}หรือ .not()แค่คืนค่าผกผัน
David Miguel

1
val fragment = this.supportFragmentManager.findFragmentById(R.id.flContainer) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? IOnBackPressed currentFragment?.onBackPressed()?.takeIf { !it }?.let{ super.onBackPressed() }เหมาะสำหรับผู้ที่ใช้ Kotlin และ NavigationController
Pavle Pavlov

97

ตาม @HaMMeRed คำตอบที่นี่คือ pseudocode วิธีการใช้งาน ให้บอกว่ากิจกรรมหลักของคุณถูกเรียกBaseActivityซึ่งมีชิ้นส่วนย่อยของเด็ก (เช่นในตัวอย่างของ SlideMenu lib) นี่คือขั้นตอน:

ก่อนอื่นเราต้องสร้างส่วนต่อประสานและคลาสซึ่งใช้ส่วนต่อประสานเพื่อให้มีวิธีการทั่วไป

  1. สร้างอินเตอร์เฟสคลาส OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. สร้างคลาสที่ใช้ทักษะของ OnBackPressedListener

    public class BaseBackPressedListener implements OnBackPressedListener {
        private final FragmentActivity activity;
    
        public BaseBackPressedListener(FragmentActivity activity) {
            this.activity = activity;
        }
    
        @Override
        public void doBack() {
            activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
  3. ตั้งแต่ตอนนี้เราจะทำงานกับโค้ดBaseActivityและแฟรกเมนต์ของมัน

  4. สร้างฟังส่วนตัวบนชั้นเรียนของคุณ BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. สร้างวิธีการตั้งค่าฟัง BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. ในการแทนที่onBackPressedใช้สิ่งที่ต้องการ

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. ในส่วนของonCreateViewคุณคุณควรเพิ่มผู้ฟังของเรา

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        activity = getActivity();
    
        ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
    
        View view = ... ;
    //stuff with view
    
        return view;
    }

Voila ตอนนี้เมื่อคุณคลิกกลับมาในส่วนที่คุณควรจะจับวิธีการที่กำหนดเองของคุณกลับ


หากมีมากกว่าหนึ่งชิ้นเป็นผู้ฟังคุณจะทราบได้อย่างไรว่าonBackPressed()ชิ้นส่วนใดถูกแสดงเมื่อกดปุ่มย้อนกลับ
อัล Lelopath

2
คุณสามารถปรับแต่ง listener การคลิกแทน baseBackPressedListener แบบใหม่และโทรไปที่นั่นแบบไม่ระบุชื่อเพื่อกำหนดพฤติกรรมของตัวเองสิ่งนี้:((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
เดดฟิช

ขอบคุณฉันได้เปลี่ยนตำแหน่งของฉันในเรื่องนี้แล้วฉันขอแนะนำให้ decoupling พร้อมกับข้อความออกอากาศท้องถิ่นแล้ว ฉันคิดว่ามันจะให้ส่วนประกอบที่มีคุณภาพสูงขึ้น
HaMMeReD


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

86

สิ่งนี้ใช้ได้สำหรับฉัน: https://stackoverflow.com/a/27145007/3934111

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

    if(getView() == null){
        return;
    }

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button's click listener
                return true;
            }
            return false;
        }
    });
}

2
ทำงานไร้ที่ติ! แต่คุณต้องจัดการกับการกระทำเริ่มต้นด้วยการเขียนเงื่อนไข IF อีกหนึ่งข้อ เนื่องจากฉันลดระดับลง SlideUpPanel ของฉันหากมีการขยาย ดังนั้นก่อนอื่นฉันต้องทำเครื่องหมายตรวจสอบว่า (แผงขึ้น) แล้ว .....
sud007

18
ปัญหาเกี่ยวกับวิธีแก้ปัญหานี้คือ getView () คืนค่ามุมมองหลักเท่านั้น (ไม่ใช่มุมมองย่อยหากได้รับการโฟกัส) ดังนั้นหากคุณมี EditText และพิมพ์ข้อความจากนั้นกด "ย้อนกลับ" หนึ่งครั้งเพื่อซ่อนแป้นพิมพ์การกดครั้งที่สองที่ "ย้อนกลับ" จะเริ่มต้นจาก "EditText" (มุมมองตัวเอง) และวิธีนี้ดูเหมือนจะไม่จับ " ย้อนกลับ "กดปุ่ม (เนื่องจากเป็นเพียงการจับใจสำหรับมุมมองหลัก) ตามที่ฉันเข้าใจคุณสามารถจับเหตุการณ์ปุ่มกดบน "EditText" ของคุณรวมถึงมุมมองอื่น ๆ ได้ แต่ถ้าคุณมีรูปแบบ "ซับซ้อน" นี่ก็ไม่ได้สะอาดและบำรุงรักษาเท่าที่ควร
Pelpotronic

นี่เป็นทางออกที่ง่ายสำหรับฝันร้ายที่เป็นเศษเล็กเศษน้อย หากคุณมีแบบฟอร์ม edittext ที่ 'ซับซ้อน' คุณควรลบโครงการของคุณและเริ่มต้นใหม่ กลับเท็จเมื่อคุณต้องการใช้พฤติกรรมมาตรฐาน กลับมาจริงเมื่อคุณสกัดกั้นการกดย้อนกลับและทำบางสิ่งบางอย่างกับมัน
behelit

65

หากคุณต้องการฟังก์ชั่นประเภทนั้นคุณจะต้องแทนที่มันในกิจกรรมของคุณแล้วเพิ่ม a YourBackPressedอินเทอร์เฟซให้กับแฟรกเมนต์ทั้งหมดของคุณซึ่งคุณโทรหาแฟรกเมนต์ที่เกี่ยวข้องทุกครั้งที่กดปุ่มย้อนกลับ

แก้ไข: ฉันต้องการผนวกคำตอบก่อนหน้าของฉัน

ถ้าฉันจะทำวันนี้ฉันจะใช้การออกอากาศหรืออาจจะเป็นการออกอากาศที่สั่งถ้าฉันคาดว่าแผงอื่น ๆ เพื่ออัปเดตพร้อมกันไปยังแผงหลัก / เนื้อหาหลัก

LocalBroadcastManagerใน Support Library สามารถช่วยคุณได้และคุณเพียงแค่ส่งการออกอากาศonBackPressedและสมัครสมาชิกในส่วนที่คุณสนใจ ฉันคิดว่าการส่งข้อความเป็นการใช้งานที่แยกกันมากขึ้นและจะขยายขนาดได้ดีขึ้นดังนั้นจึงเป็นคำแนะนำการใช้งานอย่างเป็นทางการของฉันในตอนนี้ เพียงใช้การIntentกระทำของเป็นตัวกรองสำหรับข้อความของคุณ ส่งสิ่งที่คุณสร้างขึ้นใหม่ACTION_BACK_PRESSEDส่งจากกิจกรรมของคุณและฟังในส่วนที่เกี่ยวข้อง


4
ความคิดที่ดีงาม! ความคิดเพิ่มเติมบางอย่าง: + สิ่งสำคัญคือต้องตระหนักถึงตรรกะนี้ด้วยการออกอากาศที่มาจากกิจกรรม> ชิ้นส่วน (ไม่ใช่รอบอื่น ๆ ) OnBackPressed มีให้เฉพาะในระดับกิจกรรมดังนั้นการวางเหตุการณ์ที่นั่นและส่งสัญญาณไปยังส่วน "ฟัง" ทั้งหมดเป็นกุญแจสำคัญที่นี่ + การใช้ EventBus (เช่นอ็อตโตของ Square) ทำให้การดำเนินการสำหรับกรณีเช่นนี้ไม่สำคัญ!
Kaushik Gopal

2
ฉันยังไม่ได้ใช้ EventBus ของ Square แต่ฉันจะเป็นผลิตภัณฑ์ในอนาคต ฉันคิดว่ามันเป็นทางออกที่ดีกว่าจาวาบริสุทธิ์และถ้าคุณสามารถเคาะส่วน Android ของสถาปัตยกรรมของคุณดีขึ้นทั้งหมด
HaMMeReD

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

เครื่องรับสัญญาณออกอากาศควรถูกผูกไว้กับวงจรชีวิตถ้าคุณใช้สิ่งนั้นดังนั้นเมื่อมองไม่เห็นมันไม่ควรรับเหตุการณ์
HaMMeReD

นอกจากนี้โปรดทราบว่าLocalBroadcastManagerไม่สามารถทำการสั่งออกอากาศได้
เซียว

46

ไม่มีสิ่งใดที่ใช้งานได้ง่ายและจะไม่ทำงานในวิธีที่เหมาะสมที่สุด

แฟรกเมนต์มีเมธอด call onDetach ที่จะทำงาน

@Override
    public void onDetach() {
        super.onDetach();
        PUT YOUR CODE HERE
    }

งานนี้จะทำ


20
แต่ปัญหาที่นี่คือคุณไม่ทราบว่าชิ้นส่วนถูกถอดออกเนื่องจากการกดย้อนกลับหรือเนื่องจากการดำเนินการอื่น ๆ ซึ่งทำให้ผู้ใช้นำไปสู่กิจกรรมหรือส่วนอื่น ๆ
ซันนี่

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

ทำงานได้ดีสำหรับ DialogFragment
CoolMind

2
อย่าลืมที่จะเพิ่มisRemoving()ตามที่อธิบายในstackoverflow.com/a/27103891/2914140
CoolMind

1
นี่มันผิด สามารถเรียกได้หลายสาเหตุ
บาหลี

40

หากคุณกำลังใช้งานandroidx.appcompat:appcompat:1.1.0หรือสูงกว่าคุณสามารถเพิ่มOnBackPressedCallbackส่วนย่อยของคุณได้ดังต่อไปนี้

requireActivity()
    .onBackPressedDispatcher
    .addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Log.d(TAG, "Fragment back pressed invoked")
            // Do custom work here    

            // if you want onBackPressed() to be called as normal afterwards
            if (isEnabled) {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
)

ดูhttps://developer.android.com/guide/navigation/navigation-custom-back


หากคุณกำลังใช้androidx-core-ktxงานคุณสามารถใช้requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
สูงสุด

สิ่งสำคัญคือให้สังเกตว่าLifecycleOwnerควรเพิ่มพารามิเตอร์ในตัวอย่างนี้ หากไม่มีก็จะมีชิ้นส่วนใด ๆ ที่เริ่มต้นหลังจากนั้นจะเรียกhandleBackPressed()ถ้ากดปุ่มย้อนกลับ
Eric B.

วิธีนี้จะต้องมีห้องสมุดนำทาง?
เงียบ

25

เพียงเพิ่มaddToBackStackในขณะที่คุณกำลังเปลี่ยนระหว่างชิ้นส่วนของคุณเช่นด้านล่าง:

fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).addToBackStack("tag").commit();

ถ้าคุณเขียนaddToBackStack(null)มันจะจัดการด้วยตัวเอง แต่ถ้าคุณให้แท็กคุณควรจัดการด้วยตนเอง


มีอะไรอีกบ้างที่ต้องทำหรือการเพิ่มเมธอด addToBackStack นั้นเพียงพอหรือไม่
Sreecharan Desabattula

1
หากคุณเพิ่งเพิ่มว่ามันควรจะทำงานถ้ามันยังไม่ทำงานควรมีบางสิ่งในรหัสของคุณ ทิ้งรหัสไว้ที่อื่นเพื่อดู @SreecharanDesabattula โปรด
ฤดี

@Rudi คุณสามารถบอกstackoverflow.com/questions/32132623/…
Aditya Vyas-Lakhan

20

เนื่องจากคำถามนี้และคำตอบบางอย่างมีอายุเกินห้าปีให้ฉันแบ่งปันคำตอบของฉัน นี่คือการติดตามและการสร้างสรรค์สิ่งใหม่เพื่อคำตอบจาก @oyenigun

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

ฉันต้องใช้สิ่งนี้เพราะบางส่วนที่ฉันใช้มีมุมมองที่เล็กกว่าที่ฉันต้องการยกเลิกด้วยปุ่มย้อนกลับเช่นมุมมองข้อมูลขนาดเล็กที่ปรากฏขึ้น ฯลฯ แต่นี่เป็นสิ่งที่ดีสำหรับทุกคนที่ต้องการแทนที่พฤติกรรมของ ปุ่มย้อนกลับภายในชิ้นส่วน

ก่อนกำหนดอินเทอร์เฟซ

public interface Backable {
    boolean onBackPressed();
}

อินเทอร์เฟซนี้ซึ่งฉันเรียกว่าBackable(ฉันเป็นตัวจัดการข้อตกลงการตั้งชื่อ) มีวิธีการเดียวonBackPressed()ที่ต้องคืนbooleanค่า เราจำเป็นต้องบังคับใช้ค่าบูลีนเพราะเราจำเป็นต้องทราบว่าการกดปุ่มย้อนกลับมีการ "ดูดซับ" เหตุการณ์ด้านหลังหรือไม่ การส่งคืนtrueหมายความว่ามีและไม่จำเป็นต้องดำเนินการใด ๆ เพิ่มเติมมิฉะนั้นจะfalseกล่าวว่าการดำเนินการย้อนกลับเริ่มต้นยังคงเกิดขึ้น อินเทอร์เฟซนี้ควรเป็นไฟล์ของตัวเอง (ควรอยู่ในแพ็คเกจที่แยกต่างหากinterfaces) จำไว้ว่าการแยกชั้นเรียนของคุณออกเป็นแพ็คเกจเป็นแนวปฏิบัติที่ดี

ประการที่สองพบชิ้นส่วนด้านบน

ฉันสร้างวิธีการที่ส่งคืนFragmentวัตถุสุดท้ายในกองหลัง ฉันใช้แท็ก ... หากคุณใช้ ID ให้ทำการเปลี่ยนแปลงที่จำเป็น ฉันมีวิธีแบบคงที่นี้ในคลาสยูทิลิตี้ที่เกี่ยวข้องกับรัฐนำทาง ฯลฯ ... แต่แน่นอนวางไว้ในตำแหน่งที่เหมาะสมที่สุดสำหรับคุณ NavUtilsสำหรับการสั่งสอนผมได้วางระเบิดในระดับที่เรียกว่า

public static Fragment getCurrentFragment(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        String lastFragmentName = fragmentManager.getBackStackEntryAt(
                fragmentManager.getBackStackEntryCount() - 1).getName();
        return fragmentManager.findFragmentByTag(lastFragmentName);
    }
    return null;
}

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

ประการที่สามนำไปใช้ในส่วน

ใช้Backableอินเทอร์เฟซในส่วนใดก็ตามที่คุณต้องการแทนที่ลักษณะการทำงานของปุ่มย้อนกลับ เพิ่มวิธีการใช้งาน

public class SomeFragment extends Fragment implements 
        FragmentManager.OnBackStackChangedListener, Backable {

...

    @Override
    public boolean onBackPressed() {

        // Logic here...
        if (backButtonShouldNotGoBack) {
            whateverMethodYouNeed();
            return true;
        }
        return false;
    }

}

ในการonBackPressed()แทนที่ให้ใส่ตรรกะที่คุณต้องการ หากคุณต้องการให้ปุ่มย้อนกลับไม่ปรากฏสแต็กกลับ (พฤติกรรมเริ่มต้น) ให้คืนค่าจริงว่าเหตุการณ์ด้านหลังของคุณถูกดูดซับ มิฉะนั้นส่งคืน false

สุดท้ายในกิจกรรมของคุณ ...

แทนที่onBackPressed()เมธอดและเพิ่มตรรกะนี้เข้ากับ:

@Override
public void onBackPressed() {

    // Get the current fragment using the method from the second step above...
    Fragment currentFragment = NavUtils.getCurrentFragment(this);

    // Determine whether or not this fragment implements Backable
    // Do a null check just to be safe
    if (currentFragment != null && currentFragment instanceof Backable) {

        if (((Backable) currentFragment).onBackPressed()) {
            // If the onBackPressed override in your fragment 
            // did absorb the back event (returned true), return
            return;
        } else {
            // Otherwise, call the super method for the default behavior
            super.onBackPressed();
        }
    }

    // Any other logic needed...
    // call super method to be sure the back button does its thing...
    super.onBackPressed();
}

เราได้รับส่วนปัจจุบันในกองหลังแล้วเราทำการตรวจสอบเป็นโมฆะและตรวจสอบว่ามันใช้Backableอินเตอร์เฟซของเรา ถ้าเป็นเช่นนั้นตรวจสอบว่าเหตุการณ์ถูกดูดซับ ถ้าเป็นเช่นนั้นเราจะทำกับonBackPressed()และสามารถส่งคืนได้ มิฉะนั้นถือว่าเป็นกดย้อนกลับตามปกติและเรียกใช้วิธีการ super

ตัวเลือกที่สองที่ไม่เกี่ยวข้องกับกิจกรรม

บางครั้งคุณไม่ต้องการให้กิจกรรมจัดการกับสิ่งนี้เลยและคุณต้องจัดการกับมันโดยตรงภายในส่วน แต่ใครบอกว่าคุณทำไม่ได้มี Fragments ด้วย back press API ได้ แค่ขยายแฟรกเมนต์ของคุณไปยังคลาสใหม่

สร้างคลาสนามธรรมที่ขยายส่วนและใช้View.OnKeyListnerอินเทอร์เฟซ ...

import android.app.Fragment;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public abstract class BackableFragment extends Fragment implements View.OnKeyListener {

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(this);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                onBackButtonPressed();
                return true;
            }
        }

        return false;
    }

    public abstract void onBackButtonPressed();
}

อย่างที่คุณเห็นส่วนใด ๆ ที่ขยายBackableFragmentจะจับการคลิกกลับโดยอัตโนมัติโดยใช้View.OnKeyListenerอินเทอร์เฟซ เพียงแค่เรียกonBackButtonPressed()วิธีนามธรรมจากภายในonKey()วิธีการใช้งานโดยใช้ตรรกะมาตรฐานในการมองเห็นการกดปุ่มย้อนกลับ หากคุณต้องการลงทะเบียนการคลิกที่สำคัญอื่น ๆ นอกเหนือจากปุ่มย้อนกลับเพียงแค่ให้แน่ใจว่าได้เรียกsuperวิธีการเมื่อเอาชนะonKey()ในส่วนของคุณมิฉะนั้นคุณจะแทนที่พฤติกรรมในสิ่งที่เป็นนามธรรม

ใช้งานง่ายเพียงขยายและใช้งาน:

public class FragmentChannels extends BackableFragment {

    ...

    @Override
    public void onBackButtonPressed() {
        if (doTheThingRequiringBackButtonOverride) {
            // do the thing
        } else {
            getActivity().onBackPressed();
        }
    }

    ...
}

ตั้งแต่วิธีการในชั้นซุปเปอร์เป็นนามธรรมเมื่อคุณขยายคุณต้องใช้onBackButtonPressed() onBackButtonPressed()มันกลับมาvoidเพราะมันเพียงแค่ต้องดำเนินการภายในชั้นชิ้นส่วนและไม่จำเป็นต้องถ่ายทอดการดูดซึมของสื่อกลับไปที่กิจกรรม ตรวจสอบให้แน่ใจว่าคุณโทรonBackPressed()วิธีการกิจกรรมหากสิ่งที่คุณทำกับปุ่มย้อนกลับไม่ต้องการการจัดการมิฉะนั้นปุ่มย้อนกลับจะถูกปิดการใช้งาน ... และคุณไม่ต้องการที่!

Caveats อย่างที่คุณเห็นนี่เป็นการกำหนดให้ผู้ฟังหลักเป็นมุมมองรูทของแฟรกเมนต์และเราจะต้องโฟกัสมัน หากมีการแก้ไขข้อความที่เกี่ยวข้อง (หรือมุมมองการขโมยโฟกัสอื่น ๆ ) ในส่วนของคุณที่ขยายคลาสนี้ (หรือชิ้นส่วนภายในหรือมุมมองอื่น ๆ ที่มีเหมือนกัน) คุณจะต้องจัดการแยกต่างหาก มีบทความที่ดีเกี่ยวกับการขยาย EditTextเพื่อไม่ให้ความสำคัญกับ back press

ฉันหวังว่าบางคนพบว่ามีประโยชน์นี้ การเข้ารหัสที่มีความสุข


ขอบคุณมันเป็นทางออกที่ดี คุณสามารถพูดว่าทำไมคุณถึงโทรมาsuper.onBackPressed();สองครั้ง?
CoolMind

currentFragmentมันเรียกว่าเพียงหนึ่งครั้งต่อสถานการณ์ที่เกี่ยวกับสถานะของ null หากแฟรกเมนต์ไม่เป็นโมฆะและแฟรกเมนต์ใช้Backableอินเตอร์เฟสจะไม่มีการดำเนินการใด ๆ หากแฟรกเมนต์ไม่เป็นโมฆะและไม่ได้ใช้งานBackableเราจะเรียกเมธอด super หากแฟรกเมนต์เป็นโมฆะมันจะข้ามไปยังการโทรซูเปอร์ครั้งสุดท้าย
mwieczorek

ขอโทษฉันไม่เข้าใจ ในกรณีที่ a currentFragmentไม่เป็นโมฆะและเป็นอินสแตนซ์ของ Backable และถูกถอดออก (เรากดbackปุ่มและปิดแฟรกเมนต์) การเกิดขึ้นครั้งแรกของsuper.onBackPressed();ถูกเรียกแล้วที่สอง
CoolMind

ทางออกที่ดีเลิศ
David Toledo

บางที "Closeable" อาจฟังดูดีกว่า "Backable"
นิดหน่อย

15

การแก้ปัญหาง่ายมาก:

1) ถ้าคุณมีคลาสแฟรกเมนต์พื้นฐานที่แฟรกเมนต์ทั้งหมดขยายให้เพิ่มโค้ดนี้ในคลาสของมันมิฉะนั้นสร้างคลาสแฟรกเมนต์พื้นฐานดังกล่าว

 /* 
 * called when on back pressed to the current fragment that is returned
 */
 public void onBackPressed()
 {
    // add code in super class when override
 }

2) ในคลาสกิจกรรมของคุณให้แทนที่ onBackPressed ดังนี้:

private BaseFragment _currentFragment;

@Override
public void onBackPressed()
{
      super.onBackPressed();
     _currentFragment.onBackPressed();
}

3) ในชั้น Fragment ของคุณเพิ่มรหัสที่คุณต้องการ:

 @Override
 public void onBackPressed()
 {
    setUpTitle();
 }


9

onBackPressed() ทำให้แฟรกเมนต์แยกออกจากกิจกรรม

@Sterling Diaz ตอบว่าฉันคิดว่าเขาพูดถูก แต่สถานการณ์บางอย่างจะผิด (เช่นหน้าจอหมุน)

ดังนั้นฉันคิดว่าเราสามารถตรวจสอบได้ว่า isRemoving()จะบรรลุเป้าหมายหรือไม่

คุณสามารถเขียนได้ที่หรือonDetach() onDestroyView()มันเป็นงาน

@Override
public void onDetach() {
    super.onDetach();
    if(isRemoving()){
        // onBackPressed()
    }
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    if(isRemoving()){
        // onBackPressed()
    }
}

8

ฉันทำอย่างนี้แล้วมันก็ใช้ได้ผลสำหรับฉัน

อินเตอร์เฟซที่เรียบง่าย

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

ตัวอย่างการนำไปใช้

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

public void onClick() {
       // what you want to call onBackPressed?
}

จากนั้นเพียงแค่แทนที่ onBackPressed ในกิจกรรม

    @Override
public void onBackPressed() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    Fragment lastFrag = getLastNotNull(frags);
    //nothing else in back stack || nothing in back stack is instance of our interface
    if (count == 0 || !(lastFrag instanceof FragmentOnBackClickInterface)) {
        super.onBackPressed();
    } else {
        ((FragmentOnBackClickInterface) lastFrag).onClick();
    }
}

private Fragment getLastNotNull(List<Fragment> list){
    for (int i= list.size()-1;i>=0;i--){
        Fragment frag = list.get(i);
        if (frag != null){
            return frag;
        }
    }
    return null;
}

ไม่ทำงาน ! super.onbackpressed () ถูกเรียกเสมอ :(
Animesh Mangla

วิธีผ่านเหตุการณ์นี้ชิ้นส่วนภายใน ฉันมี ViewPager ในกิจกรรมและ ViewPager นั้นมีแฟรกเมนต์ตอนนี้แฟรกเมนต์เหล่านั้นทั้งหมดมีส่วนย่อยของเด็ก วิธีส่งเหตุการณ์ปุ่มย้อนกลับไปยังส่วนย่อยของเด็กนั้น
Kishan Vaghela

@KishanVaghela ดีฉันไม่ได้สัมผัส Android ตั้งแต่นั้นมา แต่ฉันมีความคิดเดียว ให้ฉัน 24 เพื่ออัปเดตคำตอบของฉัน
Błażej

ตกลงไม่มีปัญหาตัวเลือกหนึ่งคือฉันต้องส่งเหตุการณ์ผู้ฟังไปยังส่วนย่อยของเด็กทุกคนจากชิ้นส่วนหลัก แต่มันไม่ใช่วิธีที่เหมาะเพราะฉันต้องทำสิ่งนี้กับทุก ๆ ชิ้น
Kishan Vaghela

@KishanVaghela ฉันกำลังคิดเกี่ยวกับผู้จัดการในกิจกรรม คุณสามารถเพิ่ม / ลบแฟรกเมนต์ด้วยอินเทอร์เฟซนั้น แต่ด้วยวิธีนี้คุณจะต้องใช้ตัวจัดการในทุกส่วนที่มี subfragments ดังนั้นทางออกที่ดีกว่าอาจจะสร้างผู้จัดการเป็นซิงเกิล จากนั้นคุณจะต้องเพิ่ม / ลบชิ้นส่วน / วัตถุและใน 'onBackPressed' you call method 'onBackPressed' ของผู้จัดการนี้ เมธอดนั้นจะเรียกเมธอด 'onClick' ในทุกออบเจ็กต์ที่ถูกเพิ่มเข้ากับผู้จัดการ ชัดเจนขึ้นหรือน้อยลงสำหรับคุณ
Błażej

8

คุณควรเพิ่มส่วนต่อประสานกับโปรเจคของคุณดังนี้

public interface OnBackPressed {

     void onBackPressed();
}

จากนั้นคุณควรใช้อินเทอร์เฟซนี้กับส่วนของคุณ

public class SampleFragment extends Fragment implements OnBackPressed {

    @Override
    public void onBackPressed() {
        //on Back Pressed
    }

}

และคุณสามารถทริกเกอร์เหตุการณ์ onBackPressed นี้ภายใต้กิจกรรมของคุณในกิจกรรม BackBackPressed ด้านล่าง;

public class MainActivity extends AppCompatActivity {
       @Override
        public void onBackPressed() {
                Fragment currentFragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - 1);
                if (currentFragment instanceof OnBackPressed) {  
                    ((OnBackPressed) currentFragment).onBackPressed();
                }
                super.onBackPressed();
        }
}

มันเป็นวิธี agood แต่ระวังอย่าโทรgetActivity().onBackPressed();ตามที่มันจะเรียกใช้ข้อยกเว้น: "java.lang.StackOverflowError: ขนาดสแต็ก 8MB"
CoolMind

8

นี่เป็นเพียงรหัสเล็ก ๆ ที่จะทำเคล็ดลับ:

 getActivity().onBackPressed();

หวังว่าจะช่วยให้ใครบางคน :)


4
เขาขอให้แทนที่พฤติกรรมไม่ใช่เพียงแค่เรียกใช้วิธีการกิจกรรม
mwieczorek

2
ฉันอ่านมันอย่างระมัดระวังขอบคุณ วิธีการแก้ปัญหาของคุณเพียงแค่ถ่ายทอดวิธี onBackPressed กลับไปที่กิจกรรมเมื่อเขาถามวิธีการแทนที่
mwieczorek

7

หากคุณใช้ EventBus อาจเป็นวิธีที่ง่ายกว่า:

ในส่วนของคุณ:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    EventBus.getDefault().register(this);
}

@Override
public void onDetach() {
    super.onDetach();
    EventBus.getDefault().unregister(this);
}


// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    getSupportFragmentManager().popBackStack();
}

และในชั้นเรียนกิจกรรมของคุณคุณสามารถกำหนด:

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    super.onBackPressed();
}

@Override
public void onBackPressed() {
    EventBus.getDefault().post(new BackPressedMessage(true));
}

BackPressedMessage.java เป็นเพียงวัตถุ POJO

นี่สะอาดมากและไม่มีปัญหาเรื่องอินเตอร์เฟส / การติดตั้งใช้งาน


7

นี่คือทางออกของฉัน:

ในMyActivity.java:

public interface OnBackClickListener {
        boolean onBackClick();
    }

    private OnBackClickListener onBackClickListener;

public void setOnBackClickListener(OnBackClickListener onBackClickListener) {
        this.onBackClickListener = onBackClickListener;
    }

@Override
    public void onBackPressed() {
        if (onBackClickListener != null && onBackClickListener.onBackClick()) {
            return;
        }
        super.onBackPressed();
    }

และในส่วน:

((MyActivity) getActivity()).setOnBackClickListener(new MyActivity.OnBackClickListener() {
    @Override
    public boolean onBackClick() {
        if (condition) {
            return false;
        }

        // some codes

        return true;
    }
});

5

การใช้องค์ประกอบการนำทางคุณสามารถทำสิ่งนี้ได้ :

ชวา

public class MyFragment extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
        }
    });
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

    // The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}

Kotlin

class MyFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // This callback will only be called when MyFragment is at least Started.
    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Handle the back button event
    }

    // The callback can be enabled or disabled here or in the lambda
}
...
}

4

วิธีการเกี่ยวกับการใช้ onDestroyView ()?

@Override
public void onDestroyView() {
    super.onDestroyView();
}

5
สิ่งที่เกี่ยวกับการไปอีกส่วนจากชิ้นส่วนที่เกิดขึ้นจริงจะเกิดอะไรขึ้นโดยใช้วิธีการของคุณ? :)
Choletski

คำตอบที่ไร้ประโยชน์มากที่สุด มันเป็นเช่นเดียวกับการเขียนหรือi = i if (1 < 0) {}
CoolMind

4
@Override
public void onResume() {
    super.onResume();

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                // handle back button
                replaceFragmentToBackStack(getActivity(), WelcomeFragment.newInstance(bundle), tags);

                return true;
            }

            return false;
        }
    });
}

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

4
public class MyActivity extends Activity {

    protected OnBackPressedListener onBackPressedListener;

    public interface OnBackPressedListener {
        void doBack();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    } 

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        super.onDestroy();
    }
}

ในส่วนของคุณเพิ่มสิ่งต่อไปนี้อย่าลืมใช้อินเทอร์เฟซของ mainactivity

public class MyFragment extends Framgent implements MyActivity.OnBackPressedListener {
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         ((MyActivity) getActivity()).setOnBackPressedListener(this);
    }

@Override
public void doBack() {
    //BackPressed in activity will call this;
}

}

4

ในโซลูชันของฉัน (Kotlin);

ฉันใช้ฟังก์ชั่น onBackAlternative เป็นพารามิเตอร์ในBaseActivity BaseActivity

BaseActivity

abstract class BaseActivity {

    var onBackPressAlternative: (() -> Unit)? = null

    override fun onBackPressed() {
        if (onBackPressAlternative != null) {
            onBackPressAlternative!!()
        } else {
            super.onBackPressed()
        }
    }
}

ฉันมีฟังก์ชั่นสำหรับชุดonBackPressAlternativeบนBaseFragment BaseFragment

BaseFragment

abstract class BaseFragment {

     override fun onStart() {
        super.onStart()
        ...
        setOnBackPressed(null) // Add this
     }

      //Method must be declared as open, for overriding in child class
     open fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
         (activity as BaseActivity<*, *>).onBackPressAlternative = onBackAlternative
     }
}

จากนั้นในช่องของฉันกดอัลเทอร์เนทีฟพร้อมใช้งานบนแฟรกเมนต์

ชิ้นส่วนย่อย

override fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
    (activity as BaseActivity<*, *>).onBackPressAlternative = {
        // TODO Your own custom onback function 
    }
}

3

อย่าใช้วิธี ft.addToBackStack () เพื่อที่เมื่อคุณกดปุ่มย้อนกลับกิจกรรมของคุณจะเสร็จสิ้น

proAddAccount = new ProfileAddAccount();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, proAddAccount);
//fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();


3

เพียงทำตามขั้นตอนเหล่านี้:

เสมอในขณะที่เพิ่มส่วน

fragmentTransaction.add(R.id.fragment_container, detail_fragment, "Fragment_tag").addToBackStack(null).commit();

จากนั้นในกิจกรรมหลักให้แทนที่ onBackPressed()

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStack();
} else {
    finish();
}

ในการจัดการปุ่มย้อนกลับในแอปของคุณ

Fragment f = getActivity().getSupportFragmentManager().findFragmentByTag("Fragment_tag");
if (f instanceof FragmentName) {
    if (f != null) 
        getActivity().getSupportFragmentManager().beginTransaction().remove(f).commit()               
}

แค่นั้นแหละ!


3

ในวงจรชีวิตของกิจกรรมหุ่นยนต์ปุ่มย้อนกลับเกี่ยวข้องกับธุรกรรม FragmentManager เสมอเมื่อเราใช้ FragmentActivity หรือ AppCompatActivity

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

    public void replaceFragment(Fragment fragment) {

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.activity_menu_fragment_container, fragment).commit();
    }

ที่นี่ฉันจะไม่เพิ่มสแต็กกลับสำหรับส่วนย่อยในบ้านของฉันเพราะเป็นหน้าแรกของใบสมัครของฉัน หากเพิ่ม addToBackStack ไปที่ HomeFragment แล้วแอพจะรอลบ frament ทั้งหมดในความเป็นกรดแล้วเราจะได้หน้าจอว่างเปล่าดังนั้นฉันจะรักษาตามเงื่อนไขต่อไปนี้

if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
}

ตอนนี้คุณสามารถดูส่วนที่เพิ่มก่อนหน้านี้ใน acitvity และแอพจะออกเมื่อไปถึง HomeFragment คุณยังสามารถดูตัวอย่างต่อไปนี้

@Override
public void onBackPressed() {

    if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}

3

Fragment: สร้าง BaseFragment โดยวางเมธอด:

 public boolean onBackPressed();

กิจกรรม:

@Override
public void onBackPressed() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (!fragment.isVisible()) continue;

            if (fragment instanceof BaseFragment && ((BaseFragment) fragment).onBackPressed()) {
                return;
            }
        }
    }

    super.onBackPressed();
}

กิจกรรมของคุณจะดำเนินการผ่านส่วนที่แนบมาและมองเห็นได้และโทรonBackPressed ()ในแต่ละรายการและยกเลิกหากหนึ่งในนั้นส่งคืน 'จริง' (หมายถึงมีการจัดการดังนั้นจึงไม่มีการดำเนินการเพิ่มเติม)


ท่านอันนี้เป็นวิธีที่สง่างามที่สุดและทำงานได้อย่างสมบูรณ์แบบ!
asozcan

3

ตามที่บันทึกประจำรุ่น AndroidX , androidx.activity 1.0.0-alpha01ถูกปล่อยออกมาและเปิดตัวComponentActivityเป็นชั้นฐานใหม่ของที่มีอยู่และFragmentActivity AppCompatActivityและการเปิดตัวครั้งนี้ทำให้เรามีคุณสมบัติใหม่:

ตอนนี้คุณสามารถลงทะเบียนOnBackPressedCallbackผ่านaddOnBackPressedCallbackเพื่อรับการonBackPressed()เรียกกลับโดยไม่จำเป็นต้องแทนที่วิธีการในกิจกรรมของคุณ


คุณสามารถแสดงตัวอย่างของสิ่งนี้ได้ไหม ฉันไม่ได้รับวิธีการที่จะแก้ไข
ไบรอัน Bryce

1
@BryanBryce ฉันไม่พบตัวอย่าง แต่เป็นทางการ doc: developer.android.com/reference/androidx/activity/…
TonnyL

วิธีนี้จะไม่แก้ปัญหา b / c AppCompatActivity กำลังขยายจาก androidx.core.app.ComponentActivity แทน androidx.activity.ComponentActivity
wooldridgetm

3

คุณสามารถลงทะเบียนชิ้นส่วนในกิจกรรมเพื่อจัดการกดกลับ:

interface BackPressRegistrar {
    fun registerHandler(handler: BackPressHandler)
    fun unregisterHandler(handler: BackPressHandler)
}

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

การใช้งาน:

ในส่วน:

private val backPressHandler = object : BackPressHandler {
    override fun onBackPressed(): Boolean {
        showClosingWarning()
        return false
    }
}

override fun onResume() {
    super.onResume()
    (activity as? BackPressRegistrar)?.registerHandler(backPressHandler)
}

override fun onStop() {
    (activity as? BackPressRegistrar)?.unregisterHandler(backPressHandler)
    super.onStop()
}

ในกิจกรรม:

class MainActivity : AppCompatActivity(), BackPressRegistrar {


    private var registeredHandler: BackPressHandler? = null
    override fun registerHandler(handler: BackPressHandler) { registeredHandler = handler }
    override fun unregisterHandler(handler: BackPressHandler) { registeredHandler = null }

    override fun onBackPressed() {
        if (registeredHandler?.onBackPressed() != false) super.onBackPressed()
    }
}

3

การจัดเตรียมการนำทางด้านหลังที่กำหนดเองโดยการจัดการกับ BackPressed ทำให้ตอนนี้ง่ายขึ้นด้วยการเรียกกลับภายในส่วน

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    myCustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

หากคุณต้องการให้การกระทำย้อนกลับเริ่มต้นขึ้นอยู่กับเงื่อนไขบางประการคุณสามารถใช้:

 NavHostFragment.findNavController(this@MyFragment).navigateUp();

3

ภายในเมธอด onCreate ของแฟรกเมนต์เพิ่มสิ่งต่อไปนี้:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            //Handle the back pressed
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}

หลังจากเพิ่มการกดปุ่ม myFragment นี้แล้ว แต่ตอนนี้กิจกรรม backpress ไม่ได้
Sunil Chaudhary

2

ทางออกที่ดีที่สุด

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    public void onBackPressed() {

        FragmentManager fm = getSupportFragmentManager();
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                super.onBackPressed();
                finish();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    finish();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack();
                    return;
                }
                else {

                    fm.popBackStack();
                    if (fm.getFragments().size() <= 1) {
                        finish();
                    }
                    return;
                }

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