Parçalarda onBackPressed () nasıl uygulanır?


461

Biz uygulamak hangi bir yolu var mı onBackPressed()biz Android Aktivite uygulamak hangi tarzına benzer Android Fragment?

Fragment yaşam döngüsünde yoktur onBackPressed(). onBackPressed()Android 3.0 parçalarında aşırı sürüş için başka bir yöntem var mı ?


13
IMHO, bir parça GERİ düğmesini ne bilmeli ne de önemsemelidir. Bir aktivite, normalde FragmentManager tarafından işlenen parçalarda olsa da GERİ düğmesine önem verebilir. Ancak, parça daha geniş çevresini bilmediğinden (örneğin, aktivitede birkaçdan biri olup olmadığı), bir parçanın uygun GERİ işlevselliğinin ne olduğunu belirlemeye çalışması gerçekten güvenli değildir.
CommonsWare

61
Tüm parçanın bir WebView görüntülemek için ne zaman yaptığı ve geri düğmesine basıldığında WebView'ın bir önceki sayfaya "geri dönmesini" istersiniz?
mharper

Michael Herbig yanıtı benim için mükemmeldi. Ancak getSupportFragmentManager () kullanamayanlar için. Bunun yerine getFragmentManager () öğesini kullanın.
Dantalian

Dmitry Zaitsev'in [Benzer Soru] [1] üzerindeki cevabı gayet iyi çalışıyor. [1]: stackoverflow.com/a/13450668/1372866
ashakirov

6
mharper'a cevap vermek için webview.setOnKeyListener (yeni OnKeyListener () {@Kamu boolean onKey'i gözden geçirin (Görünüm v, int keyCode, KeyEvent olayı) {if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack ()) gibi bir şey yapabilirsiniz. {webview.goBack (); return true;} return false;}});
Omid Aminiva

Yanıtlar:


307

Bu şekilde onBackPressedAktivitede geçersiz kılma sorununu çözdüm . Tüm FragmentTransactionşunlardır addToBackStackgerçekleştirmeden önce:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

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

}


4
count == 1, tek geri düğmesine basıldığında ilk parçanın kapatılmasına izin verecektir. Ama aksi takdirde en iyi çözüm
Kunalxigxag

2
Destek v7 kitaplığını kullanıyorsanız ve Etkinliğiniz FragmentActivity'yi (veya AppCompatActivity gibi bir alt sınıfı) genişletiyorsa , bu varsayılan olarak gerçekleşir. Bkz FragmentActivity # onBackPressed
Xiao

3
ancak bu kodda fragman sınıfından değişkenleri, sınıfları ve işlevleri kullanamazsınız
user25

2
@ Destek v4 parçasını kullanıyorsanız, sonra getSupportFragmentManager () kullandığınızdan emin olun. GetBackStackEntryCount ();
Veer3383

152

Bence en iyi çözüm:

JAVA ÇÖZÜM

Basit arayüz oluşturun:

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

Ve Faaliyetinizde

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

Sonunda Parçanızda:

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

KOTLIN ÇÖZÜMÜ

1 - Arayüz Oluştur

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2 - Faaliyetinizi Hazırlayın

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

3 - Hedef Parçanıza uygulayın

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

1
Nedir R.id.main_container? Bu FragmentPager için kimlik mi?
Nathan F.

1
Kotlin çözeltisindeki @MaximeJallu, güvenli çağrıları ( .?) karıştırıp null olmayanları ( ) zorlamak !!NPE'lere yol açabilir - oyuncu kadrosu her zaman güvenli değildir. if ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }Kotliney veya daha fazla yazmayı tercih ederim(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
Antek

1
@Antek Merhaba, Geri dönüşünüz için teşekkür ederiz, düzeltmenizi yapmak için yayını düzenleyebilirsiniz. Genel çözümü ciddi şekilde yargılamayın.
Maxime Jallu

4
Olmamalı mı .onBackPressed()?.takeIf { !it }?.let{...}? .not()sadece tersini döndürür.
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() }Bu Kotlin ve NavigationController kullanan kişiler içindir
Pavle Pavlov

97

@HaMMeRed cevabına göre buradaki sözde kod nasıl çalışmalı. Diyelim ki BaseActivityalt bölümleri olan ana etkinliğiniz çağrılıyor (SlidingMenu lib örneğinde olduğu gibi). İşte adımlar:

Öncelikle, jenerik bir metoda sahip olmak için arayüzünü uygulayan arayüz ve sınıf oluşturmamız gerekiyor

  1. Sınıf arayüzü oluştur OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. Becerilerini uygulayan bir sınıf yaratın 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. Artık kodumuz BaseActivityve kodları üzerinde çalışacağız.

  4. Sınıfınızın üstünde özel dinleyici oluşturun BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. dinleyiciyi ayarlamak için yöntem oluştur BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. geçersiz onBackPressedkılmada böyle bir şey uygulayın

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. senin fragmanında içinde onCreateViewbizim dinleyici eklemek gerekir

    @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, şimdi parçaya geri tıkladığınızda, özel sırt yönteminizi yakalamalısınız.


Birden fazla parça dinleyici ise, onBackPressed()geri düğmesine basıldığında hangi parçanın görüntülendiğini nasıl belirlersiniz ?
Al Lelopath

2
bunun yerine tıklama dinleyicisini yeni baseBackPressedListener'ı özelleştirebilir ve kendi davranışlarını ayarlamak için anonim olarak çağırabilirsiniz, şöyle bir şey:((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
deadfish

Teşekkürler, bununla ilgili konumumu değiştirdim, şimdi yerel yayın mesajlarıyla ayrıştırmayı tavsiye ederim. Bence çok ayrışmış yüksek kaliteli bileşenler verecek.
HaMMeReD


Bu benim için mükemmel çalıştı, Aktiviteyi yeniden
başlatıyorum

86

Bu benim için çalıştı: 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
Kusursuz çalıştı! Ancak, muhtemelen bir IF durumu daha yazarak varsayılan işlemi gerçekleştirmeniz gerekir. O zamandan beri Expanded, slideUpPanel indiriyordu. Yani, ilk önce bir panel işaretlemek zorunda kaldı (panel yukarı) sonra .....
sud007

18
Bu çözümdeki sorun, getView () işlevinin yalnızca ana görünümü döndürmesidir (odak elde edildiyse alt görünümü döndürmez). Bir EditText varsa ve bazı metin yazın ve sonra klavyeyi gizlemek için "geri" bir kez basın, "geri" ikinci basış "EditText" (bir görünüm kendisi) başlatılır ve bu yöntem " geri "düğmesine basın (yalnızca ana görünümü yakaladığından). Anladığım kadarıyla, "EditText" ve diğer görünümlerinizdeki keypress olaylarını yakalayabilirsiniz, ancak "karmaşık" bir formunuz varsa, bu olabildiğince temiz ve bakımı yapılabilir değildir.
Pelpotronic

Bu, parçalar olan kabus için harika bir basit çözümdür. 'Karmaşık' düzenleme metni formunuz varsa, muhtemelen projenizi silmeli ve baştan başlamalısınız. Standart davranışı kullanmak istediğinizde false değerini döndürün. Arka basını durdurduğunuzda ve onunla bir şeyler yaptığınızda
gerçeğe dönün

65

Bu tür bir işlevsellik istiyorsanız, etkinliğinizde geçersiz kılmanız ve ardından YourBackPressed geri düğmesine her basıldığında ilgili parçayı çağırdığınız tüm parçalarınıza arabirim gerekir.

Düzenleme: Önceki cevabımı eklemek istiyorum.

Bunu bugün yapsaydım, diğer panellerin ana / ana içerik paneliyle uyumlu olarak güncellenmesini beklesem bir yayın veya muhtemelen sıralı bir yayın kullanırdım.

LocalBroadcastManagerDestek Kitaplığı'ndaki bu konuda size yardımcı olabilirsiniz ve sadece yayını gönderip onBackPressedilgilenen parçalarınıza abone olursunuz. Messaging'in daha ayrıştırılmış bir uygulama olduğunu ve daha iyi ölçekleneceğini düşünüyorum, bu yüzden şimdi resmi uygulama önerim olacak. IntentMesajınızın filtresi olarak 'eylemini kullanın . yeni oluşturulanını gönderACTION_BACK_PRESSED gönderin, aktivitenizden gönderin ve ilgili parçalarda dinleyin.


4
Oldukça şık bir fikir! bazı ek düşünceler: + bu mantığı faaliyetten çıkan parçalarla gerçekleştirmek önemlidir> fragmanlar (tam tersi değil). OnBackPressed yalnızca etkinlik düzeyinde kullanılabilir, bu nedenle etkinliği orada yakalamak ve tüm "dinleme" parçalarına yayınlamak burada kilit önem taşır. + EventBus (Square's Otto gibi) kullanmak, bu kadar önemsiz bir vaka için uygulamayı kolaylaştırır!
Kaushik Gopal

2
Square'in EventBus'unu henüz kullanmadım, ancak gelecekteki ürünlerde olacağım. Bence bu daha iyi bir saf java çözümü ve eğer Android'i mimarinizin bölümlerini çalabilirseniz, daha iyi.
HaMMeReD

Etkinliği yalnızca üst kısımların ele almasına nasıl izin verirsiniz? örneğin, şu anda gösterilen parçayı yok etmek istersiniz
eugene

Yayın alıcısı bunu kullanıyorsanız yaşam döngüsüne bağlı olmalıdır, bu yüzden görünür olmadığında olayı almamalıdır.
HaMMeReD

Ayrıca LocalBroadcastManager, yayın siparişi verilemeyeceğini unutmayın
Xiao

46

Bunların hiçbirinin uygulanması kolay değildir ve en uygun şekilde çalışmaz.

Parçalar, işi yapacak bir yöntem çağrısı onDetach'a sahiptir.

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

BU İŞİ YAPACAK.


20
ancak buradaki sorun, parçanın bir arka pres veya başka bir eylem nedeniyle ayrıldığını bilmemenizdir, bu da kullanıcının başka bir etkinlik veya parçaya gitmesine yol açtı.
Güneşli

7
Parça artık etkinliğine bağlı olmadığında onDetach () yöntemi çağrılır. Bu, onDestroy () öğesinden sonra çağrılır. Geri düğmesine bastığınızda bu koda sahip olsanız da, parçadan parçaya geçtiğinizde de bu kodu alırsınız. Bu işi yapmak için şık bir şey yapabilirsiniz ...
apmartin1991

Bir DialogFragment için iyi çalışır.
CoolMind

2
Stackoverflow.com/a/27103891/2914140isRemoving() bölümünde açıklandığı gibi eklemeyi unutmayın .
CoolMind

1
Bu yanlış. Birkaç nedenden dolayı çağrılabilir
Bali

40

androidx.appcompat:appcompat:1.1.0Veya üstünü kullanıyorsanız OnBackPressedCallback, parçanıza aşağıdaki gibi bir

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

Bkz. Https://developer.android.com/guide/navigation/navigation-custom-back


Eğer androidx-core-ktxkullanıyorsanızrequireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
Max

LifecycleOwnerParam'in bu örnekte olduğu gibi eklenmesi gerektiğine dikkat etmek önemlidir . Bu olmadan, daha sonra başlatılan herhangi bir parça handleBackPressed()geri düğmesine basılırsa çağrılacaktır.
Eric B.

Bu yöntem gezinti kitaplığıyla zorunlu mu?
Sessizlik

25

addToBackStackAşağıdaki gibi parçalarınız arasında geçiş yaparken ekleyin :

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

yazarsanız addToBackStack(null), bunu kendi başına halleder, ancak bir etiket verirseniz, manuel olarak ele almalısınız.


Yapılması gereken başka bir şey var mı veya addToBackStack yöntemini eklemek yeterli mi?
Sreecharan Desabattula

1
eğer sadece işe yaraması gerektiğini eklerseniz, hala çalışmıyorsa kodunuzda bir şey olmalı. Lütfen kodu görmek için bir yere bırakın @SreecharanDesabattula
Rudi

@Rudi şunu söyleyebilir misin stackoverflow.com/questions/32132623/…
Aditya Vyas-

20

Bu soru ve bazı cevaplar beş yaşın üzerinde olduğundan çözümümü paylaşmama izin verin. Bu @oyenigun'un cevabı için bir takip ve modernizasyon

GÜNCELLEME: Bu makalenin en altına, Etkinliği hiç içermeyen soyut bir Fragment uzantısı kullanarak alternatif bir uygulama ekledim.

Bunu kullanmam gerekiyordu, çünkü kullandığım bazı parçaların, geri açılan küçük bilgi görünümleri gibi geri düğmesiyle kapatmak istediğim daha küçük görünümleri var, ancak bu, davranışını geçersiz kılması gereken herkes için iyi parçaların içindeki geri düğmesi.

İlk olarak, bir Arayüz tanımlayın

public interface Backable {
    boolean onBackPressed();
}

Aradığım bu arabirimin Backable(adlandırma kuralları için bir stickler olduğum), onBackPressed()bir booleandeğer döndürmesi gereken tek bir yöntemi var . Bir boolean değeri uygulamamız gerekir, çünkü geri düğmesine basmanın geri olayı "absorbe edip etmediğini" bilmemiz gerekir. Geri dönmek true, bunun olduğu ve başka bir işlem yapılması gerekmediği anlamına gelir, aksi takdirde, falsevarsayılan geri işlemin yine de yapılması gerektiğini söyler. Bu arayüz kendi dosyası olmalıdır (tercihen ayrı bir pakette interfaces). Unutmayın, sınıflarınızı paketlere ayırmak iyi bir uygulamadır.

İkincisi, üst parçayı bulun

FragmentArka yığındaki son nesneyi döndüren bir yöntem oluşturdum . Etiketleri kullanıyorum ... kimlikler kullanıyorsanız, gerekli değişiklikleri yapın. Navigasyon durumları, vb ile ilgilenen bir yardımcı program sınıfında bu statik yöntem var ... ama tabii ki, size en uygun yere koy. Eğitim için, benimkini adlı bir sınıfa koydum 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;
}

Arka yığın sayısının 0'dan büyük olduğundan emin olun, aksi takdirde ArrayOutOfBoundsExceptionçalışma zamanında bir fırlatma yapılabilir. 0'dan büyük değilse null değerini döndürün. Daha sonra boş bir değer olup olmadığını kontrol edeceğiz ...

Üçüncüsü, Bir Parçada Uygulama

Uygulamak Backablegeri düğmesi davranışı geçersiz gereken yere hangisi parçası içinde arayüz. Uygulama yöntemini ekleyin.

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

...

    @Override
    public boolean onBackPressed() {

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

}

In onBackPressed()geçersiz kılma, nasıl istersen mantık koydu. Geri düğmesi istiyorsanız değil arka yığını (varsayılan davranış) pop, dönmek gerçek sırt olay absorbe edildiğini,. Aksi takdirde, false değerini döndürün.

Son olarak, Faaliyetinizde ...

onBackPressed()Yöntemi geçersiz kılın ve bu mantığı buna ekleyin:

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

Mevcut parçayı arka yığın halinde alıyoruz, sonra boş bir kontrol yapıyoruz ve Backablearayüzümüzü uygulayıp uygulamadığını tespit ediyoruz . Varsa, olayın emilip emilmediğini belirleyin. Eğer öyleyse, işimiz bitti onBackPressed()ve geri dönebiliriz. Aksi takdirde, normal bir geri basma gibi davranın ve süper yöntemi çağırın.

Etkinliği Dahil Etmemek İçin İkinci Seçenek

Bazen, Aktivitenin bunu hiç ele almasını istemezsiniz ve doğrudan parça içinde ele almanız gerekir. Peki geri bastırma API'sı olan Fragmanlara sahip olamayacağınızı kim söylüyor ? Parçanızı yeni bir sınıfa genişletmeniz yeterlidir.

Fragment'ı genişleten ve View.OnKeyListnerarabirimi uygulayan soyut bir sınıf oluşturun ...

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

Gördüğünüz gibi, uzayan herhangi bir parça arayüzü BackableFragmentkullanarak otomatik olarak geri tıklamaları yakalayacaktır View.OnKeyListener. Sadece bir geri düğmesine basın ayırt etmek için standart mantık kullanarak onBackButtonPressed()uygulanan onKey()yöntem içinde soyut yöntemi çağırmak . Geri düğmesi dışındaki tuş tıklamalarını kaydetmeniz gerekiyorsa super, parçanızda geçersiz kılınırken yöntemi çağırdığınızdan emin olun onKey(), aksi takdirde soyutlamadaki davranışı geçersiz kılarsınız.

Kullanımı basit, sadece genişletip uygulayın:

public class FragmentChannels extends BackableFragment {

    ...

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

    ...
}

Yana onBackButtonPressed()süper sınıfındaki yöntem soyut, sen uzatmak kez uygulamalıdır onBackButtonPressed(). Geri dönüyor, voidçünkü sadece parça sınıfı içinde bir eylem gerçekleştirmesi gerekiyor ve basının emilimini tekrar Faaliyete aktarması gerekmiyor. Geri düğmesi ile ne yaparsanız yapın işlem gerektirmiyorsa , Etkinlik yöntemini çağırdığınızdan emin olunonBackPressed() , aksi takdirde geri düğmesi devre dışı bırakılır ... ve bunu istemezsiniz!

Uyarılar Gördüğünüz gibi, bu anahtar dinleyiciyi parçanın kök görünümüne ayarlar ve odaklamamız gerekir. Parçanızda bu sınıfı genişleten (veya aynı iç kısımlara veya aynı görünümlere sahip) düzenleme metinleri (veya başka bir odak çalma görünümü) varsa, bunu ayrı olarak ele almanız gerekir. Arka baskıya odaklanmamak için bir EditText'i genişletmeyle ilgili iyi bir makale var .

Umarım birisi bunu faydalı bulur. Mutlu kodlama.


Teşekkürler, güzel bir çözüm. Neden super.onBackPressed();iki kez aradın diyebilirsin ?
CoolMind

Boş durumu ile ilgili senaryo başına yalnızca bir kez çağrılır currentFragment. Parça boş değilse ve parça Backablearabirimi uygularsa , herhangi bir işlem yapılmaz. Eğer parça null değilse ve UYGULAMAZSA, Backablesüper yöntemi çağırırız. Parça null olursa, son süper çağrıya atlar.
mwieczorek

Üzgünüm, anlamadım. Durumda, zaman currentFragmentboş değilse ve Backable bir örneğidir ve (biz basılı ayrılır backdüğmesi ve yakın kısmı) ilk oluşumunu super.onBackPressed();denir, daha sonra ikinci bir.
CoolMind

Mükemmel Çözüm
David Toledo

Belki "Closeable" kulağa "Backable" dan biraz daha iyi geliyor
pumnao

15

Çözüm basit:

1) Tüm parçaların genişlettiği bir temel parça sınıfınız varsa, bu kodu sınıfına ekleyin, aksi takdirde böyle bir temel parça sınıfı oluşturun

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

2) Activity sınıfınızda, onBackPressed'i aşağıdaki gibi geçersiz kılın:

private BaseFragment _currentFragment;

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

3) Parça sınıfınıza istediğiniz kodu ekleyin:

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


9

onBackPressed() Parçanın Etkinlikten ayrılmasına neden olur.

@Sterling Diaz cevabına göre haklı olduğunu düşünüyorum. AMA bazı durumlar yanlış olacak. (örn. Ekranı Döndür)

Dolayısıyla, isRemoving()hedeflere ulaşıp ulaşamayacağımızı tespit edebileceğimizi düşünüyorum .

Sen de bunu yazabilir onDetach()veya onDestroyView(). Bu iş.

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

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

8

Bunu böyle yaptım ve benim için çalışıyor

Basit arayüz

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

Örnek uygulama

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

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

sonra yalnızca etkinlikte onBackPressed'i geçersiz kılın

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

Çalışmıyor ! super.onbackpressed () her zaman çağrılır :(
Animesh Mangla

Bu olay İç Parçası nasıl geçirilir. Aktivitede ViewPager var ve ViewPager Fragmanları var, Şimdi tüm bu Fragman alt parçası var. Bu alt bölümlere düğme olayı nasıl geri aktarılır.
Kishan Vaghela

@KishanVaghela o zamandan beri android'e dokunmadım ama bir fikrim var. Cevabımı güncellemem için bana 24. tanesini ver.
Błażej

Tamam sorun değil, bir seçenek ben dinleyici olay üst parçasından her alt parçaya geçmek gerekir olmasıdır. ama bu ideal bir yol değil çünkü bunu her fragman için yapmam gerekiyor.
Kishan Vaghela

@KishanVaghela Etkinlikteki yöneticiyi düşünüyordum. Bu arayüzle parçalar ekleyebilir / kaldırabilirsiniz. Ancak bu yaklaşımla, alt parçaları olan her parçada yönetici uygulamanız gerekir. Yani belki daha iyi bir çözüm singleton olarak yönetici oluşturmak olacaktır. Sonra parçaları / nesneleri eklemek / kaldırmak ve bu yöneticinin 'onBackPressed' yöntemini çağırmak 'onBackPressed' gerekir. Bu yöntem, yöneticiye eklenen her nesnede 'onClick' yöntemini çağırır. Bu sizin için aşağı yukarı açık mı?
Błażej

8

Projenize aşağıdaki gibi bir arayüz eklemelisiniz;

public interface OnBackPressed {

     void onBackPressed();
}

Ve sonra, bu arayüzü parçanıza uygulamalısınız;

public class SampleFragment extends Fragment implements OnBackPressed {

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

}

Ve bu onBackPressed olayını aşağıdaki gibi onBackPressed olayındaki faaliyetleriniz altında tetikleyebilirsiniz;

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

Bu iyi bir yöntem, ancak dikkatli olun, getActivity().onBackPressed();istisnayı çağıracağı için çağırma: "java.lang.StackOverflowError: yığın boyutu 8MB".
CoolMind

8

Bu sadece hile yapacak küçük bir kod:

 getActivity().onBackPressed();

Umarım birine yardımcı olur :)


4
Etkinlik yöntemini çağırmamak için davranışı geçersiz kılmayı istedi.
31:31

2
Dikkatlice okudum, teşekkürler. Çözümünüz, nasıl geçersiz kılınacağını sorduğunda onBackPressed yöntemini tekrar Aktiviteye aktarır.
mwieczorek

7

EventBus kullanıyorsanız, muhtemelen çok daha basit bir çözümdür:

Parçanızda:

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

Etkinlik sınıfınızda şunları tanımlayabilirsiniz:

@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 sadece bir POJO nesnesidir

Bu süper temiz ve hiçbir arayüz / uygulama güçlük.


7

bu benim çözümüm:

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

ve Fragment'ta:

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

        // some codes

        return true;
    }
});

6

Kullanılması Navigasyon bileşenini bunu yapabilirsiniz böyle :

Java

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 () kullanmaya ne dersiniz?

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

5
gerçek parçadan başka bir parçaya gitmeye ne dersin, yöntemini kullanarak ne olacak? :)
Choletski

En yararsız cevap. Yazmak i = iveya ile aynıdır 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;
        }
    });
}

Yalnızca parçaların odaklanabilir bir görünümü yoksa (metin düzenleme gibi), parçaların bu yanıtın yaptığı gibi düğmenin kendisini geri ele almasına izin verin. Aksi takdirde, ilk cevap yazılırken parçaları içeren etkinliğin OnBackPress () tarafından yapmasına izin verin; çünkü odaklanabilir görüş parçanın odak noktasını çalacaktır. Bununla birlikte, clearFocus () yöntemiyle tüm netleştirilebilir görünümleri net bir şekilde odaklayarak parça için odağı tekrar kazanabilir ve parçaya yeniden odaklayabiliriz.
Nguyen 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();
    }
}

parçanızda aşağıdakileri ekleyin, mainactivity'nin arayüzünü uygulamayı unutmayın.

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

Benim çözümümde (Kotlin);

Ben bir parametre olarak onBackAlternative fonksiyonunu kullanıyorum BaseActivity .

BaseActivity

abstract class BaseActivity {

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

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

Ben kümesi için bir işlevi vardır onBackPressAlternative üzerinde 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
     }
}

Sonra onBackPressAlternative'im parçalar üzerinde kullanıma hazır.

Alt Parçalar

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

3

Ft.addToBackStack () yöntemini uygulamayın, böylece geri düğmesine bastığınızda etkinliğiniz sona erer.

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


3

Şu adımları uygulamanız yeterlidir:

Her zaman bir parça eklerken,

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

Ardından ana aktivitede geçersiz kılın onBackPressed()

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

Uygulamanızdaki geri düğmesini işlemek için,

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

Bu kadar!


3

Etkinlik yaşam döngüsünde, FragmentActivity veya AppCompatActivity kullandığımızda her zaman android geri düğmesi FragmentManager işlemleri ile ilgilenir.

Backstack'i işlemek için backstack sayısını işlememize veya hiçbir şeyi etiketlememize gerek yoktur, ancak bir parça eklerken veya değiştirirken odaklanmaya devam etmeliyiz. Geri düğmesi kutularını işlemek için lütfen aşağıdaki parçacıkları bulun,

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

Burada, uygulamamın ana sayfası olduğu için ev parçam için geri yığın eklemeyeceğim. HomeFragment'a addToBackStack eklerseniz, uygulama tüm kokuyu kaldırmak için bekler, ardından boş ekran alırız, bu yüzden aşağıdaki koşulu koruyorum,

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

Şimdi, daha önce eklenen acitvity parçasını görebilirsiniz ve HomeFragment'a ulaştığınızda uygulama çıkacaktır. aşağıdaki snippet'lere de bakabilirsiniz.

@Override
public void onBackPressed() {

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

3

Parça: Bir yöntem yerleştirerek bir BaseFragment yapın:

 public boolean onBackPressed();

Aktivite:

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

Etkinliğiniz ekli ve görünür parçalar üzerinde çalışacak ve her birinde onBackPressed () yöntemini çağıracak ve bunlardan biri 'true' değerini döndürürse iptal edilecektir (yani ele alındığı için başka işlem yapılmaz).


Efendim, bu en zarif yol ve mükemmel çalışıyor!
asozcan

3

Göre AndroidX sürüm notları , androidx.activity 1.0.0-alpha01serbest ve tanıtır edilir ComponentActivity, mevcut yeni bir temel sınıf FragmentActivityveAppCompatActivity . Ve bu sürüm bize yeni bir özellik getiriyor:

Artık bir kayıt olabilirsiniz OnBackPressedCallbackyoluyla addOnBackPressedCallbackalmak onBackPressed()için aktivitesinde yöntemini geçersiz gerek kalmadan geri aramalar.


Bunun bir örneğini gösterebilir misiniz? Çözmek için bu yöntemi alamıyorum.
Bryan Bryce

1
@BryanBryce Resmi bir dokümanı bulamadım ancak resmi dokümanı: developer.android.com/reference/androidx/activity/…
TonnyL

Yöntem b / c'yi çözmeyecek AppCompatActivity aslında androidx.activity.ComponentActivity yerine androidx.core.app.ComponentActivity'den genişliyor.
wooldridgetm

3

Parçayı geri basmak için parçayı aktiviteye kaydedebilirsiniz:

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

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

kullanımı:

Fragman:

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

Faaliyette:

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

OnBackPressed'i kullanarak özel geri gezinme sağlamak parçanın içindeki geri aramalarla artık daha kolay.

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

Bazı koşullara dayalı olarak varsayılan geri işlem istiyorsanız, aşağıdakileri kullanabilirsiniz:

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

3

Parçanın onCreate yönteminin içine aşağıdakileri ekleyin:

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

Bu myFragment backpress ekledikten sonra çalışıyor ama şimdi etkinlik backpress vermedi
Sunil Chaudhary

2

En iyi çözüm,

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

            }
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.