首页 > 解决方案 > Android Stop recycler-View 适配器绑定数据已显示的项目

问题描述

我有一个应用程序使用 recyclerView 来显示来自 Google Books API 的结果。

在每一个onBindViewHolder中,我要求客户给我数据,这最终导致我超过速率限制,因为每次滚动都会调用数据。

假设我得到了位置 1-5 的数据,我向下滚动到位置 6-10,然后返回到 1-5。我如何确保它不会再次为位置 1-5 调用客户端,因为它已经加载了它们?

我只希望它已经调用的任何东西都留在那里。

我的适配器看起来像这样(我删除了一些像更多视图这样的部分,所以它不会混淆):

public class MyBooksAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<DiscoverBooks> discoverBooks;
    private FirebaseFirestore db;
    private FirebaseAuth auth;
    private String UID, BookID2;
    private int count, gbsSize, counter;

    interface OnDiscoverBookClickListener {
        void onClick(DiscoverBooks discoverBooks);
    }

    private OnDiscoverBookClickListener listener;

    public MyBooksAdapter(int counter, List<DiscoverBooks> discoverBooks, int gbsSize, OnDiscoverBookClickListener listener) {
        this.counter = counter;
        this.discoverBooks = discoverBooks;
        this.listener = listener;
        this.gbsSize = gbsSize;
    }

    @Override
    public int getItemViewType(int position) {
        return AppConstants.IS_NOT_ADS_POSITION;
    }


    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
        View view;
        view = LayoutInflater.from( viewGroup.getContext() ).inflate( R.layout.item_book_mybook, viewGroup, false );
        return new DiscoverBooksViewHolder( view );
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        holder.setIsRecyclable( false );
        if (holder instanceof DiscoverBooksViewHolder) {
            ((MyBooksAdapter.DiscoverBooksViewHolder) holder).bind( (discoverBooks.get( position )), holder );
        }
    }

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

    class DiscoverBooksViewHolder extends RecyclerView.ViewHolder {

        private Button tv_Link;
        private CardView cardView;
        private ImageView Iv_BookCover;
        private TextView tv_Author, tv_BorrowedTo, tv_BorrowedTill, tv_DateAdded, tv_Title;
        private ImageButton ib_Options;
        private ToggleButton tb_Status;
        private DiscoverBooks discoverBooks;

        private DiscoverBooksViewHolder(View itemView) {
            super( itemView );

            cardView = itemView.findViewById( R.id.imagecard );
            Iv_BookCover = itemView.findViewById( R.id.iv_BookCover );
            tv_Title = itemView.findViewById( R.id.tv_Title );
            tv_Author = itemView.findViewById( R.id.tv_Author );
            tv_DateAdded = itemView.findViewById( R.id.tv_DateAdded );
            tv_BorrowedTo = itemView.findViewById( R.id.tv_BorrowedTo );
            tv_BorrowedTill = itemView.findViewById( R.id.tv_BorrowedTill );
            tv_Link = itemView.findViewById( R.id.tv_Link );
            ib_Options = itemView.findViewById( R.id.ib_Options );
            tb_Status = itemView.findViewById( R.id.tb_Status );

        }

        private void bind(DiscoverBooks discoverBooks, RecyclerView.ViewHolder viewHolder) {
            this.discoverBooks = discoverBooks;

            db = FirebaseFirestore.getInstance();
            auth = FirebaseAuth.getInstance();

            String BookID = discoverBooks.getBookID();

            if (BookID.length() > AppConstants.UPLOADED_BOOK_LENGTH) {
                db.collection( "Books" ).document( BookID ).get()
                        .addOnCompleteListener( task -> {
                            if (task.isSuccessful()) {
                                DocumentSnapshot document = task.getResult();
                                if (document.exists()) {
                                    DO SOMETHING
                                }
                            }
                        } );

            } else {
            //HERE I CALL GOOGLE BOOKS API
                MyBookClient.getInstance().getBooks( BookID, new JsonHttpResponseHandler() {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                        if (response != null) {
                            final MyBook books = MyBook.fromJson( response );
                            //DO SOMETHING
                        }
                    }
                } );
            }

        }

    }

}

标签: javaandroidandroid-recyclerview

解决方案


好的,我明白你的问题了。对于每个 bookID,您从 Google 图书中获取其数据,现在您不想为已加载的项目点击 api。

ArrayList简单地说,为as创建一个全局变量

private ArrayList<MyBook> mybooks = new ArrayList();

打电话onBind()onBindViewHolder()

((MyBooksAdapter.DiscoverBooksViewHolder) holder).bind(position, discoverBooks.get( position), holder);

用这个替换你onBind()的:

private void bind(int position, UserData discoverBooks, RecyclerView.ViewHolder viewHolder) {
    this.discoverBooks = discoverBooks;

    db = FirebaseFirestore.getInstance();
    auth = FirebaseAuth.getInstance();

    String BookID = discoverBooks.getBookID();

    if (BookID.length() > AppConstants.UPLOADED_BOOK_LENGTH) {
        db.collection("Books").document(BookID).get()
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        DocumentSnapshot document = task.getResult();
                        if (document.exists()) {
                            DO SOMETHING
                        }
                    }
                });

    } else {
        if (position >= myBooks.size() || myBooks.get(position) == null) {
            //If the value doesn't exist in the ArrayList, 
            //it will hit the Google Books API
            MyBookClient.getInstance().getBooks(BookID, new JsonHttpResponseHandler() {
                @Override
                public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                    if (response != null) {
                        final MyBook books = MyBook.fromJson(response);
                        myBooks.add(position, books);
                        //DO SOMETHING
                    }
                }
            });
        } else {
            //If the value has already been loaded in the list, 
            //directly use it without hitting the API
            final MyBook books = myBooks.get(position);
            //DO SOMETHING
        }
    }
}

这将解决您的问题,它将存储已经从 API 访问的书籍,并且不会再次访问 API,并将使用列表来访问它们。

除此之外,我建议使用ViewBinding它将整个替换DiscoverBooksViewHolder为单行(在 Kotlin 中)或 2-3 行(在 Java 中),使用 viewBinding,您不必声明和初始化视图和视图可以直接使用 访问Binding,然后您可以将 onBind() 从ViewHolder.


推荐阅读