摘要:这似乎是将RecyclerView
托管在底部工作表中的问题,因为父片段HomeFragment
托管了子片段的另一个实例ContentFragment
不会嵌套在底表中,而onRestoreInstanceState
会按预期执行。
以RecyclerView
的{{1}}和LayoutManager
方法保存并返回Fragment
onSaveInstanceState
的状态时,预期结果是{ {1}}将显示在与更改配置之前相同的位置。
在屏幕配置更改上,onViewStateRestored
有时会显示在位置 0 而不是配置更改之前的RecyclerView
位置。在某些情况下,它还能成功保留布局状态。由于随机性,这似乎涉及生命周期+底表问题。
RecyclerView
在RecyclerView
上登录为非null。 contentRecyclerView.layoutManager!!.onSaveInstanceState()
在onSaveInstanceState
上登录为非null。savedRecyclerLayoutState
的{{1}}情况下向onViewStateRestored
加载数据后,savedRecyclerLayoutState
记录为非空。层次结构
adapter
由SAVED.name
托管在observeContentUpdated
布局中名为ContentFragment
的{{1}}片段内。 HomeFragment
的{{1}}布局包含BottomSheet
。
正在加载保存状态
在bottomSheet
情况下将数据加载到fragment_home
中的ContentFragment
后,调用 fragment_content
。实例状态在contentRecyclerView
之后设置为onRestoreInstanceState
,因为Adapter
中的单元格是可忽略的,并会导致数据再次加载。这样可确保还原仅在配置更改后发生一次。
HomeFragment.kt
observeContentUpdated
创建一个包含已保存的片段SAVED.name
的底表。
null
ContentFragment.kt
在onRestoreInstanceState
方法中填充了RecyclerView
。
initSavedBottomSheet
fragment_home.xml
ContentFragment
fragment_content.xml
class HomeFragment : Fragment() {
...
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(USER_KEY, user)
outState.putBoolean(APP_BAR_EXPANDED_KEY, isAppBarExpanded)
outState.putBoolean(SAVED_CONTENT_EXPANDED_KEY, isSavedContentExpanded)
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
if (savedInstanceState != null) {
if (savedInstanceState.getBoolean(APP_BAR_EXPANDED_KEY)) appBar.setExpanded(true)
else appBar.setExpanded(false)
if (savedInstanceState.getBoolean(SAVED_CONTENT_EXPANDED_KEY)) {
swipeToRefresh.isEnabled = false
bottomSheetBehavior.state = STATE_EXPANDED
setBottomSheetExpanded()
}
updateAds()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
homeViewModel = ViewModelProviders.of(activity!!).get(HomeViewModel::class.java)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
binding.setLifecycleOwner(this)
binding.viewmodel = homeViewModel
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
user = homeViewModel.getCurrentUser()
...
observeSignIn(savedInstanceState)
initSavedBottomSheet(savedInstanceState)
...
initSwipeToRefresh()
...
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (savedInstanceState == null
&& childFragmentManager.findFragmentByTag(PRICEGRAPH_FRAGMENT_TAG) == null
&& childFragmentManager.findFragmentByTag(CONTENT_FEED_FRAGMENT_TAG) == null) {
childFragmentManager.beginTransaction()
.replace(priceContainer.id, PriceFragment.newInstance(), PRICEGRAPH_FRAGMENT_TAG)
.commit()
childFragmentManager.beginTransaction().replace(contentContainer.id,
ContentFragment.newInstance(Bundle().apply {
putString(FEED_TYPE_KEY, MAIN.name)
}), CONTENT_FEED_FRAGMENT_TAG)
.commit()
}
}
...
private fun initSavedBottomSheet(savedInstanceState: Bundle?) {
bottomSheetBehavior = from(bottomSheet)
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.peekHeight = SAVED_BOTTOM_SHEET_PEEK_HEIGHT
bottomSheet.layoutParams.height = getDisplayHeight(context!!)
if (savedInstanceState == null && homeViewModel.user.value == null)
childFragmentManager.beginTransaction().replace(
R.id.savedContentContainer,
SignInDialogFragment.newInstance(Bundle().apply {
putInt(SIGNIN_TYPE_KEY, FULLSCREEN.code)
}))
.commit()
bottomSheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == STATE_EXPANDED) {
homeViewModel.bottomSheetState.value = STATE_EXPANDED
setBottomSheetExpanded()
}
if (newState == STATE_COLLAPSED) {
isSavedContentExpanded = false
appBar.visibility = VISIBLE
bottom_handle.visibility = VISIBLE
bottom_handle_elevation.visibility = VISIBLE
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
})
...
}
private fun setBottomSheetExpanded() {
isSavedContentExpanded = true
appBar.visibility = GONE
bottom_handle.visibility = GONE
bottom_handle_elevation.visibility = GONE
}
private fun initSavedContentFragment() {
childFragmentManager.beginTransaction().replace(
savedContentContainer.id,
ContentFragment.newInstance(Bundle().apply { putString(FEED_TYPE_KEY, SAVED.name) }),
SAVED_CONTENT_TAG).commit()
}
...
private fun observeSignIn(savedInstanceState: Bundle?) {
homeViewModel.user.observe(this, Observer { user: FirebaseUser? ->
this.user = user
...
if (user != null) { // Signed in.
...
if (savedInstanceState == null || savedInstanceState.getParcelable<FirebaseUser>(USER_KEY) == null) {
initMainContent()
initSavedContentFragment()
}
} else if (savedInstanceState == null) /*Signed out.*/ initMainContent()
})
}
private fun initMainContent() {
(childFragmentManager.findFragmentById(R.id.contentContainer) as ContentFragment)
.initMainContent(false)
}
fun initSwipeToRefresh() {
homeViewModel.isSwipeToRefreshEnabled.observe(viewLifecycleOwner, Observer { isEnabled: Boolean ->
...
(childFragmentManager.findFragmentById(R.id.priceContainer) as PriceFragment)
.getPrices(false, false)
if (homeViewModel.accountType.value == FREE) updateAds()
}
}
private fun updateAds() {
(childFragmentManager.findFragmentById(R.id.contentContainer) as ContentFragment)
.updateAds(true)
if (childFragmentManager.findFragmentById(R.id.savedContentContainer) as ContentFragment != null)
(childFragmentManager.findFragmentById(R.id.savedContentContainer) as ContentFragment)
.updateAds(true)
}
...
}
答案 0 :(得分:0)
作为保存RecyclerView
状态的一种解决方法,可以将位置保存为实例状态。
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
if (contentRecyclerView != null)
when (feedType) {
MAIN.name, DISMISSED.name ->
outState.putParcelable(CONTENT_RECYCLER_VIEW_STATE,
contentRecyclerView.layoutManager!!.onSaveInstanceState())
SAVED.name ->
outState.putInt(CONTENT_RECYCLER_VIEW_POSITION,
(contentRecyclerView.layoutManager as LinearLayoutManager)
.findLastVisibleItemPosition())
}
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
if (savedInstanceState != null)
when (feedType) {
MAIN.name, DISMISSED.name -> savedRecyclerLayoutState = savedInstanceState.getParcelable(CONTENT_RECYCLER_VIEW_STATE)
SAVED.name -> savedRecyclerPosition = savedInstanceState.getInt(CONTENT_RECYCLER_VIEW_POSITION)
}
}
为确保保存的索引没有超出范围,需要进行检查。此外,由于关闭了 RecyclerView 项目,因此清除已保存的索引位置很重要,这样在删除项目后不会更新 RecyclerView ,因为此代码段包含在LiveData
观察者。
if (feedType == SAVED.name && savedRecyclerPosition != 0) {
val position: Int =
if (savedRecyclerPosition >= adapter.itemCount) adapter.itemCount - 1
else savedRecyclerPosition
contentRecyclerView.layoutManager?.scrollToPosition(position)
savedRecyclerPosition = 0
}