{"action":"created","issue":{"url":"https://api.github.com/repos/davideas/FlexibleAdapter/issues/581","repository_url":"https://api.github.com/repos/davideas/FlexibleAdapter","labels_url":"https://api.github.com/repos/davideas/FlexibleAdapter/issues/581/labels{/name}","comments_url":"https://api.github.com/repos/davideas/FlexibleAdapter/issues/581/comments","events_url":"https://api.github.com/repos/davideas/FlexibleAdapter/issues/581/events","html_url":"https://github.com/davideas/FlexibleAdapter/issues/581","id":310321512,"number":581,"title":"selected item positions mistakenly adjusted when view state restored","user":{"login":"technicalflaw","id":7760019,"avatar_url":"https://avatars3.githubusercontent.com/u/7760019?v=4","gravatar_id":"","url":"https://api.github.com/users/technicalflaw","html_url":"https://github.com/technicalflaw","followers_url":"https://api.github.com/users/technicalflaw/followers","following_url":"https://api.github.com/users/technicalflaw/following{/other_user}","gists_url":"https://api.github.com/users/technicalflaw/gists{/gist_id}","starred_url":"https://api.github.com/users/technicalflaw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/technicalflaw/subscriptions","organizations_url":"https://api.github.com/users/technicalflaw/orgs","repos_url":"https://api.github.com/users/technicalflaw/repos","events_url":"https://api.github.com/users/technicalflaw/events{/privacy}","received_events_url":"https://api.github.com/users/technicalflaw/received_events","type":"User","site_admin":false},"labels":[{"id":345210876,"url":"https://api.github.com/repos/davideas/FlexibleAdapter/labels/support","name":"support","color":"fbca04","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-04-01T12:53:53Z","updated_at":"2018-04-21T09:30:50Z","closed_at":null,"author_association":"NONE","body":"In the case where the adapter's view state is restored, and that restoration includes the selected item positions, and if this happens *before* the adapter's data set is updated, then when the actual data is updated *with an animation* (i.e. it causes insertion notifications, not just a single change notification which a non-animated update would do), then the selected item positions are adjusted as if they were inserted before that update (which, until now, there were not).\r\n\r\nFor example\r\n* an adapter that had 2 items, both selected, has its state restored (e.g. after a rotation).\r\n* state restoration happens (via `Fragment#onViewStateRestored()`) before the adapter's data set is populated \r\n* the adapter's data set is populated with animation (e.g. via `LoaderManager.LoaderCallback#onLoaderFinished()`)\r\n* this causes `AdapterDataObserver#onItemRangeInserted()` calls for all items (because the adapter was previously empty)\r\n* the adapter's internal observer observes this, and calls `adjustPositions()` for the selected item positions\r\n* selected item positions (0 & 1) are offset to (2 & 3), but the data set only contains 2 items (at positions 0 & 1)\r\n\r\nWorkarounds include making sure your initial `updateDataSet()` is not animated, delaying view state restoration until after the initial update, or perhaps just handling the selection restoration logic yourself. You could also (if it's the initial update), take incorrect selection before `updateDataSet()`, and apply it after the update. But remember that an animated update is multithreaded, so it'll have to be done probably via an overload to `FlexibleAdapter#onPostUpdate`\r\n\r\n(FWIW I would suggest that if the adapter is null when the state is restored it should be cached until the adapter is set non-null)\r\n"},"comment":{"url":"https://api.github.com/repos/davideas/FlexibleAdapter/issues/comments/383281014","html_url":"https://github.com/davideas/FlexibleAdapter/issues/581#issuecomment-383281014","issue_url":"https://api.github.com/repos/davideas/FlexibleAdapter/issues/581","id":383281014,"user":{"login":"davideas","id":12213847,"avatar_url":"https://avatars2.githubusercontent.com/u/12213847?v=4","gravatar_id":"","url":"https://api.github.com/users/davideas","html_url":"https://github.com/davideas","followers_url":"https://api.github.com/users/davideas/followers","following_url":"https://api.github.com/users/davideas/following{/other_user}","gists_url":"https://api.github.com/users/davideas/gists{/gist_id}","starred_url":"https://api.github.com/users/davideas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davideas/subscriptions","organizations_url":"https://api.github.com/users/davideas/orgs","repos_url":"https://api.github.com/users/davideas/repos","events_url":"https://api.github.com/users/davideas/events{/privacy}","received_events_url":"https://api.github.com/users/davideas/received_events","type":"User","site_admin":false},"created_at":"2018-04-21T09:30:50Z","updated_at":"2018-04-21T09:30:50Z","author_association":"OWNER","body":"@technicalflaw, I think you already understood to do the restoration after adapter has been filled.\r\n1. `Fragment.onCreate()`.\r\n2. `Fragment.onActivityCreated()`.\r\n3. `Fragment.onViewStateRestored()`.\r\n4. `Activity.onRestoreInstanceState()`.\r\n5. I suppose the async update occurs here.\r\n6. Call here `mAdapter.onRestoreInstanceState()`.\r\n\r\nIn the demoApp the items are taken synchronously from memory, passed to the constructor and selection restored synchronously from the Activity.\r\nAnimation is not important here, the thing important is to have the items in the adapter list!\r\nAlso, the logic for rotation has to be done only if the `Bundle savedInstanceState` is not null.\r\n\r\nIn the future versions, I might move the selection state in the adapter items and remove the `adjustSelection`."}}
{"actor":{"display_login":"davideas"}}