【问题标题】:How to implement Infinite Scrolling with RecyclerView?如何使用 RecyclerView 实现无限滚动?
【发布时间】:2017-08-16 21:35:29
【问题描述】:

我有一个回收器,里面有卡片视图,我从 REST 服务获取信息,我正在尝试实现无限滚动,假设用户每次向下滚动时都会看到 10 个卡片视图,直到没有显示更多卡片视图,我该如何实现?

我看过一些例子,但没有一个能真正帮助我了解如何做到这一点。我什至不知道我需要在adapter.class或我的Fragment.class中放什么,因为我不明白如何实现它,如果有人能告诉我实现无限滚动的正确方法会很棒我的代码...

提前致谢。

MainAdapter.class

public class MainAdapter extends RecyclerView.Adapter<MainAdapter.ViewHolder> implements View.OnClickListener
{
    private ArrayList<Business> businessList;
    private Activity activity;
    private int layoutMolde,idb;

    public MainAdapter(Activity activity, ArrayList<Business> list, int layout) 
    {
        this.activity = activity;
        this.businessList = list;
        layoutMolde = layout;
    }

    @Override
    public MainAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_row, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) 
    {
        holder.mTitle.setText(businessList.get(position).getBusiness_name());
        holder.number_rating.setText(businessList.get(position).getRating().toString());
        Glide.with(activity).load(businessList.get(position).getLogo_url_string()).into(holder.mImg);
    }

    @Override
    public int getItemCount() {
        return businessList.size();
    }

    @Override
    public void onClick(View v) 
    {

    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public TextView mTitle;
        public ImageView mImg;
        public ImageView logo;
        public RatingBar main_rating;
        public TextView number_rating;

        public ViewHolder( View itemView) 
        {
            super(itemView);
            mTitle = (TextView) itemView.findViewById(R.id.nom_business_main);
            number_rating = (TextView) itemView.findViewById(R.id.number_rating);
            mImg = (ImageView) itemView.findViewById(R.id.img_main);
            main_rating=(RatingBar) itemView.findViewById(R.id.rating_main);
            main_rating.setRating((float)1);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v)
                {

                    Intent in = new Intent(v.getContext(), BusinessPremium.class);
                    int position = getAdapterPosition();
                    idb = businessList.get(position).getId();
                    in.putExtra("no", idb);
                    v.getContext().startActivity(in);
                }
            });
        }
    }
}

FeedsFragment.class

    public class FeedsFragment extends Fragment
    {

        private ArrayList<Business> arrayBusiness,arrayBasics;
        private Gson gson;

        private static final Type BUSINESS_TYPE = new TypeToken<ArrayList<Business>>() {}.getType();
        private RecyclerView.LayoutManager mLayoutManager;

        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
        {
            View android = inflater.inflate(R.layout.fragment_feeds, container, false);

            if (!internetConnectionCheck(FeedsFragment.this.getActivity())) 
            {
                Toast.makeText(getApplicationContext(), "Error de Conexión", Toast.LENGTH_LONG).show();
            }

            new RequestBase(getActivity()) {

                @Override
                public JsonObject onHttpOk(JsonObject response) throws JSONException {

                    JsonObject objeto, pagination_details = null, details, premium_img;
                    JsonArray data;

                    if (getActivity() == null)
                        return response;

                    if (response.get("pagination") == null) 
                    {
                        objeto = response;

                    } else {
                        objeto = response;
                        pagination_details = response.get("pagination").getAsJsonObject();
                        data = objeto.get("data").getAsJsonArray();
                        gson = new Gson();
                        arrayBusiness = gson.fromJson(data, BUSINESS_TYPE);
                        Log.d("size", String.valueOf(arrayBusiness.size()));
                        FeedsFragment.this.getActivity().runOnUiThread(new Runnable()
                        {
                            @Override
                            public void run() 
                            {

                                RecyclerView recycler = (RecyclerView) FeedsFragment.this.getActivity().findViewById(R.id.recycler_main);
                                MainAdapter adapter = new MainAdapter(getActivity(), arrayBusiness, R.layout.main_row);

                                recycler.setNestedScrollingEnabled(false);
                                mLayoutManager = new GridLayoutManager(FeedsFragment.this.getActivity(), 2);
                                recycler.setLayoutManager(mLayoutManager);
                                recycler.setAdapter(adapter);

                                GifTextView loading = (GifTextView)FeedsFragment.this.getActivity().findViewById(R.id.loading);
                                TextView loadingText = (TextView)FeedsFragment.this.getActivity().findViewById(R.id.loadingText);
                                loading.setVisibility(View.GONE);
                                loadingText.setVisibility(View.GONE);

                            }
                        });
                    }
                    if (pagination_details.isJsonNull()) {
                        Log.d("Paginacion", pagination_details.toString());
                    }
                    return objeto;
                }

                @Override
                public void onHttpCreate(JsonObject response) throws JSONException 
                {

                }

                @Override
                public void onHttpUnprocessableEntity(JsonObject response) throws JSONException 
                {
                    this.cancel(true);
                    final String error = response.get("errors").toString();
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getActivity().getApplicationContext(), error, Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }.execute("businesses/premiums", "GET");

            return android;
        }
}

【问题讨论】:

  • 使用嵌套滚动视图而不是滚动视图

标签: android infinite-scroll


【解决方案1】:

您可以在 android 中使用 SwipeRefreshLayout 进行刷新,并在刷新覆盖方法中调用您的 api

注意:将你的 API 调用请求放在一个方法中,然后在你 SwipeRefreshLayout 的 onRefresh 方法中调用该方法

【讨论】:

    【解决方案2】:

    在编写RecyclerView.Adapter 时,无论如何都需要提供getItemCount 方法来返回正确的项目数(可能很大)。 RecyclerView 会主动调用这个适配器的onBindViewHolder(holder, position) 方法。您所需要的只是提供与该职位相关的检索数据的功能。根本没有区别,如果您的列表小于屏幕,略大于屏幕或 Integer.MAX_VALUE 大小。 RecyclerView 会注意不要获取/分配太多额外的项目。

    您确实不需要需要实现滚动侦听器或以其他方式显式处理滚动。

    唯一棘手的部分是您可能需要执行较长的操作(例如服务器调用)来获取某些项目。然后只需在第一次调用时返回未初始化的持有者(空视图)并开始在后台线程中获取所需的行。当您拥有它时,请致电notifyDataSetChangednotifyItemRangeChanged,RecyclerView 会自行更新。

    出于性能原因,我强烈建议以固定大小的块更新内容,而不是按显示的每一行发送单独的服务器请求。对于像 Google 图书这样的一些公共服务器,这显然是一项要求,因为它们每个请求都有配额限制。

    如果您需要查看如何实现此功能的完整源代码,请访问open source project here in GitHub

    【讨论】:

      【解决方案3】:

      创建一个名为“ready”的静态布尔变量并将其初始化为 false。

      在 onLoadMore 方法中添加 if ready 条件,如下所示。

      public boolean onLoadMore(int page, int totalItemsCount) {
               if (ready) {
                 //load more from API
               }         
         return false;
      }
      

      在onBindViewHolder中,当item的位置是最后一个时,设置为true。

      【讨论】:

        【解决方案4】:

        这是我的一位同事介绍的一种方式。我们一起工作,我成功地实施了它,没有任何问题。我想回馈任何有这个问题的人。

        在您的适配器中,您需要将计数设置为无限大小,然后当您想要某个项目的位置时,您应该在需要该位置时使用 val loopPos = position % dataSource.size。让我们看看如何在 recyclerView 适配器中完成此操作,但也可以应用于 FragmentStatePagerAdapter。

        class InfiniteLoopingHorizontalRecyclerViewAdapter(var dataSource: ArrayList<String>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
        
        
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            val inflatedView: View = LayoutInflater.from(parent.context)
                    .inflate(R.layout.your_finite_layout, parent, false)
            return ItemHolder(inflatedView)
        }
        
        override fun getItemCount(): Int {
            return Integer.MAX_VALUE  //***** this should be high enough - wink wink ******
        }
        
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        //****** this is critical here when you need the position use the loopPos ****/
            val loopPos = position % dataSource.size
            (holder as? ItemHolder)?.bind(dataSource[loopPos], loopPos)
        }
        
        inner class ItemHolder(view: View) : RecyclerView.ViewHolder(view) {
        
            fun bind(myString: String, position: Int) = with(itemView) {
              myTextView.setText(myString)
            }
        }
        

        }

        它是如何工作的:

        假设您的数据源大小为 50,但您的位置为 51,这意味着: 51%50 。这给了你位置 1。让我们再说一遍你的位置是 57,你的数据源大小仍然是 50。这意味着你的位置是 7。所以要清楚,任何时候你需要一个无限的影响,你可以使用位置的模块和数据源大小。

        ps: 让我们疯狂地说我们滚动到位置 11323232323214 那么这意味着 11323232323214%50 = 14 所以它在您的数据源中的位置 14 将被使用。然后,您可以通过将您的 recyclerview 包装在 SnapHelper class 中来消除影响

        【讨论】:

        • 这会一遍又一遍地显示相同的数据?我认为这不是他们想要的。
        【解决方案5】:

        您可以将scrollListener 添加到您的回收站视图。

        检查类似的答案here

        还有主要的 SO 帖子 here

        在哪里,scrollListener 将检查您在回收站视图中的确切位置,并根据某些逻辑(您可以灵活编写)进行第二次调用!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-05
          • 1970-01-01
          • 1970-01-01
          • 2015-05-18
          • 2017-05-21
          • 1970-01-01
          相关资源
          最近更新 更多