【问题标题】:How to prevent duplicate data appear in RecyclerView when pulling SwipeRefreshLayout?拉SwipeRefreshLayout时如何防止RecyclerView出现重复数据?
【发布时间】:2020-04-23 08:33:26
【问题描述】:

我知道这个问题被问了很多时间,并且我知道通常情况下的答案,即arraylist.clear() 在拉动SwipeRefreshLayout 之前清除arraylist。但在我的情况下,它似乎有点不同,我完全不知道,所以让我一步一步告诉它。

我想做的事:

我有一个RecyclerView,它通常只显示一种类型的数据,即List<Post> posts。从这里开始,这工作得很好。现在,我想将来自 Google Admobs 的 NatvieAds 添加到 RecyclerView 的第一个元素中。

这是我的代码设置:

PostFragment:

public class PostFragment extends Fragment implement .....{

    @Override
    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
        initializeRecyclerView();

        setUpSwipeRefreshLayout();

        mSwipeRefreshLayout.post(new Runnable() {
            @Override
            public void run() {
                mSwipeRefreshLayout.setRefreshing(true);
                postAdapter.removeAllStuff(); //Here clear all the item in post
                getPostInRoom(roomId);
            }
        });
    }

   private void initializeRecyclerView() {
       recyclerView = binding.postRecyclerView;
       recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(),RecyclerView.VERTICAL,false));
       postAdapter = new PostAdapter(this);
       recyclerView.setAdapter(postAdapter);
   }

    private SwipeRefreshLayout mSwipeRefreshLayout;
    private void setUpSwipeRefreshLayout() {
        mSwipeRefreshLayout = binding.swipeRefreshLayout;
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                mSwipeRefreshLayout.setRefreshing(true);
                postAdapter.removeAllStuff(); //Here clear all the item in post
                getPostInRoom(roomId);
            }
        });
    }


    // I calling my own API in ViewModel and observe the LiveData returned.
    private void getPostInRoom(String roomId) {
        viewModel.getAllPostInRoom(roomId).observe(getViewLifecycleOwner(), new Observer<List<Post>>() {
            @Override
            public void onChanged(List<Post> posts) {
                mSwipeRefreshLayout.setRefreshing(false);

                if(posts != null && posts.size() > 0){
                    binding.postRecyclerView.setVisibility(View.VISIBLE);
                    binding.emptyStateContainer.setVisibility(View.GONE);
                    binding.unblockRoomButton.setVisibility(View.GONE);

                    postAdapter.addAllPostToList(posts); // Here add all the arraylist item into the list in adapter
                    getNativeAdsFromAdmobForPostFragment(); // here called for Admobs 

                }else if(Objects.requireNonNull(posts).size() == 0){
                    binding.emptyStateContainer.setVisibility(View.VISIBLE);
                    binding.postRecyclerView.setVisibility(View.GONE);
                }


            }
        });
    }

如您所见,在SwipeRefreshLayout 中调用getPostInRoom() 之前,我已经调用了postAdapter.removeAllStuff()(为此我将附上下面的代码)。所以如果我没记错的话,arraylist应该是清楚的。

这是PostFragment 中用于调用 Admob 进行广告的代码

   //HERE CALLED TO GOOGLE ADMOB FOR THE ADS 
    private AdLoader adLoader;
    public void getNativeAdsFromAdmobForPostFragment(){


    NativeAdOptions adOptions = new NativeAdOptions.Builder()
            .setAdChoicesPlacement(ADCHOICES_TOP_RIGHT)
            .build();

    adLoader = new AdLoader.Builder(getActivity(), getResources().getString(R.string.admob_test_ad))
            .forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
                @Override
                public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
                    // Show the ad.

                    if(!adLoader.isLoading()){

                        postAdapter.addAdsToList(unifiedNativeAd); //here update the ads into the arraylist of the recyclerView
                    }
                }
            })
            .withAdListener(new AdListener() {
                @Override
                public void onAdFailedToLoad(int errorCode) {
                    // Handle the failure by logging, altering the UI, and so on.
                    Log.e("MainActivity", "The previous native ad failed to load. Attempting to"
                            + " load another.");
                    if (!adLoader.isLoading()) {

                    }
                }
            })
            .withNativeAdOptions(adOptions)
            .build();

    adLoader.loadAd(new AdRequest.Builder().build());
  }


}

PostAdapter.java

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

    private static final int UNIFIED_ADS_VIEW  = 1;

    private static final int POST_ITEM_VIEW = 2;

    private List<Object> mRecyclerViewItem = new ArrayList<>();

    public PostAdapter(PostAdapterListener listener) {
        this.listener = listener;
    }

    public void addAllPostToList(List<Post> posts){

        mRecyclerViewItem.addAll(posts); // Here add all the post into the mRecyclerViewItem
        notifyDataSetChanged();
    }

    public void addAdsToList(UnifiedNativeAd unifiedNativeAd){
        mRecyclerViewItem.add(0,unifiedNativeAd); // Here add the 1 nativeAds into the arrayList
        notifyDataSetChanged();
    }

    public void removeAllStuff(){
        mRecyclerViewItem.clear(); // Here already called before `getPostInRoom()` in SwipeFreshLayout
        notifyDataSetChanged();
    }

    @Override
    public int getItemViewType (int position) {


        Object recyclerViewItem = mRecyclerViewItem.get(position);
        if (recyclerViewItem instanceof UnifiedNativeAd) {
            return UNIFIED_ADS_VIEW;
        }
        return POST_ITEM_VIEW;
    }

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

    ... all other code 

}

我现在拥有的:

在上面的所有代码之后,

  1. 第一次加载PostFragment时:行为是正确的,这意味着广告出现在recyclerView的第一项上,然后是我从服务器获取的post

  2. 当我拉SwipeRefreshLayout时:相同的post(这是3个帖子)重复并且RecyclerView中出现一个新广告,每次我拉SwipeRefreshLayout时,另外3个相同的帖子并且 1 个新广告再次插入到 RecyclerView

这意味着,PostAdapater 中的 mRecyclerViewItem 永远不会是 clear(),但是新项目会不断添加到 ArrayList 中,尽管我在获取新项目之前已经 clear()

问题:

  1. 在上述情况下我做错了什么?

  2. 在 1 个 RecyclerView 中处理 2 种类型的 dataPostUnifiedNativeAd)或 2 个数组列表的正确方法是什么?

【问题讨论】:

    标签: android android-recyclerview admob android-livedata swiperefreshlayout


    【解决方案1】:

    RecyclerView 是为了 recycle views 而构建的。其中的所有内容都是一个单独的过程:

    • 设置布局管理器
    • 设置适配器
    • 定义 ViewHolders

    即使在 Setting Adapter 部分,Adapter 也不关心 what 数据是否被传递,它只关心 data is there.

    在您的情况下,我建议不要使用 ArrayList,而是使用 Set。在 Set 中,您不能复制元素,因为这是它的基本条件。将所有刷新逻辑封装在 ViewController 中,在您的情况下看起来像 Fragment

    我想指出的是这个函数:

    public void removeAllStuff(){
            mRecyclerViewItem.clear(); // Here already called before `getPostInRoom()` in SwipeFreshLayout
            notifyDataSetChanged();
    }
    

    应该避免这种情况,因为在 DataSet 中添加或删除项目不需要破坏 RecyclerView 中的所有内容,然后重新构建它。您实际上阻碍了 RecyclerView 的主要目标,即 recycling 部分。而是使用诸如notifyItemChanged(position: Int)之类的智能方法,它会通知RecyclerView只有该位置的元素发生了变化,因此无需担心其他元素.

    【讨论】:

    • 嘿兄弟..using Set 是什么意思?你能给我举个例子吗??
    • 在你问题的最后一部分,你的意思是,当广告是arrived 时,然后在将广告插入arraylistnotifyItemChanged(0)(在我的例子中是第一个元素) ?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多