Amir Dora.
Amir Dora.

Reputation: 2717

Viewpager to update only current visible fragment

i update recycleView inside viewpager fragments and call pagerAdapter.notifyDataSetChanged(); to update them. It updates all recycleviews and the app takes long time. Now to overcome this problem i want to update only the current visible fragment and on ViewPage change update other fragments with notifyDataSetChanged(); is there a way to tell which fragment to update?

ViewPagerAdapter

 private class MyPageAdapter extends FragmentStatePagerAdapter {
            private List<Fragment> fragments;
            private int[] mResources;

        public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {
            super(fm);
            this.fragments = fragments;
        }

        @Override
        public Fragment getItem(int position) {

            return this.fragments.get(position);

        }

        @Override
        public int getCount() {
            return this.fragments.size();
        }

        @Override
        public int getItemPosition(Object object) {
            MyFragment f = (MyFragment) object;
            if (f != null) {
                f.update();
            }
            return super.getItemPosition(object);
        }
    }

this is fragment

public static class MyFragment extends Fragment implements CustomAdapterOdds.OnGameClickListener, Updateable {
        public static final String COLUMN_IN_DISPLAY = "column_in_display";


        RecyclerView recyclerView, recyclerView2;
        HashMap<String, List<OddsFeed>> oddsList1;
        HashMap<String, List<OddsFeed>> oddsList2;
        int oddsColDisp;
        CustomAdapterOdds adapterOdds1, adapterOdds2;

        boolean isvisible = false;

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

            oddsList1 = new HashMap<>();
            oddsList2 = new HashMap<>();
            Bundle b = this.getArguments();
            oddsColDisp = b.getInt(COLUMN_IN_DISPLAY);
            List<GameFeed> leftGames = getArguments().getParcelableArrayList("list");
            if (b.getSerializable("hashmap1") != null && b.getSerializable("hashmap2") != null) {
                oddsList1 = (HashMap<String, List<OddsFeed>>) b.getSerializable("hashmap1");
                oddsList2 = (HashMap<String, List<OddsFeed>>) b.getSerializable("hashmap2");
            }

            View v = inflater.inflate(R.layout.view_table_viewpager_fragment_layout, container, false);

            ArrayList<GameFeed> gameFeedsCol0 = getArguments().getParcelableArrayList("list");

            //recycleview1
            recyclerView = v.findViewById(R.id.table_view_recycle_view_odds1);
            adapterOdds1 = new CustomAdapterOdds(getContext(), leftGames, this, oddsList1, bestSitesList, oddsColDisp);
            configRecyclerViewOdds(getContext(), recyclerView, adapterOdds1);

            //recycleview2

            recyclerView2 = v.findViewById(R.id.table_view_recycle_view_odds2);
            adapterOdds2 = new CustomAdapterOdds(getContext(), leftGames, this, oddsList2, bestSitesList, oddsColDisp + 1, true);
            configRecyclerViewOdds(getContext(), recyclerView2, adapterOdds2);

            return v;
        }


        @Override
        public void setMenuVisibility(final boolean visible) {
            super.setMenuVisibility(visible);
            if (visible) {
                isvisible = true;
            }
        }

        @Override
        public void onResume() {
            super.onResume();
            Toast.makeText(getContext(), "onresume", Toast.LENGTH_SHORT).show();
            adapterOdds1.updateListOdds(leftGames, oddsList1, oddsColDisp);
            adapterOdds2.updateListOdds(leftGames, oddsList2, oddsColDisp + 1);

        }

        @Override
        public void OnGameClickListener(View view, int position) {

        }

        @Override
        public void update() {

            Toast.makeText(getContext(), "update", Toast.LENGTH_SHORT).show();
            if (isVisible()) {
                adapterOdds1.updateListOdds(leftGames, oddsList1, oddsColDisp);
                adapterOdds2.updateListOdds(leftGames, oddsList2, oddsColDisp + 1);
             updateViewpager++;
                int size = bestSitesList.size() / 2;

                adapterOdds1.updateListOdds(leftGames, oddsList1, oddsColDisp);
                adapterOdds2.updateListOdds(leftGames, oddsList2, oddsColDisp + 1);

                if (updateViewpager == size - 1) {
                    progressBar.setVisibility(View.INVISIBLE);
                    updateViewpager = 0;
                }
           }
        }
    }

Upvotes: 0

Views: 4246

Answers (1)

Andrew
Andrew

Reputation: 10262

I'm sorry this is a bit of rambling answer but there was not enough code example given to make a functioning answer, so more really trying to explain a concept. Really need details of when and how the data is updated BUT...

As you are using androidx, you might want to consider moving to viewpager2 or with viewpager you can change how the fragment lifecycle states are managed.

If you changed to using BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT https://developer.android.com/reference/androidx/fragment/app/FragmentStatePagerAdapter#BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT when you constructed the Adapter https://developer.android.com/reference/androidx/fragment/app/FragmentStatePagerAdapter#FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager,%20int)

Then all the Fragments will only be brought up to "Started" when created or re-created and only the current one on screen with "Resumed" and then "Paused" when moved off screen.

The exact mechanics depends on how the Fragments recyclerviews get their data and what triggers the update but then general idea is for the Fragments onCreateView to create a lightweight "shell" of a layout, I basically have the static buttons/text and create the recyclerview with an empty dataset.

Then in the onResume method of the Fragment which only gets called for the Fragment currently on Screen it calls your update method to replace the two recyclerview's empty datasets with the actual dataset and does a notifyDataSetChanged() on the recyclerviews.

Therefore when the viewpager is initially created X number of Fragments gets created and there static content is laid out plus 1 Fragment (the current one on screen) gets the recyclerview populated with actual data.

You might also then want to put in some optimisation checks in onResume of the Fragment to check the recyclerview views data has actually changed (a simple size check or using Recyclerview's DiffUtils) otherwise as you move between the Fragment's in the viewpager each Fragment will be paused/resumed.

This really only delays the cost of the two recyclerviews in the Fragment until it is really needed (when it is about to be displayed), it's a form of "Lazy loading"

With moving the "dynamic" data to this "lazy loading" is could be possible to remove the need to notifyDataSetChanged on the Fragments BUT the code snippets don't show enough about how and why the recyclerviews content changes.

With this method when the data is drawn is changed and you might not like how it looks.

This is really at a high level an inversion of the Fragments update logic, instead of saying "The data has changed redraw the data in all the Fragments" it is "This fragment is been shown, redraw the data IF it has been changed since the last time I drew it"

Upvotes: 1

Related Questions