যদি বিদ্যমান থাকে তবে কীভাবে ব্যাকস্ট্যাক থেকে টুকরো পুনরায় শুরু করবেন


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|
___

সম্পাদনা: (এ - সি দ্বারা প্রস্তাবিত)

এটি আমার চেষ্টা কোড:

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

সম্পাদনা

সমস্যাটি হ'ল - যখন আমি A এবং তারপরে বি চালু করব, তারপরে ব্যাক বোতাম টিপুন, বি সরানো হবে এবং এ আবার চালু হবে res এবং আবার ফিরে বোতাম টিপে অ্যাপ্লিকেশনটি প্রস্থান করা উচিত। তবে এটি একটি ফাঁকা উইন্ডো দেখাচ্ছে এবং এটি বন্ধ করার জন্য অন্য একটি প্রেসের প্রয়োজন।

এটি কারণ FragmentTransactionআমরা পিছনে টুকরা পরে পপ করতে পারেন তা নিশ্চিত করার জন্য পিছনে স্ট্যাক যোগ করা হচ্ছে। এর জন্য একটি দ্রুত ফিক্সটি onBackPressed()ব্যাক স্ট্যাকটিতে কেবল 1 টি থাকলে ক্রিয়াকলাপকে ওভাররাইড এবং সমাপ্ত করা হয়Fragment

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

ডুপ্লিকেট ব্যাক স্ট্যাক এন্ট্রি সম্পর্কিত, আপনার শর্তসাপেক্ষ বিবৃতি যা খণ্ডটিকে পপ করা হয়নি তা প্রতিস্থাপন করে তা আমার মূল কোড স্নিপেটের চেয়ে পরিষ্কার different আপনি যা করছেন তা পেছনের স্ট্যাকটি পপ করা হয়েছে কিনা তা নির্বিশেষে যুক্ত করছে।

এর মতো কিছু আপনার পছন্দসই কাছাকাছি হওয়া উচিত:

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

সম্পাদনা # 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
@ Habষভশ্রীবাস্তব মনে রাখবেন একটি "পপ" সাধারণত শীর্ষতম খণ্ডটি ধ্বংস করে দেয়। প্রথমে কোন পদ্ধতিটি বলা হয় তা নির্ভর করে - ফ্রেগমেন্ট লাইফাইসাইকালে একবার দেখুন। আপনার ক্ষেত্রে, আমি একটি ব্যবহার করব OnBackstackChangedListenerযাতে আপনি গ্যারান্টি দিতে পারেন যে আপনি সঠিক খণ্ডটির সাথে কাজ করছেন।
এ - সি

2
@ Habষভশ্রীবাস্তব যে কারণে আমিও এটির পরামর্শ দিয়েছি OnBackstackChangedListener
এ - সি

1
পছন্দ করুন ফিরে যখন আমি এই উত্তরটি দিয়েছিলাম আমি কখনই ভাবছিলাম না 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 ();
    }
}

যা মূলত এই প্রশ্নটিতে পোস্ট করা হয়েছিল


এর মতো উত্তর দেওয়া বিধিগুলির বিপরীতে কিনা তা দয়া করে আমাকে জানান I আমি এই পোস্টটিতে লোকদের পক্ষে এটি সহজেই দেখাতে চেয়েছিলাম। ধন্যবাদ ..
এরলক্সম্যান

এটি বিধিগুলির বিপরীতে (সম্ভবত নয়) নিশ্চিত কিনা তা আমি নিশ্চিত নই, তবে এটি অবশ্যই সহায়ক
কাঠির

1
তৃতীয় লাইনের ব্যবহার কী? -> ম্যানেজার.ফাইন্ডফ্রেগমেন্টবিট্যাগ (ট্যাগ); দেখে মনে হচ্ছে এটি কিছুই করছে না ..
রিক ভ্যান ভেলজেন

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

এবং আপনার এক্সএমএল লেআউটটির ভিতরে দয়া করে ব্যবহার করুন

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

Clickable এর অর্থ এটি একটি পয়েন্টার ডিভাইস দ্বারা ক্লিক করা যেতে পারে বা একটি টাচ ডিভাইস দ্বারা আলতো চাপতে পারে।

Focusableএর অর্থ এটি কোনও কীবোর্ডের মতো কোনও ইনপুট ডিভাইস থেকে ফোকাস অর্জন করতে পারে। কীবোর্ডের মতো ইনপুট ডিভাইসগুলি ইনপুটগুলির উপর ভিত্তি করে কোনও ইনপুট ইভেন্টগুলি কী ভিউ প্রেরণ করবে তা সিদ্ধান্ত নিতে পারে না, তাই তারা তাদের ফোকাসযুক্ত দর্শনে প্রেরণ করে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.