web-dev-qa-db-fra.com

Remplacer un fragment par un autre dans ViewPager

J'ai des problèmes lorsque j'essaie de remplacer un Fragment par un autre dans un ViewPager.

Situation actuelle

J'ai un ViewPager avec 3 pages, chacune étant un Fragment. En première page, j'ai un ListView à l'intérieur d'un ListFragment ("FacturasFragment"). Lorsque je clique sur un élément de cette liste, j'utilise la méthode onListItemClick pour gérer cet événement.

Ce que je veux

  • Lorsque vous cliquez sur un élément de la liste, je souhaite remplacer ListFragment (contient une liste de factures) par un autre Fragment ("DetallesFacturaFragment", contient les détails de la facture).
  • Quand je suis dans "DetallesFacturaFragment" et que j'appuie sur Retour Button, devrait revenir à ListFragment.
  • Le défilement entre les pages ne devrait pas changer Fragment affiché dans la première. Si je suis en première page avec "DetallesFacturaFragment" et que vous passez à la deuxième page, lorsque vous revenez à la première, continuez à afficher "DetallesFacturaFragment".

Code

FragmentActivity

public class TabsFacturasActivity extends SherlockFragmentActivity {

    private MyAdapter mAdapter;
    private ViewPager mPager;
    private PageIndicator mIndicator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_pager);
        mAdapter = new MyAdapter(getSupportFragmentManager());

        mPager = (ViewPager)findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

        mIndicator = (TitlePageIndicator)findViewById(R.id.indicator);
        mIndicator.setViewPager(mPager);
    }

    private static class MyAdapter extends FragmentPagerAdapter {

        private String[] titles = { "VER FACTURAS", "VER CONSUMO", "INTRODUCIR LECTURA" };
        private final FragmentManager mFragmentManager;
        private Fragment mFragmentAtPos0;
        private Context context;

        public MyAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
            mFragmentManager = fragmentManager;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return titles[position];
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0: // Fragment # 0
                return new FacturasFragment();
            case 1: // Fragment # 1
                return new ConsumoFragment();
            case 2:// Fragment # 2
                return new LecturaFragment();
            }
            return null;
        }

        @Override
        public int getCount() {
            return titles.length;
        }

        @Override
        public int getItemPosition(Object object)
        {
            if (object instanceof FacturasFragment && mFragmentAtPos0 instanceof DetallesFacturaFragment)
                return POSITION_NONE;
            return POSITION_UNCHANGED;
        }
    }

}

ListFragment

public class FacturasFragment extends ListFragment {

    private ListView lista;

    private ArrayList<TuplaFacturaWS> facturas;

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

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.activity_facturas, container, false);
        facturas = myApplication.getFacturas();

        lista = (ListView) view.findViewById(id.list);

        MyAdapter myAdaptador = new MyAdapter(this, facturas);
        setListAdapter(myAdaptador);

        return view;
    }

    public void onListItemClick (ListView l, View v, int position, long id) {
        myApplication.setFacturaActual(position);
        mostrarDatosFactura();
    }


    private void mostrarDatosFactura() {
        final DetallesFacturaFragment fragment = new DetallesFacturaFragment();
        FragmentTransaction transaction = null;
        transaction = getFragmentManager().beginTransaction();
        transaction.replace(R.id.pager, fragment); //id of ViewPager
        transaction.addToBackStack(null);
        transaction.commit();
    }

    private class MyAdapter extends BaseAdapter {
        private final FacturasFragment actividad;
        private final ArrayList<TuplaFacturaWS> facturas;

        public MyAdapter(FacturasFragment facturasActivity, ArrayList<TuplaFacturaWS> facturas) {
            super();
            this.actividad = facturasActivity;
            this.facturas = facturas;
        }

        @Override
        public View getView(int position, View convertView, 
                ViewGroup parent) {
            LayoutInflater inflater = actividad.getLayoutInflater(null);
            View view = inflater.inflate(R.layout.list_row, null, true);
            //Set data to view
            return view;
        }

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

        @Override
        public Object getItem(int pos) {
            return facturas.get(pos);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        private OnClickListener checkListener = new OnClickListener()
        {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO Auto-generated method stub

            }

        };
    }
}

Fragment

public class DetallesFacturaFragment extends SherlockFragment {

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

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.activity_factura, container, false);

        //Set data to view

        return view;
    }
}

Pour le moment, lorsque je clique sur un élément de la liste, une vue en blanc apparaît en première page. J'ai vérifié et la méthode onCreateView de "DetallesFacturaFragment" est exécutée, mais rien ne s'affiche sur cette page.

Et la première fois que je clique sur un élément de la liste, il affiche cet écran blanc. Mais après être revenu à la liste, je dois cliquer deux fois sur un élément de la liste pour afficher un écran blanc.

J'ai cherché sur Google et examiné de nombreuses questions, mais je n'ai trouvé personne résolu avec du code complet.

53
Lyd

Après tant d’heures passées, j’ai trouvé la bonne solution pour modifier le code dans cette réponse .

Il remplace Fragment dans la première page de ViewPager par un autre, et si vous revenez de la deuxième Fragment, le premier Fragment est correctement affiché. Peu importe Fragment affiché dans la première page, si vous glissez d'une page à une autre, cela ne change pas son Fragment.

Voici mon code:

FragmentActivity

public class TabsFacturasActivity extends SherlockFragmentActivity {

    public void onBackPressed() {
        if(mPager.getCurrentItem() == 0) {
            if (mAdapter.getItem(0) instanceof DetallesFacturaFragment) {
                ((DetallesFacturaFragment) mAdapter.getItem(0)).backPressed();
            }
            else if (mAdapter.getItem(0) instanceof FacturasFragment) {
                finish();
            }
        }
    }

    private static class MyAdapter extends FragmentPagerAdapter {

        private final class FirstPageListener implements
        FirstPageFragmentListener {
            public void onSwitchToNextFragment() {
                mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
                .commit();
                if (mFragmentAtPos0 instanceof FacturasFragment){
                    mFragmentAtPos0 = new DetallesFacturaFragment(listener);
                }else{ // Instance of NextFragment
                    mFragmentAtPos0 = new FacturasFragment(listener);
                }
                notifyDataSetChanged();
            }
        }

        private String[] titles = { "VER FACTURAS", "VER CONSUMO", "INTRODUCIR LECTURA" };
        private final FragmentManager mFragmentManager;
        public Fragment mFragmentAtPos0;
        private Context context;
        FirstPageListener listener = new FirstPageListener();

        public MyAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
            mFragmentManager = fragmentManager;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return titles[position];
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0: // Fragment # 0
                if (mFragmentAtPos0 == null)
                {
                    mFragmentAtPos0 = new FacturasFragment(listener);
                }
                return mFragmentAtPos0;

            case 1: // Fragment # 1
                return new ConsumoFragment();
            case 2:// Fragment # 2
                return new LecturaFragment();
            }
            return null;
        }

        @Override
        public int getCount() {
            return titles.length;
        }

        @Override
        public int getItemPosition(Object object)
        {
            if (object instanceof FacturasFragment && 
                    mFragmentAtPos0 instanceof DetallesFacturaFragment) {
                return POSITION_NONE;
            }
            if (object instanceof DetallesFacturaFragment && 
                    mFragmentAtPos0 instanceof FacturasFragment) {
                return POSITION_NONE;
            }
            return POSITION_UNCHANGED;
        }

    }

}

FirstPageFragmentListener

public interface FirstPageFragmentListener {
    void onSwitchToNextFragment();
}

FacturasFragment (PremierFragment)

public class FacturasFragment extends ListFragment implements FirstPageFragmentListener {

    static FirstPageFragmentListener firstPageListener;

    public FacturasFragment() {
    }

    public FacturasFragment(FirstPageFragmentListener listener) {
        firstPageListener = listener;
    }

    public void onListItemClick (ListView l, View v, int position, long id) {
        firstPageListener.onSwitchToNextFragment();
    }
}

DetallesFacturaFragment (SecondFragment)

public class DetallesFacturaFragment extends SherlockFragment {

    static FirstPageFragmentListener firstPageListener;

    public DetallesFacturaFragment() {
    }

    public DetallesFacturaFragment(FirstPageFragmentListener listener) {
        firstPageListener = listener;
    }

    public void backPressed() {
        firstPageListener.onSwitchToNextFragment();
    }
}
48
Lyd

Pour résoudre le problème mineur que Android suggère de ne pas utiliser de constructeur autre que celui par défaut pour fragment. Vous devez modifier le constructeur de la manière suivante:

dans FacturasFragment:

 public static FacturasFragment createInstance(FirstPageFragmentListener listener) {
    FacturasFragment facturasFragment = new FacturasFragment(); 
    facturasFragment.firstPageListener = listener;
    return facturasFragment;
 }

Ensuite, vous pouvez l’appeler à partir de la fonction getItem comme ceci:

 mFragmentAtPos0 = FacturasFragment.createInstance(listener);

dans DetallesFacturaFragment:

 public static DetallesFacturaFragment createInstance(FirstPageFragmentListener listener) {
    DetallesFacturaFragment detallesFacturaFragment= new DetallesFacturaFragment(); 
    detallesFacturaFragment.firstPageListener = listener;
    return detallesFacturaFragment;
 }

Ensuite, vous pouvez l'appeler à partir de FirstPageListener.onSwitchToNextFragment () comme suit:

 mFragmentAtPos0 = DetallesFacturaFragment.createInstance(listener);
0
faruk

J'ai trouvé deux façons de remplacer fragment dans ViewPager.

Méthode 1: en mettant à jour ViewPagerAdapter Méthode 2: en utilisant un fragment racine

Way1: De cette manière, nous devons mettre à jour le fragment à partir de la liste de fragments de l'adaptateur Viewpager et informer le fragment de la modification. Par exemple:

  private void changefragment(int position) {
        Fragment newFragment = CategoryPostFragment.newInstance();
        adapter.fragments.set(position, newFragment);
        adapter.notifyDataSetChanged();
        viewPager.setCurrentItem(position);

    }

Voici le code de l'adaptateur viewpager pour cette

import Java.util.List;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.PagerAdapter;

import com.trickbd.trickbdapp.ui.activity.homeactivity.fragments.FavFragment;
import com.trickbd.trickbdapp.ui.activity.homeactivity.fragments.HomePostFragment;

public class ViewPagerAdapter extends FragmentStatePagerAdapter {
    public List<Fragment> fragments;

    public ViewPagerAdapter(FragmentManager manager, List<Fragment> fragments) {
        super(manager);
        this.fragments = fragments;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        if (fragments.get(position)!=null){
            return fragments.get(position);
        }else {
            if (position==0){
                return new HomePostFragment();
            }else {
                return new FavFragment();
            }
        }

    }

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

    //method to add fragment
    public void addFragment(Fragment fragment) {
        fragments.add(fragment);
    }


    @Override
    public int getItemPosition(@NonNull Object object) {
        if (object instanceof FavFragment) {
            return POSITION_UNCHANGED; // don't force a reload
        } else {
            // POSITION_NONE means something like: this fragment is no longer valid
            // triggering the ViewPager to re-build the instance of this fragment.
            return POSITION_NONE;
        }
    }

}

Je me débrouillais bien avec ce code. Je m'inquiète lorsque la méthode super (manager) de FragmentStatePagerAdapter est obsolète en api 28. Lorsque j'utilise "super (manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);" ainsi que la première façon de remplacer fragmenter le crash de l'application au moment de l'exécution.

De plus, il y a un tirage arrière qui, lorsque nous appelons adapter.notifydatasetchanged () à partir de l'adaptateur viewpager, n'est pas efficace. Il s'agit de recréer l'intégralité du viewpager pendant que nous essayons de ne modifier qu'un fragment spécifique.

Donc, la solution n ° 2 vient avec la solution de tous les problèmes.

Voie 2: Sans ajouter le fragment réel au viewpager, nous devrions ajouter un fragment racine au viewpager. Dans le fragment racine, un fragment réel sera ajouté (nous l'avons précédemment ajouté dans le viewpager. Nous pouvons alors remplacer facilement tout fragment sans l'aide de FragmentStatePagerAdapter. Code du fragment racine:

Rootfragment.Java

import Android.os.Bundle;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentTransaction;

import com.trickbd.trickbdapp.R;

import dagger.Android.support.DaggerFragment;

public class RootFragment extends DaggerFragment {
    public RootFragment() {
    }

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


    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.root_layout, container, false);
        if (getFragmentManager() != null) {
            FragmentTransaction transaction = getFragmentManager()
                    .beginTransaction();
            transaction.replace(R.id.root_frame, new HomePostFragment());

            transaction.commit();
        }

        return view;

    }
}

root_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:id="@+id/root_frame">

</FrameLayout>

Donc, le code de remplacement précédent sera mis à jour comme ci-dessous

private void changefragment(int position) {
        Fragment newFragment = CategoryPostFragment.newInstance();
        replacefragment(newFragment);
        viewPager.setCurrentItem(position);

    }

public void replacefragment(Fragment fragment){
        getSupportFragmentManager();
        FragmentTransaction trans = getSupportFragmentManager()
                .beginTransaction();
        trans.replace(R.id.root_frame, fragment);
        trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        trans.addToBackStack(null);
        trans.commit();
    }

De cette manière, nous pouvons remplacer le fragment plus efficacement.

Pour obtenir le fragment actuellement ajouté dans rootfragment, nous pouvons utiliser ce code.

if (getSupportFragmentManager().findFragmentById(R.id.root_frame) instanceof HomePostFragment){
            HomePostFragment postFragment = (HomePostFragment) getSupportFragmentManager().findFragmentById(R.id.root_frame);

 }

Vous pouvez en apprendre plus à ce sujet ici

0
Afjalur Rahman Rana