android - recyclerview - Instantánea congelada de la lista procesada con SwipeRefreshLayout en ViewPager
refresh layout android studio (2)
Considere una actividad de Android que es la subclase de android.support.v4.app.FragmentActivity
. Contiene varias aplicaciones administradas por FragmentStatePagerAdapter
. Cada página contiene un ListView
envuelto por android.support.v4.widget.SwipeRefreshLayout
. La siguiente captura de pantalla muestra el estado inicial de esta actividad.
Ahora, deslizo hacia abajo para actualizar la lista en la página 0. Aparece la animación de actualización. Ahora, deslicemos hacia la derecha en la página 2 y luego regresemos a la página 0.
La animación de actualización aún está visible. Esto me parece extraño ya que la página 0 fue destruida y creada nuevamente. Además, si deslizo hacia arriba (intentando desplazarme hasta el final de la lista en la página 0), se representa una capa extraña sobre la página que parece contener el estado anterior de la página desde el momento en que se destruyó.
No tengo idea de por qué sucede esto y cómo solucionarlo. Gracias por cualquier ayuda.
Aquí está el código fuente de una aplicación mínima que consta de 3 archivos de diseño, una clase de actividad, una clase de fragmento, un archivo de manifiesto y un script de compilación.
swipeandroid / src / main / res / layout / service_schedule_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/servicePager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.PagerTabStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"/>
</android.support.v4.view.ViewPager>
swipeandroid / src / main / res / layout / service_schedule_tab_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/serviceRefreshLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/timeSlots"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
swipeandroid / src / main / res / layout / service_schedule_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"/>
</RelativeLayout>
swipeandroid / src / main / java / com / swipetest / ServiceScheduleActivity.java
package com.swipetest;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
public class ServiceScheduleActivity extends FragmentActivity {
@Override
protected void onCreate(final Bundle inState) {
// call super class
super.onCreate(inState);
// set content
setContentView(R.layout.service_schedule_activity);
// set up service pager
((ViewPager) findViewById(R.id.servicePager)).setAdapter(new ServiceFragmentPagerAdapter());
}
private class ServiceFragmentPagerAdapter extends FragmentStatePagerAdapter {
public ServiceFragmentPagerAdapter() {
super(getSupportFragmentManager());
}
@Override
public int getCount() {
return 5;
}
@Override
public Fragment getItem(final int position) {
return ServiceScheduleTabFragment.newInstance(position);
}
@Override
public CharSequence getPageTitle(final int position) {
return String.format("page %d", position);
}
}
}
swipeandroid / src / main / java / com / swipetest / ServiceScheduleTabFragment.java
package com.swipetest;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class ServiceScheduleTabFragment extends Fragment {
private static final String TAB_POSITION_ARGUMENT = "tabPosition";
public static ServiceScheduleTabFragment newInstance(final int tabPosition) {
// prepare arguments
final Bundle arguments = new Bundle();
arguments.putInt(TAB_POSITION_ARGUMENT, tabPosition);
// create fragment
final ServiceScheduleTabFragment fragment = new ServiceScheduleTabFragment();
fragment.setArguments(arguments);
return fragment;
}
@Override
public View onCreateView(final LayoutInflater layoutInflater, final ViewGroup parent, final Bundle inState) {
// create fragment root view
final View rootView = layoutInflater.inflate(R.layout.service_schedule_tab_fragment, parent, false);
// initialize time slot list view
final ListView timeSlotListView = (ListView) rootView.findViewById(R.id.timeSlots);
timeSlotListView.setAdapter(new TimeSlotListAdapter());
// return fragment root view
return rootView;
}
private int getTabPosition() {
return getArguments().getInt(TAB_POSITION_ARGUMENT);
}
private static class TimeSlotRowViewHolder {
private final TextView timeView;
public TimeSlotRowViewHolder(final View rowView) {
timeView = (TextView) rowView.findViewById(R.id.time);
}
public TextView getTimeView() {
return timeView;
}
}
private class TimeSlotListAdapter extends BaseAdapter {
@Override
public int getCount() {
return 80;
}
@Override
public Object getItem(final int position) {
return position;
}
@Override
public long getItemId(final int position) {
return position;
}
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
// reuse or create row view
final View rowView;
if (convertView != null) {
rowView = convertView;
} else {
rowView = LayoutInflater.from(getContext()).inflate(R.layout.service_schedule_row, parent, false);
rowView.setTag(new TimeSlotRowViewHolder(rowView));
}
// fill data
final TimeSlotRowViewHolder rowViewHolder = (TimeSlotRowViewHolder) rowView.getTag();
rowViewHolder.getTimeView().setText(String.format("tab %d, row %d", getTabPosition(), position));
return rowView;
}
}
}
swipeandroid / src / main / AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.swipetest"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23"/>
<application
android:name="android.app.Application"
android:label="Swipe Test"
android:allowBackup="false">
<activity
android:name="com.swipetest.ServiceScheduleActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
swipeandroid / build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath ''com.android.tools.build:gradle:1.5.0''
}
}
apply plugin: ''com.android.application''
repositories {
mavenCentral()
}
dependencies {
compile "com.android.support:support-v4:23.1.1"
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
buildTypes {
debug {
debuggable true
minifyEnabled false
}
}
}
Intente borrar la lista y luego cargue los datos nuevos en la actualización de barrido.
Como se menciona en el comentario.
Finalmente, descubrí una solución que es muy simple de implementar y soluciona el problema.
Simplemente SwipeRefreshLayout
FrameLayout
por ejemplo, mediante FrameLayout
. Parece que a ViewPager
no le gusta que SwipeRefreshLayout
sea la raíz de la jerarquía de vistas de un fragmento de página.
De todos modos, este problema parece un error en la Biblioteca de soporte de Android. Ver https://code.google.com/p/android/issues/detail?id=194957
También probé otras soluciones alternativas que no funcionan:
- Reemplazar
FragmentStatePagerAdapter
por `FragmentPagerAdapter no ayudará. - Llamar a
mySwipeRefreshLayout.setRefreshing(false)
enonPause
no ayudará. - Modificar el
FragmentStatePagerAdapter
para que no recuerde los estados de las páginas destruidas no ayudará.
Las siguientes soluciones alternativas pueden ayudar de alguna manera, pero prefiero el SwipeRefreshLayout
sugerido de SwipeRefreshLayout
todos modos:
- Borrar los datos (para que
ListView
no contenga filas) enSwipeRefreshLayout.OnRefreshListener#onRefresh()
solo ayuda siListView
tiene fondo transparente. La instantánea no deseada probablemente aún se muestra en la primera página, pero es completamente transparente. - No puede permitir la paginación para el momento de la animación de actualización. Sin embargo, esto requiere un poco de esfuerzo ya que debe subclasificar tanto
ViewPager
comoPagerTabStrip
y evitar eventos de IU apropiados en sus subclases. - Por supuesto, puede evitar el uso de
SwipeRefreshLayout
enViewPager
completo, pero esto no parece ser necesario.