如何使用RecyclerView实现无限滚动?

时间:2017-03-24 03:57:13

标签: android infinite-scroll

我有一个回收站,里面有卡片视图,我从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;
        }
}

5 个答案:

答案 0 :(得分:1)

你可以使用Android中的SwipeRefreshLayout刷新刷新,并在on refresh override方法中调用你的api

注意:将您的API调用请求放入方法中,并在您的SwipeRefreshLayout的onRefresh方法中调用该方法

答案 1 :(得分:1)

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

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

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

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

如果您需要查看有关如何实现此功能的完整源代码,则会有open source project here in GitHub

答案 2 :(得分:0)

您可以在Recyclerview中添加scrollListener

检查类似的答案here

主要的SO帖子here

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

答案 3 :(得分:0)

创建一个名为&#34的静态布尔变量; ready&#34;并将其初始化为false。

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

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

当item的位置为last时,在onBindViewHolder中设置为true。

答案 4 :(得分:0)

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

在您的适配器中,您需要将计数设置为无限大小,然后当您想要某个项目的位置时,在需要该位置时应使用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)
    }
}

}

它如何工作:

让我们说您的dataSource大小为50,但您的排名为51,表示以下内容:51%50。这将给您排名1的位置,再说一次您的位置是57,而dataSource的大小仍然是50。这意味着您的位置是7。要明确一点,只要您需要无限影响,就可以使用该位置的模块dataSource大小。

ps: 让我们抓狂,说我们滚动到位置11323232323214,然后这意味着11323232323214%50 = 14,因此它将在您的数据源中使用位置14。然后,您可以将您的recyclerview封装在SnapHelper class

中,以消除影响