Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9e7c7d7b authored by Chet Haase's avatar Chet Haase
Browse files

ListView transient state fix

ListView child views with transientState (setHasTransientState(true)) are not
handled correctly when the data set changes, such as when an item is added
or removed. The problem is that the transient views are cached by their
position, but this position is out of sync between the ListView and the adapter
until the ListView layout process is complete.

A better way, which unfortunately only works on ListViews with stable IDs, is
to cache the views by their itemID instead, and to use that ID to determine when
and where to reuse/retrieve a transient view during the ListView layout.

Issue #8254775 View.setHasTransient state has side-effects when deleting content in ListView

Change-Id: Ic3b1669ed79dd6cf9e4c1c6c26f9d75ccf074b3e
parent 6eeb41cf
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -6068,8 +6068,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            mTransientStateCount = 0;
            Log.e(VIEW_LOG_TAG, "hasTransientState decremented below 0: " +
                    "unmatched pair of setHasTransientState calls");
        }
        if ((hasTransientState && mTransientStateCount == 1) ||
        } else if ((hasTransientState && mTransientStateCount == 1) ||
                (!hasTransientState && mTransientStateCount == 0)) {
            // update flag if we've just incremented up from 0 or decremented down to 0
            mPrivateFlags2 = (mPrivateFlags2 & ~PFLAG2_HAS_TRANSIENT_STATE) |
+57 −15
Original line number Diff line number Diff line
@@ -6167,6 +6167,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        private ArrayList<View> mSkippedScrap;

        private SparseArray<View> mTransientStateViews;
        private LongSparseArray<View> mTransientStateViewsById;

        public void setViewTypeCount(int viewTypeCount) {
            if (viewTypeCount < 1) {
@@ -6205,6 +6206,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                    mTransientStateViews.valueAt(i).forceLayout();
                }
            }
            if (mTransientStateViewsById != null) {
                final int count = mTransientStateViewsById.size();
                for (int i = 0; i < count; i++) {
                    mTransientStateViewsById.valueAt(i).forceLayout();
                }
            }
        }

        public boolean shouldRecycleViewType(int viewType) {
@@ -6234,6 +6241,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            if (mTransientStateViews != null) {
                mTransientStateViews.clear();
            }
            if (mTransientStateViewsById != null) {
                mTransientStateViewsById.clear();
            }
        }

        /**
@@ -6281,17 +6291,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        }

        View getTransientStateView(int position) {
            if (mTransientStateViews == null) {
                return null;
            if (mAdapter != null && mAdapterHasStableIds && mTransientStateViewsById != null) {
                long id = mAdapter.getItemId(position);
                View result = mTransientStateViewsById.get(id);
                mTransientStateViewsById.remove(id);
                return result;
            }
            if (mTransientStateViews != null) {
                final int index = mTransientStateViews.indexOfKey(position);
            if (index < 0) {
                return null;
            }
            final View result = mTransientStateViews.valueAt(index);
                if (index >= 0) {
                    View result = mTransientStateViews.valueAt(index);
                    mTransientStateViews.removeAt(index);
                    return result;
                }
            }
            return null;
        }

        /**
         * Dump any currently saved views with transient state.
@@ -6300,6 +6315,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            if (mTransientStateViews != null) {
                mTransientStateViews.clear();
            }
            if (mTransientStateViewsById != null) {
                mTransientStateViewsById.clear();
            }
        }

        /**
@@ -6342,12 +6360,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                    mSkippedScrap.add(scrap);
                }
                if (scrapHasTransientState) {
                    scrap.dispatchStartTemporaryDetach();
                    if (mAdapter != null && mAdapterHasStableIds) {
                        if (mTransientStateViewsById == null) {
                            mTransientStateViewsById = new LongSparseArray<View>();
                        }
                        mTransientStateViewsById.put(lp.itemId, scrap);
                    } else {
                        if (mTransientStateViews == null) {
                            mTransientStateViews = new SparseArray<View>();
                        }
                    scrap.dispatchStartTemporaryDetach();
                        mTransientStateViews.put(position, scrap);
                    }
                }
                return;
            }

@@ -6405,11 +6430,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                            removeDetachedView(victim, false);
                        }
                        if (scrapHasTransientState) {
                            if (mAdapter != null && mAdapterHasStableIds) {
                                if (mTransientStateViewsById == null) {
                                    mTransientStateViewsById = new LongSparseArray<View>();
                                }
                                long id = mAdapter.getItemId(mFirstActivePosition + i);
                                mTransientStateViewsById.put(id, victim);
                            } else {
                                if (mTransientStateViews == null) {
                                    mTransientStateViews = new SparseArray<View>();
                                }
                                mTransientStateViews.put(mFirstActivePosition + i, victim);
                            }
                        }
                        continue;
                    }

@@ -6457,6 +6490,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                    }
                }
            }
            if (mTransientStateViewsById != null) {
                for (int i = 0; i < mTransientStateViewsById.size(); i++) {
                    final View v = mTransientStateViewsById.valueAt(i);
                    if (!v.hasTransientState()) {
                        mTransientStateViewsById.removeAt(i);
                        i--;
                    }
                }
            }
        }

        /**