3

I'm studying MVP.

I have and activity with nav menu and I change fragments from menu like this:

switch (id) {
    case R.id.nav_status:
        fragmentManager.beginTransaction().replace(R.id.fr_main, mAppProductFragment).commit();
        Log.d(TAG, "Выбрано меню статус");
        break;           
    case R.id.nav_schemas:
        AppRedirectFragment schemasFragment = new AppRedirectFragment();
        fragmentManager.beginTransaction().replace(R.id.fr_main, schemasFragment).commit();
        Log.d(TAG, "Выбрано меню переадресация");
        break;
}

The problem is that when I change the fragment, my previous fragment is destroyed. It calls the following callbacks:

D/AppProductPresenter: onStopDetouchView()
D/AppProductFragment:  onDestroyView
D/AppProductFragment: onDestroy()

It loses its reference to the presenter and also dsipose all retrofit requests. mPresenter.onDestroyView();, so all my network operations are destroyd. But Id like to run them in backgroud. But its disposed.

public void onDestroy() {
        super.onDestroy();
        mPresenter.onDestroyView();
    }

So how can change fragments without calling onDestroy being called? I read that I have to use add instead of fragmentManager.beginTransaction().replace So how do it correctly?

Masquitos
  • 554
  • 4
  • 22
  • why you didn't inject presenter back in onAttach? – Selvin Apr 19 '18 at 14:28
  • @Selvin How to do that? My problem that i dispose CompositeDisposable inside `mPresenter.onDestroyView();`, so all my network operations are destroyd. But Id like to run them in backgroud. But its disposed. `public void onDestroy() { super.onDestroy(); mPresenter.onDestroyView(); }` – Masquitos Apr 19 '18 at 14:41
  • See https://stackoverflow.com/a/76120054/14773942 – ʀᴀʜɪʟ Apr 27 '23 at 11:51

3 Answers3

3

When you use

fragmentManager
.beginTransaction()
.replace(R.id.fr_main, schemasFragment)
//addToBackstack(String name) //if you want to keep transaction to backstack
.commit();

replace() will remove previous fragment and add new one, so you cant avoid onDestroy(). But when you call

fragmentManager
.beginTransaction()
.add(R.id.fr_main, schemasFragment)
//addToBackstack(String name) //if you want to keep transaction to backstack
.commit();

you just add new fragment over previous one and previous fragment will still exist. But if you will add a lot of fragments without removing previous - your app can become sluggish. If your retrofit requests are complex - good decision is to do it in services.

H.Taras
  • 753
  • 4
  • 15
1

I solve a problem. The answer is use combo of: .add .attach and .detach methods. I create a function that replace a fragmetns like this:

 switch (id) {
            case R.id.nav_product:
                replaceFragment(PRODUCT_FRAGMENT);
                break;
            case R.id.nav_redirection:
                replaceFragment(REDIRECTION_FRAGMENT);
                break;
}

private void replaceFragment(String tag) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    Fragment currentFragment = fragmentManager.findFragmentById(R.id.fr_container);
    Fragment nextFragment = fragmentManager.findFragmentByTag(tag);

    Log.d(TAG, "f detached: " + currentFragment.toString());
    transaction.detach(currentFragment);

    if (nextFragment == null) {
        nextFragment = createFragment(tag);
        transaction.add(R.id.fr_container, nextFragment, tag);
    } else {
        Log.d(TAG, "f attach: " + nextFragment.toString());
        transaction.attach(nextFragment);
    }
    transaction.commit();
}

private Fragment createFragment(String tag) {
    Fragment result = null;
    switch (tag) {
        case CALLHISTORY_FRAGMENT:
            result = new AppCallHistoryFragment();
            break;
        case CALLTRACKING_FRAGMENT:
            result = new AppCallTrackingFragment();
            break;
        case REDIRECTION_FRAGMENT:
            result = new AppRedirectFragment();
            break;
    }
    Log.d(TAG, "create: " + result.toString());
    return result;
}

Now its ok. Fragments are Destroyed only when Activity is destroying.

Masquitos
  • 554
  • 4
  • 22
  • note that calling `transaction.detach(currentFragment);` may cause NPE if the current fragment still not created. – Maher Nabil Dec 30 '21 at 00:05
0

I've a similar problem. I'm using a ViewPager to manager a view of fragments, and a ArrayList to store the instances of them. I'm also the bottomNavigationView to take the fragment, you can change to your navigate method. As I know the ordem that I create the fragments and put into the list, I know the index to set on ClickListener of my BottomNavigationView.

MainActivity.java

public class MainActivity extends AppCompatActivity  {

List<Fragment> fragmentList;
MyAdapter myAdapter;
ViewPager viewPager;

BottomNavigationView bottomNavigationView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.i(TAG,"onCreate()");

    fragmentList = new ArrayList<>();
    fragmentList.add(new Fragment1());
    fragmentList.add(new Fragment2());

    bottomNavigationView = findViewById(R.id.bottomNavigationView);
    viewPager = findViewById(R.id.viewPagerAppActivity);

    myAdapter = new MyAdapter(getSupportFragmentManager());
    viewPager.setAdapter(myAdapter);
    viewPager.setCurrentItem(0);

    bottomNavigationView.setOnNavigationItemSelectedListener(botNavViewItemSelectedListener());

}

public BottomNavigationView.OnNavigationItemSelectedListener botNavViewItemSelectedListener() {
    return new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {

            switch (item.getItemId()) {
                case R.id.itemFragment1:

                    viewPager.setCurrentItem(0);
                    break;

                case R.id.itemFragment2:

                    viewPager.setCurrentItem(1);
                    break;
            }

            return true;
        }
    };
}

public class MyAdapter extends FragmentStatePagerAdapter {

    public MyAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }
}

}

activity_main.xml

<android.support.constraint.ConstraintLayout android:layout_width="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPagerAppActivity"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/bottomNavigationView"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginBottom="0dp"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="0dp"
        app:layout_constraintVertical_bias="1.0" />

    <android.support.design.widget.BottomNavigationView
        app:menu="@menu/menu_bottom"
        android:id="@+id/bottomNavigationView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:itemBackground="@color/colorPrimary"
        app:itemIconTint="@drawable/bottom_bar_item_selector"
        app:itemTextColor="@drawable/bottom_bar_item_selector"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

</android.support.constraint.ConstraintLayout>

menu_bottom.xml to populate my menu of navigation

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/itemFragment1" android:title="Frag1" android:enabled="true" app:showAsAction="always|withText" android:icon="@mipmap/ic_launcher"/>
    <item android:id="@+id/itemFragment2" android:title="Frag2" android:enabled="true" app:showAsAction="always|withText" android:icon="@mipmap/ic_launcher"/>


</menu>
Augusto
  • 3,825
  • 9
  • 45
  • 93