Fragments and FragmentPagerStateAdapter - recreation of views with same id fails

238 Views Asked by At

I'm using a ViewPager with a FragmentPagerStateAdapter. Every page has a list, manually managed => I create views for this rows by inflating a layout from xml. Resulting in, that views in different rows have the same id. Because the rows are inflated from XML...

The behaviour now is following:

Page 1 looks like following:

// Page 1:
// Row 1: EditText = "Test1" 
// Row 2: EditText = "Test2"

I swipe to Page 2 and then to Page 3, afterwards I swipe back to Page 1 and Page 1 looks like following:

// Page 1:
// Row 1: EditText = "Test2" 
// Row 2: EditText = "Test2"

Problem: All EditTexts display the value of the lastEditText in the list... (Note, the EditTexts have the same ID).

If I manually set the ids of my EditTexts to the row index, everything works fine.

Is this normal? Is there an easy (good) solution for that problem?

1

There are 1 best solutions below

2
On

Tt seems like a bug in Android 3.X

The solution is setting ID's manually so they never match between fragments or resetting id's when Fragment becomes invisible. I like the second one since it can be easily disabled or enabled and requires no special id handling.

/**
 * Used for resetting and restoring View id's. This is used as a workaround for
 * Android 3.x when ViewPager Fragment instances with the same layout have
 * problems referencing next focus id's.
 */
public final class ViewIdResetter {

    private final Map<View, Integer> viewMap;

    public ViewIdResetter(final View rootView) {
        this.viewMap = new HashMap<View, Integer>();
        this.fillmap(rootView);
    }

    private void fillmap(final View view) {
        if (view instanceof ViewGroup) {
            final ViewGroup vg = (ViewGroup) view;
            for (int i = 0; i < vg.getChildCount(); i++) {
                this.fillmap(vg.getChildAt(i));
            }
        }
        this.viewMap.put(view, view.getId());
    }

    public void clearIds() {
        for (final View key : this.viewMap.keySet()) {
            key.setId(-1);
        }
    }

    public void restoreIds() {
        for (final View key : this.viewMap.keySet()) {
            key.setId(this.viewMap.get(key));
        }
    }

}

The Fragment

private ViewIdResetter resetter;

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    this.resetter = new ViewIdResetter(view);
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        if (this.resetter != null) {
            this.resetter.restoreIds();
        }
    } else {
        if (this.resetter != null) {
            this.resetter.clearIds();
        }
    }
}

setUserVisibleHint is triggered by ViewPager when the Fragment becomes visible. The ViewIdResetter makes sure only one Fragment at a time has the same id's set.