android - studio - ¿Cómo mostrar el fragmento actual en una pestaña específica de un viewpager?
viewpager example in android (4)
Quiero obtener el último fragmento en el backstack, o el actual mostrado es el mismo para mí, en la tab b_1
. Como puede ver en la siguiente imagen, tengo un ViewPager y otra tab b
interna tab b
. Por lo tanto, se muestran cuatro fragmentos actuales.
Pregunta : ¿Cómo puedo obtener la instancia del Fragment 2
?
He visto otras soluciones, pero ninguna funciona para este escenario.
Anotación: El fragmento a devolver no es necesario que esté alojado en el ViewPager. Puedo haber abierto dos fragmentos más en una pestaña.
Con este método obtengo todos los fragmentos visibles actuales, pero no el específico que quiero.
public ArrayList<Fragment> getVisibleFragment() {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
ArrayList<Fragment> visibleFragments = new ArrayList<>();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment != null && fragment.isVisible())
visibleFragments.add(fragment);
}
}
return visibleFragments;
}
Algun codigo interesante
activity_main.xml
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static ViewPagerAdapter adapter;
private static ViewPager viewPager;
private TabLayout tabLayout;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager();
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
setupTabIcons();
}
private void setupViewPager() {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
// Wrap with HostFragment to get separate tabbed nagivation.
adapter.addFrag(HostFragment.newInstance(new Fragment1()), null);
adapter.addFrag(HostFragment.newInstance(new RootFragment2()), null);
adapter.addFrag(HostFragment.newInstance(new Fragment4()), null);
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(2);
}
public void openNewFragment(Fragment fragment) {
HostFragment hostFragment = (HostFragment) adapter.getItem(viewPager.getCurrentItem());
hostFragment.replaceFragment(fragment, true);
}
}
fragmento_host.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/hosted_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
HostFragment.java
/**
* This class implements separate navigation for a tabbed viewpager.
*
* Based on https://medium.com/@nilan/separate-back-navigation-for-
* a-tabbed-view-pager-in-android-459859f607e4#.u96of4m4x
*/
public class HostFragment extends BackStackFragment {
private Fragment fragment;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_host, container, false);
if (fragment != null) {
replaceFragment(fragment, false);
}
return view;
}
public void replaceFragment(Fragment fragment, boolean addToBackstack) {
if (addToBackstack) {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
} else {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
}
}
public static HostFragment newInstance(Fragment fragment) {
HostFragment hostFragment = new HostFragment();
hostFragment.fragment = fragment;
return hostFragment;
}
public Fragment getFragment() {
return fragment;
}
}
fragmento2_root.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/fragment2_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tab2_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabGravity="fill"/>
<android.support.v4.view.ViewPager
android:id="@+id/tab2_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</LinearLayout>
RootFragment2.java
public class RootFragment2 extends Fragment {
private ViewPagerAdapter adapter;
private ViewPager viewPager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inflate the layout for this fragment.
View root = inflater.inflate(R.layout.fragment2_root, container, false);
viewPager = (ViewPager) root.findViewById(R.id.tab2_viewpager);
setupViewPager(viewPager);
TabLayout tabLayout = (TabLayout) root.findViewById(R.id.tab2_tabs);
tabLayout.setupWithViewPager(viewPager);
return root;
}
private void setupViewPager(ViewPager viewPager) {
adapter = new ViewPagerAdapter(getActivity().getSupportFragmentManager());
// Wrap with HostFragment to get separate tabbed nagivation.
adapter.addFrag(HostFragment.newInstance(new Fragment2()), null);
adapter.addFrag(HostFragment.newInstance(new Fragment3()), null);
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);
}
public ViewPagerAdapter getAdapter() {
return adapter;
}
public ViewPager getViewPager() {
return viewPager;
}
}
Después de algunos comentarios con DEADMC, decidí implementarlo con memoria adicional, guardando en una lista los fragmentos abiertos.
Te explico mi solución. Tengo dos tipos de fragmentos especiales, host y raíz . Fragmentos de host son aquellos que son el inicio de una pestaña. Los fragmentos de raíz son aquellos que contienen un ViewPager. En este escenario, tab_b
tiene un fragmento de raíz con un ViewPager interno.
Para cada pestaña, esto es, para cada HostFragment , he agregado una lista de fragments
para guardar los fragmentos abiertos en esta pestaña. También un método getCurrentFragment
para obtener el último mostrado en esta pestaña.
public class HostFragment extends BackStackFragment {
private ArrayList<Fragment> fragments = new ArrayList<>();
public Fragment getCurrentFragment() {
return fragments.get(fragments.size() - 1);
}
También se necesita un método para eliminar el último fragmento mostrado en esta pestaña, para mantener un estado verdadero. El botón Atrás debe ser redirigido a este método.
public void removeCurrentFragment() {
getChildFragmentManager().popBackStack();
fragments.remove(fragments.size() - 1);
}
También es necesario cambiar los métodos anteriores para insertar los nuevos fragmentos abiertos en la lista.
public void replaceFragment(Fragment fragment, boolean addToBackstack) {
// NEW: Add new fragment to the list.
fragments.add(fragment);
if (addToBackstack) {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
} else {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
}
}
public static HostFragment newInstance(Fragment fragment) {
HostFragment hostFragment = new HostFragment();
// NEW: Add first fragment to the list.
hostFragment.fragments.add(fragment);
return hostFragment;
}
} // HostFragment.
RootFragment es una interfaz implementada por esos fragmentos que contiene un ViewPager.
public interface RootFragment {
/**
* Opens a new Fragment in the current page of the ViewPager held by this Fragment.
*
* @param fragment - new Fragment to be opened.
*/
void openNewFragment(Fragment fragment);
/**
* Returns the fragment displayed in the current tab of the ViewPager held by this Fragment.
*/
Fragment getCurrentFragment();
}
Y luego, la implementación sería:
MainActivity no es un fragmento, pero implemento el significado de la interfaz RootFragment .
public class MainActivity extends AppCompatActivity implements RootFragment {
private void setupViewPager() {
adapter = new ViewPagerAdapter(getSupportFragmentManager());
// Wrap with HostFragment to get separate tabbed nagivation.
adapter.addFrag(HostFragment.newInstance(new Fragment1()), null);
adapter.addFrag(new RootFragment2(), null); // This is a Root, not a Host.
adapter.addFrag(HostFragment.newInstance(new Fragment4()), null);
viewPager.setAdapter(adapter);
// 2 because TabLayout has 3 tabs.
viewPager.setOffscreenPageLimit(2);
}
@Override
public void openNewFragment(Fragment fragment) {
Fragment hosted = adapter.getItem(viewPager.getCurrentItem());
// Replace the fragment of current tab to [fragment].
if (hosted instanceof HostFragment) {
((HostFragment) hosted).replaceFragment(fragment, true);
// Spread action to next ViewPager.
} else {
((RootFragment) hosted).openNewFragment(fragment);
}
}
@Override
public Fragment getCurrentFragment() {
Fragment hosted = adapter.getItem(viewPager.getCurrentItem());
// Return current tab''s fragment.
if (hosted instanceof HostFragment) {
return ((HostFragment) hosted).getCurrentFragment();
// Spread action to next ViewPager.
} else {
return ((RootFragment) hosted).getCurrentFragment();
}
}
}
y RootFragment2
public class RootFragment2 extends Fragment implements RootFragment {
private void setupViewPager(ViewPager viewPager) {
adapter = new ViewPagerAdapter(getActivity().getSupportFragmentManager());
// Wrap with HostFragment to get separate tabbed nagivation.
adapter.addFrag(HostFragment.newInstance(new Fragment2()), null);
adapter.addFrag(HostFragment.newInstance(new Fragment3()), null);
viewPager.setAdapter(adapter);
// 1 because TabLayout has 2 tabs.
viewPager.setOffscreenPageLimit(1);
}
@Override
public void openNewFragment(Fragment fragment) {
((HostFragment) adapter.getItem(viewPager.getCurrentItem())).replaceFragment(fragment, true);
}
@Override
public Fragment getCurrentFragment() {
return ((HostFragment) adapter.getItem(viewPager.getCurrentItem())).getCurrentFragment();
}
}
Y luego, solo necesito llamar:
((MainActivity) getActivity ()). GetCurrentFragment ();
Primero defina un SparseArray
en los adaptadores de sus ViewPagers
como a continuación. En esta matriz tendremos la instancia de fragmentos.
SparseArray<Fragment> registeredFragments = new SparseArray<>();
Y anule el método instantiateItem
sus adaptadores.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
También anule el método destroyItem
de sus ViewPagers
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
Y define un nuevo método para obtener tu instancia de ViewPager
Fragments
.
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
Y finalmente establezca agregar un PageChangeListener
a sus ViewPagers
:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// Here''s your instance
YourFragment fragment =(YourFragment)yourPagerAdapter.getRegisteredFragment(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
Espero que esto te ayude. Buena suerte.
Edición: lo siento, no puedo entender exactamente lo que planea hacer, pero si necesita mantener la instancia del sub fragmento (b_1, b_2), puede definir un método para su actividad, como
public void setCurrentFragment(Fragment fragment){
this.currentFragment = fragment;
}
y en el adaptador de localizador de su subvista puede llamar a este método como se muestra a continuación:
subViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// Here''s your instance
YourFragment fragment =(YourFragment)yourSubPagerAdapter.getRegisteredFragment(position);
((MyActivity)getActivity).setCurrentFragment(fragment);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
De esta manera puedes mantener una instancia y tu fragmento superior.
Puede crear algo como la clase CustomFragment
que contiene el método getClassName
y se extiende desde la clase Fragment
. Luego extienda todos sus fragmentos, como RootFragment2
de la clase CustomFragment
lugar de solo Fragment
.
Para que puedas obtener un fragmento como este:
public CustomFragment getNeededFragment(String fragmentName) {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
CustomFragment result = null;
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment != null && fragment.isVisible()) {
try {
CustomFragment customFragment = (CustomFragment) customFragment;
if (customFragment.getClassName().equals(fragmentName)))
result = customFragment;
} catch (ClassCastException e) {
}
}
}
}
return result ;
}
Yo haría algo así:
1 . Crea una interfaz como esta.
public interface ReturnMyself {
Fragment returnMyself();
}
2 . Todos los fragmentos dentro de ViewPagers deben implementar este.
3 . Agrega OnPageChangeListener
a tu VP principal. Así que siempre sabrás la posición actual.
4 . Agregue OnPageChangeListener
su OnPageChangeListener
interno para que sepa cuál está allí en la pantalla.
5 . Agregue un método a su adaptador (principal e interno) que devuelve su fragmento de la lista que se le pasó.
public ReturnMyself getReturnMyselfAtPosition()
6 . Todos los Fragmentos deberían devolver esto a returnMyself()
7 . El fragmento que tiene fragmentos internos debe devolver, a cambio, a Mí mismo algo parecido.
Fragment returnMyself() {
return this.myInnerFragmentAdapter.getReturnMyselfAtPosition().returnMyself();
}
8 . Del fragmento principal / actividad que acaba de llamar.
this.adapter.getReturnMyselfAtPosition().returnMyself();
Y tienes tu fragmento actual.