【问题标题】:Recyclerview view is not working with setNestedScrollingEnabled and its never recycle any views alsoRecyclerview 视图不能与 setNestedScrollingEnabled 一起使用,它也从不回收任何视图
【发布时间】:2018-01-10 16:45:43
【问题描述】:

我面临两个问题:

  1. 滚动监听器不起作用
  2. RecyclerView 在附加到 NestedScrollView 时从不回收任何视图。它就像 ScrollView 内的线性布局。它使用大量内存并产生滞后。

我在回收站视图顶部附加了一个 youtube 播放器片段,因为我无法将片段放在回收站视图中。在我的代码中你可以看到有一个框架布局。

我的布局是这样的:

    <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>

我想使用滚动监听器来加载更多项目 所以我尝试了以下方法:

@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();
    }


}

但是layoutManager.findFirstVisibleItemPosition()==0 所以它不起作用,而且自从我设置了onScrolled never called twice recyclerview.setNestedScrollingEnabled(false)

所以我在onBindView 中尝试过这样的

   public void onBindViewHolder(RecyclerView.ViewHolder customViewHolder, int i) {
    Log.w("d","inside bind view");

    if(i>=getItemCount()-1  &&   !datamanager.isDataLoading()){
        datamanager.loadmoreData();
    }

但是recylerview在我开始滚动之前一次绑定了所有视图,所以这个方法也不起作用。

【问题讨论】:

    标签: android android-recyclerview android-nestedscrollview onscrolllistener


    【解决方案1】:

    回收器视图在附加到嵌套滚动视图时从不回收任何视图,它在滚动视图内的线性布局上设置。它会创建巨大的内存并滞后于屏幕。

    没错。您不能将 RecyclerView 放在另一个滚动视图中。

    这是行不通的,因为包裹视图需要知道 RecyclerView 的总高度,而 RecyclerView 只能通过测量和布局它的 所有项来知道它的总高度。

    如何解决这个问题?

    不要将 RecyclerView 放在另一个滚动视图中。

    如果您需要页眉或页脚,则必须将其添加到 RecyclerView 并让 RecyclerView 负责显示它。摆脱您的 ScrollView 并将您的标题移动到 RecyclerView 的内部

    从技术上讲,也可以在 RecyclerView 中加载片段,但我不得不承认要让它正常工作有点棘手。

    还有很多库可以促进这个过程,我个人最喜欢的是 AirBnb 制作的 Epoxy,但也有 Groupie 等等。

    【讨论】:

    • 我已经加载了片段,但是回收器视图不允许片段状态。它随机崩溃。
    【解决方案2】:

    我处理过这种情况。有一个recyclerview。回收站视图有 viewpager。 viewpager有fragment,然后fragment里面有recyclerview。

    您需要修改触摸以使其完美滚动。

    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;
        }
    

    【讨论】:

      【解决方案3】:

      这是我实现它的方法,我尝试将代码精简到最低限度

      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;
          }
      
      }
      

      loadingViewHolder 的 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="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>
      

      希望你会觉得这很有帮助

      【讨论】:

      • 我正在使用 recyclerview 顶部的片段。我不能这样写
      • 抱歉,我在回答另一个问题
      • 如果您确定自己发布的答案有误,请随时删除。
      • 你知道吗,是的,我没有发布错误问题的答案。当您滚动到底部时,我解决了将更多项目加载到回收器视图中的部分。正如其他人指出的那样,您不能在回收站视图中使用片段。总有更好的方法来实现你想要做的事情
      猜你喜欢
      • 1970-01-01
      • 2020-06-27
      • 2020-07-23
      • 2016-10-02
      • 2015-11-29
      • 1970-01-01
      • 1970-01-01
      • 2015-08-29
      • 1970-01-01
      相关资源
      最近更新 更多