简体   繁体   English

Android 停止回收器-查看已显示项目的适配器绑定数据

[英]Android Stop recycler-View adapter binding data for already shown items

I have an app that uses a recyclerView to show results from Google Books API.我有一个应用程序使用 recyclerView 来显示来自 Google Books API 的结果。

In every onBindViewHolder , I ask the client to give me data which leads me eventually to exceeding the rate limit since every scroll calls data.在每个onBindViewHolder中,我要求客户端给我数据,这最终导致我超过速率限制,因为每个滚动都调用数据。

Let say I got data for position 1-5 and I scroll down to position 6-10 and then go back to 1-5.假设我得到了 position 1-5 的数据,我向下滚动到 position 6-10,然后 go 回到 1-5。 How can I make sure that it won't call the client again for positions 1-5 since it has already loaded them?我如何确保它不会再次为位置 1-5 调用客户端,因为它已经加载了它们?

I just want whatever it already called to stay there.我只希望它已经调用的任何东西都留在那里。

My adapter looks like this ( I deleted some parts like more views so it wont confuse):我的适配器看起来像这样(我删除了一些像更多视图这样的部分,所以它不会混淆):

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

        }

    }

}

Ok, I understood your problem.好的,我明白你的问题了。 For every bookID, you fetch its data from Google Books, and now you want to not hit the api for already loaded items.对于每个 bookID,您从 Google 图书中获取其数据,现在您不想为已加载的项目点击 api。

Simply, create a global variable for ArrayList as简单地说,为ArrayList创建一个全局变量为

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

Call your onBind() in onBindViewHolder() asonBind() onBindViewHolder()作为

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

Replace your onBind() with this:将您的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
        }
    }
}

This will solve your issue, it will store the already accessed books from the API and will not hit the API again and will use the List to access them.这将解决您的问题,它将存储已从 API 访问的书籍,并且不会再次访问 API 并将使用列表访问它们。

Apart from this, I'll suggest to go with ViewBinding which will replace your whole DiscoverBooksViewHolder into a single line(in Kotlin) or 2-3 lines(in Java), with viewBinding, you'll not have to declare and initialize views and the views can be accessed directly using Binding , you can then extract your onBind() out of the ViewHolder .除此之外,我建议 go 使用ViewBinding它将您的整个DiscoverBooksViewHolder替换为单行(在 Kotlin 中)或 2-3 行(在 Java 中),使用 viewBinding,您不必声明和初始化视图和可以使用Binding直接访问视图,然后可以将 onBind() 从ViewHolder中提取出来。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM