RecyclerView onBindViewHolder sólo se llama una vez dentro del diseño de la pestaña

Tengo cuatro pestañas y cuatro fragmentos (cada uno por cada pestaña).

Cada fragmento tiene una vista de reciclador vertical. Dado que todos los fragmentos de vista se ven similares Estoy reutilizando el mismo archivo de diseño, los mismos elementos de vista de reciclador y el mismo adaptador.

El problema es que solo se carga un elemento bajo la primera pestaña y la tercera pestaña y la cuarta, mientras que la segunda pestaña carga con éxito todos los datos.

Espero que la imagen añadida a continuación da una mejor comprensión sobre el tema.

Introduzca aquí la descripción de la imagen

Aquí está mi código del adaptador

public class OthersAdapter extends RecyclerView.Adapter<OthersAdapter.OthersViewHolder> { private final Context context; private final ArrayList<LocalDealsDataFields> othersDataArray; private LayoutInflater layoutInflater; public OthersAdapter(Context context, ArrayList<LocalDealsDataFields> othersDataArray) { this.context = context; this.othersDataArray = othersDataArray; if (this.context != null) { layoutInflater = LayoutInflater.from(this.context); } } class OthersViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { TextView othersSmallTitleTextView; ImageView othersImageView; OthersViewHolder(View itemView) { super(itemView); othersSmallTitleTextView = (TextView) itemView.findViewById(R.id.others_small_title); othersImageView = (ImageView) itemView.findViewById(R.id.others_image); itemView.setOnClickListener(this); } @Override public void onClick(View view) { Intent couponDetailsItem = new Intent(context, LocalDealsActivity.class); Bundle extras = new Bundle(); extras.putString(Constants.SECTION_NAME, context.getString(R.string.local_deals_section_title)); // Add the offer id to the extras. This will be used to retrieve the coupon details // in the next activity extras.putInt(Constants.COUPONS_OFFER_ID, othersDataArray.get( getAdapterPosition()).getLocalDealId()); couponDetailsItem.putExtras(extras); context.startActivity(couponDetailsItem); } } @Override public OthersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = layoutInflater.inflate(R.layout.others_items, parent, false); return new OthersViewHolder(view); } @Override public void onBindViewHolder(OthersViewHolder holder, int position) { String lfImage = othersDataArray.get(position).getLocalDealImage(); String lfCategoryName = othersDataArray.get(position).getLocalDealSecondTitle(); if (lfCategoryName != null) { // Set the second title holder.othersSmallTitleTextView.setText(lfCategoryName); } if (lfImage != null) { if (!lfImage.isEmpty()) { // Get the Uri Uri lfUriImage = Uri.parse(lfImage); // Load the Image Picasso.with(context).load(lfUriImage).into(holder.othersImageView); } } } @Override public int getItemCount() { return othersDataArray.size(); } } 

Me gusta señalar un par de cosas –

  • He comprobado otras respuestas sobre el desbordamiento de pila. Hablan de configurar la vista de reciclaje layout_height para wrap_content . Este no es el problema ya que el layout_height ya es wrap_content y también la segunda pestaña carga todos los datos como se esperaba.

  • Y algunas otras respuestas mencionadas usaron las mismas versiones para todas las bibliotecas de soporte y ya estoy usando la versión 25.1.0 para todas las bibliotecas de soporte.

  • El tamaño de la matriz de datos es 20 y devuelve 20 del método getItemCount() del adaptador.

  • La matriz de datos tiene el número esperado de elementos en ella y no son nulos o vacíos.

  • Clean build, invalidate / caches tampoco funciona.

  • Por último, estoy usando FragmentStatePagerAdapter para cargar los fragmentos cuando las pestañas están en foco.

EDITAR:

Esta es la forma en que estoy analizando los datos JSON recibidos

 private void parseLocalDeals(String stringResponse) throws JSONException { JSONArray localJSONArray = new JSONArray(stringResponse); // If the array length is less than 10 then display to the end of the JSON data or else // display 10 items. int localArrayLength = localJSONArray.length() <= 20 ? localJSONArray.length() : 20; for (int i = 0; i < localArrayLength; i++) { // Initialize Temporary variables int localProductId = 0; String localSecondTitle = null; String localImageUrlString = null; JSONObject localJSONObject = localJSONArray.getJSONObject(i); if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_ID)) { localProductId = localJSONObject.getInt(JSONKeys.KEY_LOCAL_DEAL_ID); } if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_CATEGORY)) { localSecondTitle = localJSONObject.getString(JSONKeys.KEY_LOCAL_DEAL_CATEGORY); } if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_IMAGE)) { localImageUrlString = localJSONObject.getString(JSONKeys.KEY_LOCAL_DEAL_IMAGE); } if (localImageUrlString != null) { if (!localImageUrlString.isEmpty()) { // Remove the dots at the start of the Product Image String while (localImageUrlString.charAt(0) == '.') { localImageUrlString = localImageUrlString.replaceFirst(".", ""); } // Replace the spaces in the url with %20 (useful if there is any) localImageUrlString = localImageUrlString.replaceAll(" ", "%20"); } } LocalDealsDataFields localDealsData = new LocalDealsDataFields(); localDealsData.setLocalDealId(localProductId); localDealsData.setLocalDealSecondTitle(localSecondTitle); localDealsData.setLocalDealImage(localImageUrlString); localDealsDataArray.add(localDealsData); } // Initialize the Local Deals List only once and notify the adapter that data set has changed // from second time. If you initializeRV the localDealsRVAdapter at an early instance and only // use the notifyDataSetChanged method here then the adapter doesn't update the data. This is // because the adapter won't update items if the number of previously populated items is zero. if (localDealsCount == 0) { if (localArrayLength != 0) { // Populate the Local Deals list // Specify an adapter localDealsRVAdapter = new OthersAdapter(context, localDealsDataArray); localDealsRecyclerView.setAdapter(localDealsRVAdapter); } else { // localArrayLength is 0; which means there are no rv elements to show. // So, remove the layout contentMain.setVisibility(View.GONE); // Show no results layout showNoResultsIfNoData(localArrayLength); } } else { // Notify the adapter that data set has changed localDealsRVAdapter.notifyDataSetChanged(); } // Increase the count since parsing the first set of results are returned localDealsCount = localDealsCount + 20; // Remove the progress bar and show the content prcVisibility.success(); } 

parseLocalDeals método parseLocalDeals está dentro de una clase auxiliar y se llama usando initializeHotels.initializeRV();

initializeRV() inicializa la vista Recycler, realiza una llamada de red al servidor y los datos recibidos se pasan al método parseLocalDeals . initializeHotels siendo una variable de instancia de la clase Helper.

EDIT 2:

Para aquellos que quieran explorar el código en detalle, he movido la parte del código a otro proyecto y lo compartimos en Github. Aquí está el enlace https://github.com/gSrikar/TabLayout y para entender la jerarquía echa un vistazo al archivo README.

¿Puede alguien decirme lo que estoy perdiendo?

No mucho de una respuesta, pero demasiado tiempo para un comentario.

He duplicado (casi) su código del adaptador y trabaja totalmente para mí. Creo que he hecho lo mismo que tú. Estoy utilizando el mismo archivo de diseño, el mismo elemento y el mismo adaptador para todas las pestañas. Creo que no hay problemas con el código del adaptador.

Digo "casi" porque tuve que cambiar un par de cosas ya que no tengo acceso a sus datos. Cambié su modelo de LocalDealsDataField para incluir un BitmapDrawable y cambié onBindViewHolder() para manejarlo.

  BitmapDrawable lfImage = othersDataArray.get(position).getLocalDealImage(); holder.othersImageView.setBackground(lfImage); 

Dado que no parece haber ningún problema con su adaptador, me centraría en obtener los datos o configurar el adaptador como su problema. Lo siento, no puedo ser de ayuda más allá de eso.

FYI, he aquí cómo configuro el adaptador en onCreateView()

  rootView = inflater.inflate(R.layout.recycler_view, container, false); mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview); mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); mAdapter = new OthersAdapter(this.getContext(), list); mRecyclerView.setAdapter(mAdapter); 

He mirado su código, el problema es igual que explicado por @ardock

Solución me gustaría proponer,

Tienes que cambiar tu código en 3 lugar ::

  1. Dentro de todo el Fragment que está utilizando en ViewPager No llame a initializeRESPECTIVEView() del método onCreateView .

  2. Dentro de LocalFragment hacer una lista de fragmentos que va a utilizar con ViewPager y pasarlo a BottomSectionsPagerAdapter . Y devuelve Fragment de esa lista desde getItem(int position) de BottomSectionsPagerAdapter .

  3. Agregue el siguiente código a LocalFragment dentro de useSlidingTabViewPager() .

    tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {

      ` @Override public void onTabSelected(TabLayout.Tab tab) { } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { } });` 

    // Call Respective fragment método initializeRESPECTIVEView() de onTabSelected , puede obtener la instancia de fragmento de la lista que pasó a BottomSectionsPagerAdapter

Resumen

Resolvió el problema de diseño en el punto 1 reemplazando un LinearLayout por un RelativeLayout , invirtiendo la lógica de visibilidad para evitar el efecto fantasma y capturando excepciones e impidiéndolas cuando no se encuentra la vista relacionada.

Se añadió el punto 2 para demostrar que el defecto visual sólo está presente en los dispositivos de Marshmallow y Nougat.

Finalmente, FragmentStatePagerAdapter carga las páginas antes de obtener el foco, por lo que se propone una corrección en el punto 3 (cargar todas las páginas y actualizarlas cuando estén seleccionadas).

Más información en los comentarios a continuación y @ d4h respuesta .

La cuarta página no está usando el mismo diseño, sólo el mismo RecyclerView e id , tal vez un trabajo en progreso. El problema de diseño puede resolverse utilizando el mismo diseño que las páginas anteriores, pero considero este cambio fuera del alcance.


1. Parcialmente fijado para dispositivos Marshmallow y Nougat. Trabajo en progreso.

Update2 Cambiar LinearLayout por RelativeLayout e invertir la lógica de visibilidad resuelve el problema de diseño:

Introduzca aquí la descripción de la imagen

Actualización: Comentar initializeTrending en todas las inicializaciones de fragmentos también funciona enApi23 +

Introduzca aquí la descripción de la imagen

Lo comprobaré más adelante, parece que las ofertas se cargan correctamente, pero las tendencias se cargan y las ofertas se pierden. WIP aquí .

Si la matriz de tendencias está vacía y la vista de tendencias ha desaparecido, las ofertas no se muestran , pero se muestran invisibles

Introduzca aquí la descripción de la imagen

Introduzca aquí la descripción de la imagen


2. Estás cargando una página incorrecta en los dispositivos de Marshmallow y Nougat

FragmentStatePagerAdapter primera llamada a getItem () incorrecta en dispositivos Nougat

Esto terminó por no tener nada que ver con el código FragmentStatePagerAdapter. Más bien, en mi fragmento, agarré un objeto almacenado de una matriz usando la cadena ("id") que pasé al fragmento en init. Si agarré ese objeto almacenado pasando en la posición del objeto en la matriz, no hubo ningún problema. Solo ocurre en dispositivos con Android 7.

FragmentStatePagerAdapter – getItem

Un adaptador FragmentStatePager cargará la página actual y una página a cada lado. Es por eso que registra 0 y 1 al mismo tiempo. Cuando cambia a la página 2, cargará la página 3 y mantendrá la página 1 en la memoria. Luego, cuando llegue a la página 4, no cargará nada, ya que 4 se cargó cuando se desplazó a 3 y no hay nada más allá de eso. Así que el int que se está dando en getItem () no es la página que se está viendo, es el que se está cargando en la memoria. Espero que despeje las cosas por ti

Estos comentarios se confirman en esta

Todas las páginas se cargan correctamente en el emulador Lollipop, la última página tiene un problema extra, vea OthersFragment :

Introduzca aquí la descripción de la imagen

Introduzca aquí la descripción de la imagen


3. Inicialice todas las páginas en la creación y actualícelas en la selección.

Aumentar OffScreenPageLimit para que todas las páginas se inicialicen

Añadir en la página seleccionada / no seleccionada / reselected escucha

Estos cambios solucionan el problema comentado a continuación:

 /** * Implement the tab layout and view pager */ private void useSlidingTabViewPager() { // Create the adapter that will return a fragment for each of the three // primary sections of the activity. BottomSectionsPagerAdapter mBottomSectionsPagerAdapter = new BottomSectionsPagerAdapter(getChildFragmentManager()); // Set up the ViewPager with the sections adapter. ViewPager mBottomViewPager = (ViewPager) rootView.findViewById(R.id.local_bottom_pager); mBottomViewPager.setOffscreenPageLimit(mBottomSectionsPagerAdapter.getCount()); mBottomViewPager.setAdapter(mBottomSectionsPagerAdapter); TabLayout tabLayout = (TabLayout) rootView.findViewById(R.id.tab_layout); tabLayout.setupWithViewPager(mBottomViewPager); tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { /** * Called when a tab enters the selected state. * * @param tab The tab that was selected */ @Override public void onTabSelected(TabLayout.Tab tab) { // TODO: update the selected page here Log.i(LOG_TAG, "page " + tab.getPosition() + " selected."); } /** * Called when a tab exits the selected state. * * @param tab The tab that was unselected */ @Override public void onTabUnselected(TabLayout.Tab tab) { // Do nothing Log.i(LOG_TAG, "Page " + tab.getPosition() + " unselected and "); } /** * Called when a tab that is already selected is chosen again by the user. Some applications * may use this action to return to the top level of a category. * * @param tab The tab that was reselected. */ @Override public void onTabReselected(TabLayout.Tab tab) { // Do nothing Log.i(LOG_TAG, "Page " + tab.getPosition() + " reselected."); } }); } 

Comentarios anteriores:

Compruebe su método LocalFragmento getItem () utilizando puntos de interrupción.

Si selecciona una página, la página siguiente también se inicializa y está compartiendo el recicladorVer, etc.

Me gustaría mover la inicialización fuera de getItem () como se sugiere aquí :

ViewPager es la opción por defecto para cargar la siguiente página (Fragmento) que no puede cambiar por setOffscreenPageLimit (0). Pero puedes hacer algo para hackear. Puede implementar la función onPageSelected en Activity que contiene el ViewPager. En el fragmento siguiente (que no quieres cargar), escribes una función, digamos showViewContent (), donde se pone todo el código init que consuma recursos y no hace nada antes del método onResume (). A continuación, llame a la función showViewContent () dentro de onPageSelected. Espero que esto ayude

Lea estas preguntas relacionadas (la primera tiene soluciones posibles para cortar el límite a cero):

ViewPager.setOffscreenPageLimit (0) no funciona como se esperaba

¿ViewPager requiere un mínimo de 1 páginas sin pantalla?

Sí. Si estoy leyendo el código fuente correctamente, debería recibir una advertencia sobre esto en LogCat, algo así como:

Límite de página fuera de pantalla requerido 0 demasiado pequeño; Predeterminado a 1

viewPager.setOffscreenPageLimit(couponsPagerAdapter.getCount());

 public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; } if (limit != mOffscreenPageLimit) { mOffscreenPageLimit = limit; populate(); } } 
  • ActionBarSherlock y FragmentTabsPager
  • ¿Cómo utilizar el flash / led de la cámara como antorcha en una lengüeta de Samsung Galaxy?
  • Barra de Acción Sherlock con botones
  • Separa la pila trasera de cada pestaña en Android mediante Fragmentos
  • Android | Detener TabLayout recargar / actualizar mis fragmentos
  • Las pestañas TabHost de Android roban el enfoque al utilizar el teclado de hardware
  • Actionbarsherlock + tabs + multi fragmentos?
  • Crash en MediaController.show ()
  • Cómo implementar Android 4.0 como deslizar para descartar funcionalidad en ListView?
  • Android: ¿Por qué se llama onTabSelected cuando se crean pestañas?
  • Mostrar opciones de menú en la barra de herramientas de Actividad contiene pestañas como fragmento
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.