यदि मौजूद है तो बैकस्टैक से फ्रैगमेंट कैसे फिर से शुरू करें


151

मैं टुकड़ों का उपयोग करना सीख रहा हूं। मेरे पास इसके तीन उदाहरण हैं Fragmentजो कक्षा के शीर्ष पर आरंभिक हैं। मैं इस तरह की गतिविधि में अंश जोड़ रहा हूं:

घोषणा करना और आरंभ करना:

Fragment A = new AFragment();
Fragment B = new BFragment();
Fragment C = new CFragment();

/ जोड़ना की जगह:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, A);
ft.addToBackStack(null);
ft.commit();

ये स्निपेट ठीक से काम कर रहे हैं। प्रत्येक टुकड़ा गतिविधि से जुड़ा हुआ है, और बिना किसी समस्या के बैक स्टैक में सहेजा गया है।

तो जब मैं शुभारंभ A, C, और फिर B, इस तरह ढेर दिखता है:

| |
|B|
|C|
|A|
___

और जब मैं 'बैक' बटन दबाता हूं, Bनष्ट हो जाता है और Cफिर से शुरू हो जाता है।

लेकिन, जब मैं Aदूसरी बार टुकड़ा लॉन्च करता हूं, तो बैक स्टैक से फिर से शुरू करने के बजाय, इसे बैक स्टैक के शीर्ष पर जोड़ा जाता है

| |
|A|
|C|
|A|
___

लेकिन मैं Aइसके ऊपर (यदि कोई हो) सभी टुकड़ों को फिर से शुरू करना और नष्ट करना चाहता हूं । वास्तव में, मैं सिर्फ डिफ़ॉल्ट बैक स्टैक व्यवहार को पसंद करता हूं।

मैं इसे कैसे पूर्ण करूं?

उम्मीद: ( Aफिर से शुरू किया जाना चाहिए और शीर्ष टुकड़े नष्ट हो जाना चाहिए)

| |
| |
| |
|A|
___

संपादित करें: (A - C द्वारा सुझाया गया)

यह मेरा प्रयास कोड है:

private void selectItem(int position) {
        Fragment problemSearch = null, problemStatistics = null;
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        String backStateName = null;
        Fragment fragmentName = null;
        boolean fragmentPopped = false;
        switch (position) {
        case 0:
            fragmentName = profile;
            break;
        case 1:
            fragmentName = submissionStatistics;
            break;
        case 2:
            fragmentName = solvedProblemLevel;
            break;
        case 3:
            fragmentName = latestSubmissions;
            break;
        case 4:
            fragmentName = CPExercise;
            break;
        case 5:
            Bundle bundle = new Bundle();
            bundle.putInt("problem_no", problemNo);
            problemSearch = new ProblemWebView();
            problemSearch.setArguments(bundle);
            fragmentName = problemSearch;
            break;
        case 6:
            fragmentName = rankList;
            break;
        case 7:
            fragmentName = liveSubmissions;
            break;
        case 8:
            Bundle bundles = new Bundle();
            bundles.putInt("problem_no", problemNo);
            problemStatistics = new ProblemStatistics();
            problemStatistics.setArguments(bundles);
            fragmentName = problemStatistics;
        default:
            break;
        }
        backStateName = fragmentName.getClass().getName();
        fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
        if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
        }
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(backStateName);
        ft.commit();

        // I am using drawer layout
        mDrawerList.setItemChecked(position, true);
        setTitle(title[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

समस्या यह है, जब मैं लॉन्च करता हूं Aऔर फिर B, फिर 'बैक' दबाता हूं , Bहटा दिया जाता है और Aफिर से शुरू किया जाता है। और 'बैक' को दूसरी बार दबाने पर ऐप से बाहर निकल जाना चाहिए। लेकिन यह एक खाली खिड़की दिखा रहा है और मुझे इसे बंद करने के लिए तीसरी बार वापस प्रेस करना होगा।

इसके अलावा, जब मैं लॉन्च करता हूं A, तब B, फिर C, Bफिर ...

अपेक्षित होना:

| |
| |
|B|
|A|
___

वास्तविक:

| |
|B|
|B|
|A|
___

क्या मुझे onBackPressed()किसी अनुकूलन के साथ ओवरराइड करना चाहिए या मुझे कुछ याद आ रहा है?

जवाबों:


279

दस्तावेज़ीकरण पढ़ना , लेनदेन के नाम या प्रतिबद्ध द्वारा प्रदान की गई आईडी के आधार पर बैक स्टैक को पॉप करने का एक तरीका है। नाम का उपयोग करना आसान हो सकता है क्योंकि इसे किसी ऐसे नंबर का ट्रैक रखने की आवश्यकता नहीं होनी चाहिए जो "अद्वितीय बैक स्टैक प्रविष्टि" लॉजिक को बदल और मजबूत कर सके।

चूंकि आप प्रति केवल एक बैक स्टैक प्रविष्टि चाहते हैं Fragment, इसलिए बैक स्टेट का नाम फ्रैगमेंट का वर्ग नाम (थ्रू getClass().getName()) बनाएं । फिर जब एक की जगह Fragment, popBackStackImmediate()विधि का उपयोग करें । यदि यह सही है, तो इसका मतलब है कि बैक स्टैक में फ्रैगमेंट का एक उदाहरण है। यदि नहीं, तो वास्तव में फ्रैगमेंट प्रतिस्थापन तर्क को निष्पादित करें।

private void replaceFragment (Fragment fragment){
  String backStateName = fragment.getClass().getName();

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment);
    ft.addToBackStack(backStateName);
    ft.commit();
  }
}

संपादित करें

समस्या यह है - जब मैं ए और फिर बी लॉन्च करता हूं, तो वापस बटन दबाएं, बी हटा दिया जाता है और ए को फिर से शुरू किया जाता है। और फिर से वापस बटन दबाने पर ऐप से बाहर निकल जाना चाहिए। लेकिन यह एक खाली खिड़की दिखा रहा है और इसे बंद करने के लिए एक और प्रेस की आवश्यकता है।

ऐसा इसलिए है क्योंकि यह FragmentTransactionसुनिश्चित करने के लिए बैक स्टैक में जोड़ा जा रहा है कि हम बाद में टुकड़े को पॉप कर सकें। onBackPressed()यदि बैक स्टैक में केवल 1 है, तो इसके लिए एक त्वरित सुधार गतिविधि को ओवरराइड करना और समाप्त करना हैFragment

@Override
public void onBackPressed(){
  if (getSupportFragmentManager().getBackStackEntryCount() == 1){
    finish();
  }
  else {
    super.onBackPressed();
  }
}

डुप्लिकेट बैक स्टैक प्रविष्टियों के बारे में, आपके सशर्त विवरण जो टुकड़े को प्रतिस्थापित करता है यदि इसे पॉप नहीं किया गया है तो यह मेरे मूल कोड स्निपेट की तुलना में स्पष्ट रूप से भिन्न है। आप जो कर रहे हैं वह बैक स्टैक में शामिल है, चाहे बैक स्टैक पॉपप किया गया हो या नहीं।

कुछ इस तरह से होना चाहिए कि आप क्या चाहते हैं:

private void replaceFragment (Fragment fragment){
  String backStateName =  fragment.getClass().getName();
  String fragmentTag = backStateName;

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment, fragmentTag);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(backStateName);
    ft.commit();
  } 
}

सशर्त को उसी खंड का चयन करने के बाद से बदल दिया गया था, जबकि यह दिखाई दे रहा था कि डुप्लिकेट प्रविष्टियां भी थीं।

कार्यान्वयन:

मैं सुझाव देता हूं कि replaceFragment()आप अपने कोड में अपडेट की गई विधि को अलग से न लें। सभी तर्क इस पद्धति में सम्‍मिलित हैं और आसपास के भागों को ले जाने से समस्‍या हो सकती है।

इसका मतलब है कि आपको replaceFragment()अपनी कक्षा में अपडेट की गई विधि को कॉपी करना चाहिए फिर बदलना चाहिए

backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();

तो यह बस है

replaceFragment (fragmentName);

EDIT # 2

बैक स्टैक में परिवर्तन होने पर दराज को अपडेट करने के लिए, एक विधि बनाएं जो एक फ्रैगमेंट में स्वीकार करता है और वर्ग नामों की तुलना करता है। यदि कुछ भी मेल खाता है, तो शीर्षक और चयन बदलें। OnBackStackChangedListenerयदि कोई मान्य फ़्रैगमेंट है, तो उसे भी जोड़ें और उसे अपनी अपडेट विधि को कॉल करें।

उदाहरण के लिए, गतिविधि में onCreate(), जोड़ें

getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {

  @Override
  public void onBackStackChanged() {
    Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
    if (f != null){
      updateTitleAndDrawer (f);
    }

  }
});

और दूसरी विधि:

private void updateTitleAndDrawer (Fragment fragment){
  String fragClassName = fragment.getClass().getName();

  if (fragClassName.equals(A.class.getName())){
    setTitle ("A");
    //set selected item position, etc
  }
  else if (fragClassName.equals(B.class.getName())){
    setTitle ("B");
    //set selected item position, etc
  }
  else if (fragClassName.equals(C.class.getName())){
    setTitle ("C");
    //set selected item position, etc
  }
}

अब, जब भी बैक स्टैक बदलता है, तो शीर्षक और चेक की गई स्थिति दृश्यमान होगी Fragment


आपके उत्तर के लिए धन्यवाद। :) यह आंशिक रूप से काम कर रहा है। मैंने अपने प्रश्न को प्रगति के साथ संपादित किया है। कृपया देख लें)
कैदुल

2
@ ऋषभ श्रीवास्तव एक "पॉप" को ध्यान में रखते हैं जो आमतौर पर सबसे ऊपरी टुकड़े को नष्ट कर देता है। किस विधि को पहले कहा जाता है, यह निर्भर करता है - फ्रैगमेंट लाइफसाइकल पर एक नजर। आपके मामले के लिए, मैं इसका उपयोग करूँगा OnBackstackChangedListenerताकि आप गारंटी दे सकें कि आप सही फ़्रैगमेंट के साथ काम कर रहे हैं।
ए - सी

2
@ ऋषभ श्रीवास्तव यही कारण है कि मैंने भी सुझाव दिया OnBackstackChangedListener
A - C

1
@AlexanderFarber आप सही हैं। जब मैंने यह उत्तर दिया तो मैं इसके बारे में नहीं सोच रहा था instanceof:)
ए - सी

2
आपका समाधान अच्छा लगता है, लेकिन यदि आपके पास तीन टुकड़े हैं और उन पर क्लिक करें A-B-C-Bऔर एक बार वापस दबाएं और आप वापस Aनहीं जा रहे हैं C। ऐसा इसलिए है क्योंकि popBackStackImmediateन केवल दिए गए टैग को पॉप करता है, बल्कि ऊपर का सब कुछ भी। क्या वहां कोई काम है?
राएगलान

10

मुझे लगता है कि इस विधि से मेरी समस्या हल हो गई है:

public static void attachFragment ( int fragmentHolderLayoutId, Fragment fragment, Context context, String tag ) {


    FragmentManager manager = ( (AppCompatActivity) context ).getSupportFragmentManager ();
    FragmentTransaction ft = manager.beginTransaction ();

    if (manager.findFragmentByTag ( tag ) == null) { // No fragment in backStack with same tag..
        ft.add ( fragmentHolderLayoutId, fragment, tag );
        ft.addToBackStack ( tag );
        ft.commit ();
    }
    else {
        ft.show ( manager.findFragmentByTag ( tag ) ).commit ();
    }
}

जो मूल रूप से इस प्रश्न में पोस्ट किया गया था


कृपया मुझे बताएं कि क्या इस तरह का उत्तर देना उन नियमों के खिलाफ है जो मैं सिर्फ इस पोस्ट में लोगों के लिए देखना आसान बनाना चाहता था। धन्यवाद ..
erluxman

मुझे यकीन नहीं है कि यह नियमों के खिलाफ है (शायद नहीं), लेकिन यह निश्चित रूप से मददगार है
काथिर

1
तीसरी पंक्ति का उपयोग क्या है? -> Manager.findFragmentByTag (टैग); ऐसा लगता है कि यह कुछ भी नहीं कर रहा है ..
रिक वैन वेलज़ेन

3

चरण 1: अपनी गतिविधि वर्ग के साथ एक इंटरफ़ेस लागू करें

public class AuthenticatedMainActivity extends Activity implements FragmentManager.OnBackStackChangedListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        .............
        FragmentManager fragmentManager = getFragmentManager();           
        fragmentManager.beginTransaction().add(R.id.frame_container,fragment, "First").addToBackStack(null).commit();
    }

    private void switchFragment(Fragment fragment){            
      FragmentManager fragmentManager = getFragmentManager();
      fragmentManager.beginTransaction()
        .replace(R.id.frame_container, fragment).addToBackStack("Tag").commit();
    }

    @Override
    public void onBackStackChanged() {
    FragmentManager fragmentManager = getFragmentManager();

    System.out.println("@Class: SummaryUser : onBackStackChanged " 
            + fragmentManager.getBackStackEntryCount());

    int count = fragmentManager.getBackStackEntryCount();

    // when a fragment come from another the status will be zero
    if(count == 0){

        System.out.println("again loading user data");

        // reload the page if user saved the profile data

        if(!objPublicDelegate.checkNetworkStatus()){

            objPublicDelegate.showAlertDialog("Warning"
                    , "Please check your internet connection");

        }else {

            objLoadingDialog.show("Refreshing data..."); 

            mNetworkMaster.runUserSummaryAsync();
        }

        // IMPORTANT: remove the current fragment from stack to avoid new instance
        fragmentManager.removeOnBackStackChangedListener(this);

    }// end if
   }       
}

चरण 2: जब आप किसी अन्य खंड को कॉल करते हैं तो इस विधि को जोड़ें:

String backStateName = this.getClass().getName();

FragmentManager fragmentManager = getFragmentManager();
fragmentManager.addOnBackStackChangedListener(this); 

Fragment fragmentGraph = new GraphFragment();
Bundle bundle = new Bundle();
bundle.putString("graphTag",  view.getTag().toString());
fragmentGraph.setArguments(bundle);

fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragmentGraph)
.addToBackStack(backStateName)
.commit();

2

मुझे पता है कि इस प्रश्न का उत्तर देने में काफी देर हो चुकी है लेकिन मैंने इस समस्या को स्वयं हल किया और सभी के साथ साझा करने के लायक समझा

public void replaceFragment(BaseFragment fragment) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    final FragmentManager fManager = getSupportFragmentManager();
    BaseFragment fragm = (BaseFragment) fManager.findFragmentByTag(fragment.getFragmentTag());
    transaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);

    if (fragm == null) {  //here fragment is not available in the stack
        transaction.replace(R.id.container, fragment, fragment.getFragmentTag());
        transaction.addToBackStack(fragment.getFragmentTag());
    } else { 
        //fragment was found in the stack , now we can reuse the fragment
        // please do not add in back stack else it will add transaction in back stack
        transaction.replace(R.id.container, fragm, fragm.getFragmentTag()); 
    }
    transaction.commit();
}

और ऑन बैकपैक्ड () में

 @Override
public void onBackPressed() {
    if(getSupportFragmentManager().getBackStackEntryCount()>1){
        super.onBackPressed();
    }else{
        finish();
    }
}

1
@ एक्स-ब्लैक ... बेसफ्रैगमेंट एप्लिकेशन में उपयोग होने वाले हर टुकड़े के लिए एक बेस क्लास है।
विवेक प्रताप सिंह

0
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {

    @Override
    public void onBackStackChanged() {

        if(getFragmentManager().getBackStackEntryCount()==0) {
            onResume();
        }    
    }
});

0

आसान समाधान इस लाइन को बदल देगा

ft.replace(R.id.content_frame, A); सेवा ft.add(R.id.content_frame, A);

और अपने XML लेआउट के अंदर का उपयोग करें

  android:background="@color/white"
  android:clickable="true"
  android:focusable="true"

Clickable इसका मतलब है कि यह एक पॉइंटर डिवाइस द्वारा क्लिक किया जा सकता है या एक टच डिवाइस द्वारा टैप किया जा सकता है।

Focusableइसका मतलब है कि यह कीबोर्ड की तरह इनपुट डिवाइस से फोकस हासिल कर सकता है। कीबोर्ड जैसे इनपुट डिवाइस यह तय नहीं कर सकते हैं कि इनपुट के आधार पर अपने इनपुट इवेंट को किस दृश्य को भेजें, इसलिए वे उन्हें उस दृश्य पर भेजते हैं जिस पर ध्यान केंद्रित किया गया है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.