ViewPager2+Fragment interface refresh problem

Let's start with the code. You can ignore the creation of sets and fragment s in the adapter style we often write.

public class VP2TaskPagerAdapter extends FragmentStateAdapter {
    private List<TaskTableBean> taskTableBeans;
    public VP2TaskPagerAdapter(FragmentManager fragmentManager, Lifecycle lifecycle,List<TaskTableBean> taskTableBeans) {
        super(fragmentManager, lifecycle);
        this.taskTableBeans = taskTableBeans;
    }
    //Update data
    public void update(List<TaskTableBean> taskTableBeans) {
        this.taskTableBeans = taskTableBeans;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        Fragment fragment = null;
        switch (getItemViewType(position)) {
            case 5:
                fragment = GroupTaskFragment.newInstance();
                break;
            case 7:
                fragment = NewdiscoveryTaskFragment.newInstance();
                break;
            case 4:
                fragment = new GrowthPlanFragmentMvc();
                break;
            case 6:
            case 8:
                fragment = ExclusiveTaskFragment.newInstance();
                break;
            case 9:
                fragment = ExperiencetaskFragment.newInstance();
                break;
            case 0:
            case 1:
            case 2:
            case 3:
                fragment = NewCardFragment.newInstance();
                break;
            case 10:
                fragment = TounLockFragment.newInstance();
                break;
        }
        return fragment;
    }

    @Override
    public int getItemViewType(int position) {
        TaskTableBean taskTableBean = taskTableBeans.get(position);
        return Integer.valueOf(taskTableBean.taskType);
    }

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

ViewPager2 is generally refreshed. What we call is

adapter.notifyDataSetChanged() method, but when the data changes, we will find that our page has not changed. Why? By viewing

FragmentStateAdapter

Source code

private void ensureFragment(int position) {
        long itemId = getItemId(position);
        if (!mFragments.containsKey(itemId)) {
            // TODO(133419201): check if a Fragment provided here is a new Fragment
            Fragment newFragment = createFragment(position);
            newFragment.setInitialSavedState(mSavedStates.get(itemId));
            mFragments.put(itemId, newFragment);
        }
    }

You can see the mFragments created by our fragment Containskey (itemid) is created by the judgment, and the default getItemId method returns the value of position. Let's look at the variable mFragments

Final LongSparseArray < fragment > mfragments, the key of LongSparseArray's data is actually position, so our position must be included in it, so we
When notifyDataSetChanged, the fragment will not be re created, so our data and interface will not be rebuilt.

So how should we update the interface?

There are two options:

The first is to add an update operation directly to the fragment, and call the update method every time it needs to be changed.

The second is what we want to introduce now, by rewriting

The getItemId method lets our adapter help us rebuild this fragment

What exactly should I do?

Now that we know that the system judges whether to rebuild according to the return value of getItemId, we can find a way to change this value.

So how do I return this value?

I use the hashCode() method. If the object changes internally, we can easily know whether the data has changed by overriding the hashCode() method

for example

@Override
    public int hashCode() {
        int hashCode=super.hashCode();
        if (!TextUtils.isEmpty(nowTime)){
            hashCode += nowTime.hashCode();
        }
        if (!TextUtils.isEmpty(jadeStarMsg)){
            hashCode += jadeStarMsg.hashCode();
        }
        return hashCode;
    }

hash accumulation is performed for some important parameters. The probability of repetition is very low, which is basically negligible.

Next, I attach my adapter source code

public class VP2TaskPagerAdapter extends FragmentStateAdapter {
    private List<TaskTableBean> taskTableBeans;
    public VP2TaskPagerAdapter(FragmentManager fragmentManager, Lifecycle lifecycle,List<TaskTableBean> taskTableBeans) {
        super(fragmentManager, lifecycle);
        this.taskTableBeans = taskTableBeans;
    }
    public void update(List<TaskTableBean> taskTableBeans) {
        this.taskTableBeans = taskTableBeans;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        Fragment fragment = null;
        switch (getItemViewType(position)) {
            case 5:
                fragment = GroupTaskFragment.newInstance();
                break;
            case 7:
                fragment = NewdiscoveryTaskFragment.newInstance();
                break;
            case 4:
                fragment = new GrowthPlanFragmentMvc();
                break;
            case 6:
            case 8:
                fragment = ExclusiveTaskFragment.newInstance();
                break;
            case 9:
                fragment = ExperiencetaskFragment.newInstance();
                break;
            case 0:
            case 1:
            case 2:
            case 3:
                fragment = NewCardFragment.newInstance();
                break;
            case 10:
                fragment = TounLockFragment.newInstance();
                break;
        }
        return fragment;
    }

    @Override
    public long getItemId(int position) {
        if (taskTableBeans.size() == 0||taskTableBeans.size()<=position) {
            return 0;
        }
        return taskTableBeans.get(position).hashCode();
    }

    @Override
    public boolean containsItem(long itemId) {
        return false;
    }

    @Override
    public int getItemViewType(int position) {
        TaskTableBean taskTableBean = taskTableBeans.get(position);
        return Integer.valueOf(taskTableBean.taskType);
    }

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

This can solve the problem that the data is not refreshed.

Note: creation of fragment

createFragment method I think many small partners are selected by subscript through the set of fragments passed in. This method can not be updated. And that method is also easy to cause memory leakage. 

If you have any questions, I hope you can raise them.

Keywords: Android Fragment viewpager

Added by Ton Wibier on Sat, 15 Jan 2022 17:12:37 +0200