La vista Recyclerview no funciona con setNestedScrollingEnabled y nunca recicla ninguna vista

Estoy enfrentando dos problemas:

  1. El oyente de desplazamiento no funcionará
  2. El RecyclerView nunca recicla ninguna vista cuando está conectado a un NestedScrollView . Actúa como un diseño lineal dentro del ScrollView. Utiliza mucha memoria y crea rezagos.

Estoy adjuntando un fragmento de reproductor de youtube en la parte superior de la vista de reciclador ya que no puedo poner un fragmento dentro de la vista de reciclador. En mi código se puede ver que hay un diseño de marco.

Mi diseño se ve así:

  <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/nestedScroll" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="240dp" android:layout_alignParentTop="true"/> </LinearLayout> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/youtube_layout" android:visibility="visible"/> </LinearLayout> </android.support.v4.widget.NestedScrollView> 

Quiero usar el detector de desplazamiento para cargar más elementos, así que he probado lo siguiente:

 @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { Log.i(TAG, "onScrolled: "); // bail out if scrolling upward or already loading data if (dy < 0 || dataLoading.isDataLoading()) return; final int visibleItemCount = recyclerView.getChildCount(); final int totalItemCount = layoutManager.getItemCount(); final int firstVisibleItem = layoutManager.findFirstVisibleItemPosition(); if ((totalItemCount - visibleItemCount) <= (firstVisibleItem )) { onLoadMore(); } } 

Pero layoutManager.findFirstVisibleItemPosition()==0 por lo que no funciona, y más sobre onScrolled never called twice desde que establecí recyclerview.setNestedScrollingEnabled (false)

Así que he intentado en onBindView como este

  public void onBindViewHolder(RecyclerView.ViewHolder customViewHolder, int i) { Log.w("d","inside bind view"); if(i>=getItemCount()-1 && !datamanager.isDataLoading()){ datamanager.loadmoreData(); } 

Pero el recylerview vincula la vista a la vez una vez antes de que incluso empiece a desplazarse, por lo que este método también no funciona.

La vista del reciclador nunca recicla ninguna vista cuando está asociada a una vista de desplazamiento anidada, sino que actúa como un diseño lineal dentro de la vista de desplazamiento. Esto crea una memoria enorme y se retrasa en la pantalla.

Exactamente. No se puede colocar un RecyclerView dentro de otra vista de desplazamiento.

No funcionará porque la vista de envolvente necesita conocer la altura total del RecyclerView y el RecyclerView sólo puede conocer su altura total midiendo y distribuyendo todos sus elementos.

¿Cómo arreglar esto?

No coloque un RecyclerView dentro de otra vista de desplazamiento.

Si necesita un encabezado o un pie de página, deberá agregarlo al RecyclerView y dejar que el RecyclerView se encargue de mostrarlo. Deshágase de su ScrollView y mueva su cabecera dentro de RecyclerView.

Técnicamente también es posible cargar fragmentos dentro de un RecyclerView, pero tengo que admitir que esto funcione correctamente es un poco complicado.

También hay un montón de bibliotecas que facilitan el proceso, mi favorito personal es Epoxy hecho por AirBnb, pero también hay Groupie , y muchos otros.

He manejado este tipo de situación. Hay una vista de reciclaje. Recycler view tiene viewpager. El viewpager tiene fragmentos y entonces hay un recyclerview dentro del fragmento.

Necesitas cortar los toques para hacer que se desplace perfectamente.

 private boolean interceptTouch = true; private static final int MIN_DISTANCE = 200; private float downX = 0, downY = 0, moveX = 0, moveY = 0, upX = 0, upY; public TouchInterceptRecyclerView(Context context) { super(context); } public TouchInterceptRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (getRvTop() == 0) { onTouchEvent(ev); } return interceptTouch; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); break; case MotionEvent.ACTION_MOVE: moveX = event.getX(); moveY = event.getY(); break; case MotionEvent.ACTION_UP: upX = event.getX(); upY = event.getY(); break; } float deltaX = upX - downX; if (getViewTop() == 0 && moveY > downY && Math.abs(moveY - downY) > MIN_DISTANCE) { // moving down interceptTouch = true; }else if (Math.abs(deltaX) > MIN_DISTANCE) { if (upX > downX) { // Left to Right swipe action interceptTouch = false; }else { // Right to left swipe action interceptTouch = false; } } else { // screen tap interceptTouch = false; } return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); } @Override public void onScrolled(int dx, int dy) { if (getViewTop() <= 0) { interceptTouch = false; // dispatch the touch to child when reached top } else { interceptTouch = true; // do not dispatch the touch event } super.onScrolled(dx, dy); } public int getViewTop() { int top = 0; View v = this.getChildAt(1); if (v == null) { v = this.getChildAt(0); } if (v != null && v instanceof LinearLayout) { top = v.getTop(); } return top; } public int getRvTop() { int top = -1; View v = this.getChildAt(1); if (v == null) { v = this.getChildAt(0); } if (v != null && v instanceof LinearLayout) { ViewPager vp = (ViewPager) v.findViewById(R.id.single_tribe_pager); if (vp != null) { int currentPos = vp.getCurrentItem(); RecyclerView rv = (RecyclerView) vp.findViewWithTag("recyclerview" + currentPos); if (rv != null) { View v1 = rv.getChildAt(0); if (v1 != null && v1 instanceof CardView) { top = v1.getTop() - ResourceUtils.getDimensionPixelOffset(R.dimen.padding_20); // deducting 20 as we have give top margin as 20 to the top layout } } } } return top; } 

Aquí es cómo lo logré, he intentado descorrer el código al mínimo mínimo

 public class DataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int VIEW_TYPE_LOADING = 0; private static final int VIEW_TYPE_ITEM = 1; public boolean isLoading; private int visibleThreshold = 5; private int lastVisibleItem, totalItemCount; public ArrayList<Data> datas; private OnLoadMoreListener mOnLoadMoreListener; public DataAdapter(RecyclerView mRecyclerView, ArrayList<Data> datas) { this.datas = datas; final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager(); mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); totalItemCount = linearLayoutManager.getItemCount(); lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition(); if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) { if (mOnLoadMoreListener != null) { DataAdapter.this.datas.remove(null); mOnLoadMoreListener.onLoadMore(); } } } }); } public static class DataObjectHolder extends RecyclerView.ViewHolder implements View.OnClickListener { //your views public DataObjectHolder(View itemView) { super(itemView); //initialize your views } @Override public void onClick(View v) { if (onItemClickListener != null) onItemClickListener.onItemClick(getAdapterPosition(), v); } } public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } @Override public int getItemViewType(int position) { return datas.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_ITEM) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_data, parent, false); DataObjectHolder dataObjectHolder = new DataObjectHolder(view); return dataObjectHolder; } else if (viewType == VIEW_TYPE_LOADING) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_loading_item, parent, false); return new LoadingViewHolder(view); } return null; } @Override public void onBindViewHolder(final RecyclerView.ViewHolder hldr, final int position) { if (hldr instanceof DataObjectHolder) { final Data data = datas.get(position); final DataObjectHolder holder = (DataObjectHolder) hldr; //bind your views } else if (hldr instanceof LoadingViewHolder) { LoadingViewHolder loadingViewHolder = (LoadingViewHolder) hldr; if (isLoading) { loadingViewHolder.progressBar.setVisibility(View.VISIBLE); loadingViewHolder.progressBar.setIndeterminate(true); loadingViewHolder.tv_load_more.setVisibility(View.GONE); loadingViewHolder.tv_load_more.setOnClickListener(null); } else { loadingViewHolder.progressBar.setVisibility(View.GONE); loadingViewHolder.tv_load_more.setVisibility(View.VISIBLE); loadingViewHolder.tv_load_more.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mOnLoadMoreListener != null) { datas.remove(null); mOnLoadMoreListener.onLoadMore(); } } }); } } } @Override public int getItemCount() { return datas.size(); } public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) { this.mOnLoadMoreListener = mOnLoadMoreListener; } } 

El XML para el loadViewHolder se muestra a continuación, que básicamente se infla para obtener el titular

 <?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="60dp"> <ProgressBar android:id="@+id/progressBar1" android:layout_width="20dp" android:layout_height="20dp" android:layout_centerInParent="true" android:layout_gravity="center" android:indeterminate="true"/> <TextView android:id="@+id/tv_load_more" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:gravity="center" android:text="Tap to load more" android:textSize="14sp"/> </RelativeLayout> 

Espero que lo encuentre útil

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.