Как предотвратить дублирование данных в RecyclerView при извлечении SwipeRefreshLayout? - PullRequest
0 голосов
/ 22 апреля 2020

Я знаю, что этот вопрос задают много раз, и я знаю ответ для обычного случая: arraylist.clear() очистите arraylist перед вытягиванием SwipeRefreshLayout. Но в моем случае это выглядит немного по-другому, и я понятия не имею об этом, поэтому позвольте мне рассказать об этом шаг за шагом.

Что я хочу сделать:

У меня есть RecyclerView, который обычно представляет только 1 тип данных, который List<Post> posts. Отсюда это работает отлично. Теперь я хочу добавить NatvieAds из Google Admobs к первому элементу 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);
                }


            }
        });
    }

Так что, как вы можете видеть, перед getPostInRoom() вызывается в SwipeRefreshLayout, я уже вызывал 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());
  }


}

PostAdapater. 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 снова.

Это означает, что mRecyclerViewItem в PostAdapater никогда не будет clear(), но новый элемент продолжает добавляться в ArrayList, хотя я уже clear() прежде чем я получу новый предмет.

Вопрос:

  1. Что я делаю не так в описанной выше ситуации?

  2. Что такое правильный способ обработки 2 типов data (Post и UnifiedNativeAd в моем случае) или 2-х массивов в 1 RecyclerView?

1 Ответ

0 голосов
/ 23 апреля 2020

RecyclerView построен с намерением recycle views. Все в нем - отдельный процесс:

  • Настройка layoutManager
  • Настройка адаптера
  • Определение ViewHolders

Даже в Setting Adapter часть, Adapter все равно what данные передаются, важно только, чтобы data is there.

В вашем случае я бы предложил вместо ArrayList использовать Set. В Set нельзя дублировать элементы, поскольку это является его базовым критерием. Сохраняйте все освежающие логику c в ViewController, которая в вашем случае выглядит как Fragment.

Я хотел бы отметить одну функцию:

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

Этого следует избегать, поскольку добавление или удаление элемента в DataSet не должно требуется уничтожить все в RecyclerView, а затем восстановить его. Вы на самом деле препятствует основной цели RecyclerView, часть recycling. Скорее используйте интеллектуальные методы, такие как notifyItemChanged(position: Int), которые уведомят RecyclerView, что изменился только элемент в этом месте, поэтому не нужно беспокоиться о других элементах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...