Android ViewPager setCurrentItem no funciona después de onResume
methods android-viewpager (13)
Tengo este extraño problema, el SetCurrentItem de ViewPager (position, false) funciona perfectamente bien, luego estoy cambiando a otra actividad, y luego de volver a la primera actividad, el ViewPager siempre termina en el primer elemento. Aunque he agregado setCurrentItem al método onResume, todavía lo ignora. No está lanzando ninguna excepción cuando intento establecer el ítem fuera del índice de límites. Aunque más adelante cuando llamo a este método, cuando se hace clic en el botón "siguiente", funciona como se esperaba. Comprobé mi código 10 veces para cualquier posible llamada a setCurrentItem (0) o smth, pero simplemente no está allí en absoluto.
Debe llamar a pager.setCurrentItem (activePage) justo después de pager.setAdapter (buildAdapter ())
@Override
public void onResume() {
if (pager.getAdapter() != null) {
activePage=pager.getCurrentItem();
Log.w(getClass().getSimpleName(), "pager.getAdapter()!=null");
pager.setAdapter(null);
}
pager.setAdapter(buildAdapter());
pager.setCurrentItem(activePage);
}
En el momento en que llamo a setCurrentItem()
la vista está a punto de ser recreada. De hecho, invoco setCurrentItem()
para el viewpager y luego el sistema llama onCreateView()
y crea un nuevo viewpager.
Esta es la razón por la que no veo ningún cambio. Y esta es la razón por la que un postDelayed()
puede ayudar.
Solución teórica: setCurrentItem()
invocación setCurrentItem()
hasta que la vista haya sido recreada.
Solución práctica: no tengo idea de una solución estable y simple. Deberíamos poder verificar si la clase está a punto de recrear su vista y si ese es el caso posponer la invocación de setCurrentItem()
al final de onCreateView()
Encontré una solución muy simple para esto:
if (mViewPager.getAdapter() != null)
mViewPager.setAdapter(null);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setCurrentItem(desiredPos);
Y, si eso no funciona, puede ponerlo en un controlador, pero no hay necesidad de un retraso programado:
new Handler().post(new Runnable() {
@Override
public void run() {
mViewPager.setCurrentItem(desiredPos);
}
});
Este es un problema de ciclo de vida, como lo señalan varios carteles aquí. Sin embargo, creo que las soluciones con la publicación de Runnable
son impredecibles y probablemente propensas a errores. Parece una forma de ignorar el problema al publicarlo en el futuro.
No estoy diciendo que esta sea la mejor solución, pero definitivamente funciona sin usar Runnable
. ViewPager
un entero separado dentro del Fragment
que tiene el ViewPager
. Este número entero contendrá la página que queremos establecer como la página actual cuando onResume
se llame a continuación. El valor del entero se puede establecer en cualquier punto y, por lo tanto, se puede establecer antes de una FragmentTransaction
o al reanudar una actividad. También tenga en cuenta que todos los miembros están configurados en onResume()
, no en onCreateView()
.
public class MyFragment extends Fragment
{
private ViewPager mViewPager;
private MyPagerAdapter mAdapter;
private TabLayout mTabLayout;
private int mCurrentItem = 0; // Used to keep the page we want to set in onResume().
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.my_layout, container, false);
mViewPager = (ViewPager) view.findViewById(R.id.my_viewpager);
mTabLayout = (TabLayout) view.findViewById(R.id.my_tablayout);
return view;
}
@Override
public void onResume()
{
super.onResume();
MyActivity myActivity = (MyActivity) getActivity();
myActivity.getSupportActionBar().setTitle(getString(R.string.my_title));
mAdapter = new MyPagerAdapter(getChildFragmentManager(), myActivity);
mViewPager.setAdapter(mAdapter);
mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT);
mViewPager.setCurrentItem(mCurrentItem); // <-- Note the use of mCurrentItem here!
mTabLayout.setupWithViewPager(mViewPager);
}
/**
* Call this at any point before needed, for example before performing a FragmentTransaction.
*/
public void setCurrentItem(int currentItem)
{
mCurrentItem = currentItem;
// This should be called in cases where onResume() is not called later,
// for example if you only want to change the page in the ViewPager
// when clicking a Button or whatever. Just omit if not needed.
mViewPager.setCurrentItem(mCurrentItem);
}
}
He usado el método post () descrito aquí y estoy seguro de que funcionaba bien en algunos escenarios, pero como mis datos provienen del servidor, no era el santo grial.
Mi problema es que quiero tener
notifyDataSetChanged
llamado en un momento arbitrario y luego cambiar las pestañas en mi viewPager. Así que justo después de la llamada de notificación tengo esto
ViewUtilities.waitForLayout(myViewPager, new Runnable() {
@Override
public void run() {
myViewPager.setCurrentItem(tabIndex , false);
}
});
y
public final class ViewUtilities {
public static void waitForLayout(final View view, final Runnable runnable) {
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//noinspection deprecation
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
runnable.run();
}
});
}
}
Dato curioso : // la desactivación de noinspection al final se debe a que hay un error de ortografía en la API que se corrigió después de API 16, por lo que debería leer remove On GlobalLayoutListener en lugar de removeGlobal en LayoutListener
Esto parece estar cubriendo todos los casos para mí.
Lo hice de esta manera para restaurar el elemento actual:
@Override
protected void onSaveInstanceState(Bundle outState) {
if (mViewPager != null) {
outState.putInt(STATE_PAGE_NO, mViewPager.getCurrentItem());
}
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mCurrentPage = savedInstanceState.getInt(STATE_PAGE_NO, 0);
}
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onRestart() {
mViewPager.setCurrentItem(mCurrentPage);
super.onRestart();
}
Para mí, esto funcionó estableciendo el elemento actual después de configurar el adaptador
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));
viewPager.setCurrentItem(idx);
pagerSlidingTabStrip.setViewPager(viewPager);// assign viewpager to tabs
Tengo el mismo problema y edito
@Override
public int getCount() { return NUM_PAGES; }
Establecí NUM_PAGES
un error de solo 1.
Tuve un problema similar en OnCreate of my Activity. El adaptador se configuró con el recuento correcto y apliqué setCurrentItem después de configurar el adaptador en ViewPager; sin embargo, devolvería el índice fuera de límites. Creo que ViewPager no había cargado todos mis Fragmentos en el momento en que establecí el elemento actual. Al publicar un ejecutable en ViewPager, pude solucionar esto. Aquí hay un ejemplo con un poco de contexto.
// Locate the viewpager in activity_main.xml
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
// Set the ViewPagerAdapter into ViewPager
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
viewPager.setOffscreenPageLimit(2);
viewPager.post(new Runnable() {
@Override
public void run() {
viewPager.setCurrentItem(ViewPagerAdapter.CENTER_PAGE);
}
});
Tuve una falla similar en el código, el problema fue que estaba configurando la posición antes de cambiar los datos.
La solución fue simplemente establecer el puesto luego y notificar que los datos habían cambiado
notifyDataSetChanged()
setCurrentItem()
un tipo escribió en foros aquí. https://code.i-harness.com/en/q/126bff9 funcionó para mí
if (mViewPager.getAdapter() != null)
mViewPager.setAdapter(null);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setCurrentItem(desiredPos);
LIMPIO Y SIMPLE No es necesario agregar el método de publicación simplemente setCurrentItem después de llamar notifyDataSetChanged ().
realmente no puedo responder POR QUÉ sucede exactamente esto, pero si retrasa la llamada setCurrentItem durante unos milisegundos debería funcionar. Supongo que porque durante onResume
todavía no ha habido un pase de representación, y ViewPager necesita uno o algo así.
private ViewPager viewPager;
@Override
public void onResume() {
final int pos = 3;
viewPager.postDelayed(new Runnable() {
@Override
public void run() {
viewPager.setCurrentItem(pos);
}
}, 100);
}
ACTUALIZACIÓN: tiempo de la historia
así que hoy tuve el problema de que el viewpager ignoró mi acción setCurrentItem, y busqué una solución en . encontré a alguien con el mismo problema y una solución; Implementé la solución y no funcionó. ¡Whoa! de vuelta a para rechazar ese faux-fix-provider, y ...
fui yo. Implementé mi propia falla no corregida, que se me ocurrió la primera vez que tropecé con el problema (y que luego fue olvidado). ahora tendré que rechazar mi voto por proporcionar información incorrecta.
la razón por la que mi "corrección" inicial no funcionó debido a un "pase de representación"; el problema era que el contenido del buscapersonas estaba controlado por un girador. tanto los spinners como el estado de los buscapersonas se restauraron en Reesume, y debido a esto, se llamó al oyente spinners onItemSelected durante el siguiente ciclo de propagación del evento, que repobló al viewpager, esta vez usando un valor predeterminado diferente.
eliminar y restablecer el oyente durante la restauración de estado inicial solucionó el problema.
el arreglo anterior funcionaba la primera vez, porque configuraba la posición actual de los buscapersonas después de disparar el evento onItemSelected. más tarde, dejó de funcionar por alguna razón (probablemente la aplicación se volvió demasiado lenta; en mi implementación no usé 100 ms, sino 10 ms). Luego eliminé el mensaje Detenido en un ciclo de limpieza, porque no cambió el comportamiento ya defectuoso.
actualización 2: no puedo rechazar mi propia publicación. supongo, honorable seppuku es la única opción que queda.