androidandroid-recyclerviewfragmentandroid-viewpager2notifydatasetchanged

Android ViewPager2 RecyclerView notifyDataSetChanged() not working


I'm using ViewPager2 and RecyclerView develop to Search activity.
What I want is use text entered in EditText as request parameter and response data is reflected in the RecyclerView.
I checked that the data was responded normally, but it was not reflected in the view.
I read so many questions and posts, but I couldn't solve the problem that the view was not refreshed.
Upon checking, data was added to the list of RecyclerView, the methods of the RecyclerView Adapter were not executed at all.

SearchActivity

public class SearchActivity extends AppCompatActivity {
    private ActivitySearchBinding binding;
    private static final int[] TAB_TITLES = new int[]{R.string.tab_text_1, R.string.tab_text_2};
    private final String key;
    private BusStopInterface busStopInterface;
    private PlaceholderFragment fragment;
    private SectionsPagerAdapter sectionsPagerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivitySearchBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        sectionsPagerAdapter = new SectionsPagerAdapter(SearchActivity.this);
        ViewPager2 pager = binding.viewPager;
        pager.setAdapter(sectionsPagerAdapter);
        TabLayout tabs = binding.tabs;
        EditText searchBox = binding.searchBox;
        fragment = new PlaceholderFragment();
        new TabLayoutMediator(tabs, pager, (tab, position) -> tab.setText(TAB_TITLES[position])).attach();

        RetrofitClient retrofitClient = RetrofitClient.getInstance();
        busStopInterface = RetrofitClient.getRetrofitInterface();

        searchBox.addTextChangedListener(textWatcher);
        pager.registerOnPageChangeCallback(pageChangeCallback);
    }

    ViewPager2.OnPageChangeCallback pageChangeCallback = new ViewPager2.OnPageChangeCallback() {
        @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);
            if (position == 0) {
                Log.d("position", "Bus stop");;
            } else if (position == 1) {
                Log.d("position", "Bus");;
            }
        }
    };

    TextWatcher textWatcher = new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable s) {
        busStopInterface.getBusStop(key, 25, "json", 25, s.toString()).enqueue(new Callback<Example>() {
            @Override
            public void onResponse(Call<Example> call, Response<Example> response) {
                if (response.isSuccessful()) {
                    Example example = response.body();
                    Items items = example.getResult().getBody().getItems();  //I checked the data response.
                    fragment.resetRecyclerView(items.getItem());
                    sectionsPagerAdapter.notifyDataSetChanged();
                    Log.d("retrofit", "Data fetch success");
                } else {
                    Log.d("retrofit", "Data fetch fail");
                }
            }

            @Override
            public void onFailure(Call<Example> call, Throwable t) {
                Log.d("retrofit", t.getMessage());
            }
        });
        }
    };
}

PlaceholderFragment

public class PlaceholderFragment extends Fragment {

    private static final String ARG_SECTION_NUMBER = "section_number";

    private PageViewModel pageViewModel;
    private FragmentSearchBinding binding;

    public List<BusStopItem> items = new ArrayList<>();
    public SearchRecyclerViewAdapter adapter = new SearchRecyclerViewAdapter(items);

    public static PlaceholderFragment newInstance(int index) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle bundle = new Bundle();
        bundle.putInt(ARG_SECTION_NUMBER, index);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        pageViewModel = new ViewModelProvider(this).get(PageViewModel.class);
        int index = 1;
        if (getArguments() != null) {
            index = getArguments().getInt(ARG_SECTION_NUMBER);
        }
        pageViewModel.setIndex(index);
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        binding = FragmentSearchBinding.inflate(inflater, container, false);
        View root = binding.getRoot();

        pageViewModel.getIndex().observe(getViewLifecycleOwner(), new Observer<Integer>() {
            @Override
            public void onChanged(Integer index) {
                if (index == 1) {
                    adapter.setItemViewType(SearchRecyclerViewAdapter.VIEWTYPE_BUS_STOP);
                } else if (index == 2) {
                    adapter.setItemViewType(SearchRecyclerViewAdapter.VIEWTYPE_BUS);
                }
            }
        });
        return root;
    }

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

        RecyclerView recyclerView = binding.searchRecyclerView;
        recyclerView.setAdapter(adapter);
        recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), 1));
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }

    public void resetRecyclerView(List<BusStopItem> newItems) {
        items.clear();
        items.addAll(newItems);
        adapter.notifyDataSetChanged();
    }
}

SearchRecyclerViewAdapter

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

    public static final int VIEWTYPE_BUS_STOP = 0;
    public static final int VIEWTYPE_BUS = 1;
    int mItemViewType;

    List<BusStopItem> busStopItems;

    public class ViewHolder extends RecyclerView.ViewHolder {

        TextView busStopName, busStopId, busStopLocation, busName, busArea, busRoute;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            busStopName = itemView.findViewById(R.id.busStopName);
            busStopId = itemView.findViewById(R.id.busStopId);
            busStopLocation = itemView.findViewById(R.id.busStopLocation);
            busName = itemView.findViewById(R.id.busName);
            busArea = itemView.findViewById(R.id.busArea);
            busRoute = itemView.findViewById(R.id.busRoute);
        }

        public void setBusStopItem(BusStopItem item) {
            busStopName.setText(item.getNodenm());
            if(item.getNodeno() != null) {
                busStopId.setText(item.getNodeno().toString());
            }
        }

    }

    public SearchRecyclerViewAdapter(List<BusStopItem> busStopItems) {
        this.busStopItems = busStopItems;
    }

    public void setItemViewType(int viewType) {
        mItemViewType = viewType;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = null;

        if (viewType == VIEWTYPE_BUS_STOP) {
            view = inflater.inflate(R.layout.search_bus_stop_item, parent, false);
        } else if (viewType == VIEWTYPE_BUS) {
            view = inflater.inflate(R.layout.search_bus_item, parent, false);
        }

        SearchRecyclerViewAdapter.ViewHolder vh = new SearchRecyclerViewAdapter.ViewHolder(view);

        return vh;
    }

    @Override
    public int getItemViewType(int position) {
        return mItemViewType;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        if (mItemViewType == VIEWTYPE_BUS_STOP) {
            holder.setBusStopItem(busStopItems.get(position));
        } else if (mItemViewType == VIEWTYPE_BUS) {
        }
    }

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

}

SectionsPagerAdapter

public class SectionsPagerAdapter extends FragmentStateAdapter {

    public SectionsPagerAdapter(FragmentActivity fa) {
        super(fa);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return PlaceholderFragment.newInstance(position + 1);
    }

    @Override
    public int getItemCount() {
        return 2;
    }
}

What I tried

public void resetRecyclerView(List<BusStopItem> newItems) {
    items.clear();
    items.addAll(newItems);
    adapter.notifyDataSetChanged();
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.detach(this).attach(this).commit();
}

java.lang.NullPointerException: Attempt to invoke virtual method 'androidx.fragment.app.FragmentTransaction androidx.fragment.app.FragmentManager.beginTransaction()' on a null object reference


Solution

  • You create PlaceholderFragment but not add it to FragmmentManger.

    fragment = new PlaceholderFragment();


    When `busStopInterface.getBusStop()` get return, you set data to

    private PlaceholderFragment fragment

    and fragment is not attach to any Activity,so UI dosen't refresh.
    You should get PlaceholderFragment instance from SectionsPagerAdapter or create PlaceholderFragment instance for SectionsPagerAdapter, and you can set data to real PlaceholderFragment

     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            binding = ActivitySearchBinding.inflate(getLayoutInflater());
            setContentView(binding.getRoot());
            fragments = new ArrayList<>();
            fragments.add(PlaceholderFragment.newInstance(1));
            fragments.add(PlaceholderFragment.newInstance(2));
            sectionsPagerAdapter = new SectionsPagerAdapter(SearchActivity.this, fragments);
            ViewPager2 pager = binding.viewPager;
            pager.setAdapter(sectionsPagerAdapter);
            TabLayout tabs = binding.tabs;
            EditText searchBox = binding.searchBox;
            
            new TabLayoutMediator(tabs, pager, (tab, position) -> tab.setText(TAB_TITLES[position])).attach();
    
            RetrofitClient retrofitClient = RetrofitClient.getInstance();
            busStopInterface = RetrofitClient.getRetrofitInterface();
    
            searchBox.addTextChangedListener(textWatcher);
            pager.registerOnPageChangeCallback(pageChangeCallback);
        }
    
    public class SectionsPagerAdapter extends FragmentStateAdapter {
    
        private final List<PlaceholderFragment> fragments;
    
        public SectionsPagerAdapter(FragmentActivity fa, List<PlaceholderFragment> fragments) {
            super(fa);
            this.fragments = fragments;
        }
    
        @NonNull
        @Override
        public Fragment createFragment(int position) {
            return fragments.get(position);
        }
    
        @Override
        public int getItemCount() {
            return 2;
        }
    }
    
    busStopInterface.getBusStop(key, 25, "json", 25, s.toString()).enqueue(new Callback<Example>() {
                    @Override
                    public void onResponse(Call<Example> call, Response<Example> response) {
                        if (response.isSuccessful()) {
                            Example example = response.body();
                            Items items = example.getResult().getBody().getItems();  //I checked the data response.
                            fragments.get(1).resetRecyclerView(items.getItem());
                            Log.d("retrofit", "Data fetch success");
                        } else {
                            Log.d("retrofit", "Data fetch fail");
                        }
                    }
    
                    @Override
                    public void onFailure(Call<Example> call, Throwable t) {
                        Log.d("retrofit", t.getMessage());
                    }
                });
    

    PS. remove

    FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.detach(this).attach(this).commit();