Android Jetpack 导航禁用滚动位置
Posted
技术标签:
【中文标题】Android Jetpack 导航禁用滚动位置【英文标题】:Android Jetpack Navigation Disable Scroll Position 【发布时间】:2020-09-25 10:21:03 【问题描述】:我有一个包含许多片段的活动(使用喷气背包导航)。在我的第一个片段上,我有一个 recyclerview。如果我在第一个片段上滚动,然后导航到另一个片段,该片段将保留滚动位置,我不希望这样。一个例子如下:
即假设我有两个片段 A 和 B,当我的应用程序启动时,它从 A 开始。假设我开始在 A 上滚动,然后导航到 B。我的应用程序保留 B 上的滚动位置,这不是我想要的。我希望片段 B 从顶部开始。然后当它返回到片段 A 时,我希望它保留之前滚动的滚动位置。
片段 A.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="ViewModel"
type="....AccountViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/Layout_Fragment_Account"
android:layout_
android:layout_>
<!--
Recyclerview
-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/RecyclerView_Account"
android:layout_
android:layout_
android:visibility="@ViewModel.accountListVisibility? View.VISIBLE : View.GONE"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<!--
Empty Views and group
-->
<androidx.constraintlayout.widget.Group
android:id="@+id/Empty_View"
android:layout_
android:layout_
android:visibility="@ViewModel.accountEmptyViewVisibility?
View.VISIBLE : View.GONE"
app:constraint_referenced_ids="Empty_View_Illustration,Empty_View_Title,Empty_View_Subtitle" />
<ImageView
android:id="@+id/Empty_View_Illustration"
android:layout_
android:layout_
android:layout_gravity="center_horizontal"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="@+id/Empty_View_Title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:srcCompat="@drawable/il_account" />
<TextView
android:id="@+id/Empty_View_Title"
style="@style/Locky.Text.Title6"
android:layout_
android:layout_
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:text="@string/text_title_emptyView_accounts"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@id/Empty_View_Subtitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/Empty_View_Illustration"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent=".8" />
<TextView
android:id="@+id/Empty_View_Subtitle"
style="@style/Locky.Text.Subtitle"
android:layout_
android:layout_
android:layout_gravity="center_horizontal"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="?attr/actionBarSize"
android:text="@string/text_subtitle_emptyView_accounts"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/Empty_View_Title"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent=".8" />
<!--
Progress Bar
-->
<include
android:id="@+id/Progress_Bar"
layout="@layout/custom_view_list_loading"
android:layout_
android:layout_
android:visibility="@ViewModel.loadingStatus? View.VISIBLE : View.GONE"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
片段 A.kt:
class AccountFragment : Fragment()
private var _binding: FragmentAccountBinding? = null
private var _viewModel: AccountViewModel? = null
private var _lastClickTime: Long = 0
private val binding get() = _binding!!
private val viewModel get() = _viewModel!!
companion object
const val TAG = "ACCOUNT_FRAGMENT_DEBUG"
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
// Inflate the layout for this fragment
_binding = FragmentAccountBinding.inflate(inflater, container, false)
// Fetch view model
_viewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
//Bind view model to layout
binding.viewModel = _viewModel
// Bind lifecycle owner
binding.lifecycleOwner = this
return binding.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
/* Hides the soft keyboard */
hideSoftKeyboard(binding.root)
/* Observe snack bar events */
observeSnackBarEvent()
/* Observe the account list changes */
observeAccounts()
/* Observe back stack entry result after navigating from sort sheet */
observeBackStackEntryForSortSheet()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater)
inflater.inflate(R.menu.menu_toolbar_filter, menu)
super.onCreateOptionsMenu(menu, inflater)
override fun onOptionsItemSelected(item: MenuItem): Boolean
return when (item.itemId)
R.id.Toolbar_Filter ->
navigateToSort()
true
else -> false
override fun onDestroyView()
super.onDestroyView()
_binding = null
/*
* My Functions
*/
private fun observeBackStackEntryForSortSheet()
val navController = findNavController()
// After a configuration change or process death, the currentBackStackEntry
// points to the dialog destination, so you must use getBackStackEntry()
// with the specific ID of your destination to ensure we always
// get the right NavBackStackEntry
val navBackStackEntry = navController.getBackStackEntry(R.id.Fragment_Account)
// Create our observer and add it to the NavBackStackEntry's lifecycle
val observer = LifecycleEventObserver _, event ->
if (event == Lifecycle.Event.ON_RESUME
&& navBackStackEntry.savedStateHandle.contains(KEY_ACCOUNTS_SORT)
)
viewModel.sortChange(
navBackStackEntry.savedStateHandle.get<AccountSort>(
KEY_ACCOUNTS_SORT
)!!
)
navBackStackEntry.savedStateHandle.remove<AccountSort>(KEY_ACCOUNTS_SORT)
navBackStackEntry.lifecycle.addObserver(observer)
// As addObserver() does not automatically remove the observer, we
// call removeObserver() manually when the view lifecycle is destroyed
viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver _, event ->
if (event == Lifecycle.Event.ON_DESTROY)
navBackStackEntry.lifecycle.removeObserver(observer)
)
private fun observeSnackBarEvent()
viewModel.showSnackBarEvent.observe(viewLifecycleOwner, Observer
if (it != null)
snackBarAction(it)
)
private fun observeAccounts()
with(viewModel)
accounts.observe(viewLifecycleOwner, Observer
if (it != null)
//set loading flag to hide loading animation
doneLoading()
//Alternate visibility for account list and empty view
alternateAccountListVisibility(it.size)
//Submit the cards
initiateAccounts().submitList(it)
)
private fun initiateAccounts(): AccountAdapter
val adapter = AccountAdapter(
AccountClickListener
navigateToSelectedAccount(it)
,
AccountOptionsClickListener view, card ->
view.apply
isEnabled = false
createPopupMenu(view, card)
)
binding.RecyclerViewAccount.apply
this.adapter = adapter
setHasFixedSize(true)
return adapter
private fun createPopupMenu(view: View, account: Account)
requireContext().createPopUpMenu(
view,
R.menu.menu_moreoptions_account,
PopupMenu.OnMenuItemClickListener
when (it.itemId)
R.id.Menu_CopyUsername -> copyToClipboardAndToast(account.username)
R.id.Menu_CopyPass -> copyToClipboardAndToast(account.password)
R.id.Menu_ShowPass -> triggerSnackBarEvent(account.password)
else -> false
, PopupMenu.OnDismissListener
view.apply
isEnabled = true
)
private fun navigateToSort()
if (SystemClock.elapsedRealtime() - _lastClickTime >= 800)
_lastClickTime = SystemClock.elapsedRealtime()
navigateTo(AccountFragmentDirections.actionFragmentAccountToBottomSheetFragmentAccountFilter())
private fun navigateToSelectedAccount(account: Account)
navigateTo(
AccountFragmentDirections.actionFragmentAccountToFragmentViewAccount(
account
)
)
private fun snackBarAction(message: String)
binding.LayoutFragmentAccount.snackbar(message)
action(getString(R.string.button_snack_action_close)) dismiss()
viewModel.doneShowingSnackBar()
private fun triggerSnackBarEvent(message: String): Boolean
viewModel.setSnackBarMessage(message)
return true
private fun copyToClipboardAndToast(message: String): Boolean
copyToClipboard(message)
toast(getString(R.string.message_copy_successful))
return true
片段 B.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="Account"
type="....Account" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/Layout_Credential_View"
android:layout_
android:layout_>
<ImageView
android:id="@+id/Account_Logo"
imageUrl="@Account.logoUrl"
loadingResource="@@drawable/ic_image_loading"
errorResource="@@drawable/ic_account_placeholder"
android:layout_
android:layout_
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_account_placeholder" />
<TextView
android:id="@+id/Account_Name"
style="@style/Locky.Text.Title5.Name"
android:layout_
android:layout_
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:textAlignment="center"
android:text="@Account.accountName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/Account_Logo"
tools:text="This can be a very very very long title toooooo" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/RecyclerView_Credentials_Field"
android:layout_
android:layout_
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="8dp"
android:nestedScrollingEnabled="true"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/Account_Name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
片段 B.kt
class ViewAccountFragment : Fragment()
private var _binding: FragmentViewAccountBinding? = null
private var _viewModel: ViewAccountViewModel? = null
private lateinit var _account: Account
private val binding get() = _binding!!
private val viewModel get() = _viewModel!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
//Fetch the layout and do the binding
_binding = FragmentViewAccountBinding.inflate(inflater, container, false)
//Instantiate view model
_viewModel = ViewModelProvider(this).get(ViewAccountViewModel::class.java)
binding.lifecycleOwner = this
//Fetch the account clicked on the previous screen
_account = ViewAccountFragmentArgs.fromBundle(requireArguments()).parcelcredaccount
with(_account)
//Bind the account to the layout for displaying
binding.account = this
//Submit the account details to the recyclerview
initiateCredentialsFieldList().submitList(viewModel.fieldList(this))
return binding.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
/* Hides the soft keyboard */
hideSoftKeyboard(binding.root)
override fun onDestroyView()
super.onDestroyView()
_binding = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater)
inflater.inflate(R.menu.menu_credentials_actions, menu)
super.onCreateOptionsMenu(menu, inflater)
override fun onOptionsItemSelected(item: MenuItem): Boolean
return when (item.itemId)
R.id.Action_Duplicate ->
/*
* We set the account id to empty here
* When the add screen receives it, it wil perceive it as a new account that needs to be
* added to the database
*/
navigateToEditScreen(_account.copy(accountID = generateUniqueID()))
true
R.id.Action_Edit ->
navigateToEditScreen(_account)
true
R.id.Action_Delete ->
deleteConfirmationDialog(_account.accountName)
true
else -> false
private fun initiateCredentialsFieldList(): CredentialsViewAdapter
val credentialsAdapter =
CredentialsViewAdapter(
CopyClickListener data ->
copyToClipboardAndToast(data)
,
ViewClickListener data ->
snackBarAction(data)
)
binding.RecyclerViewCredentialsField.apply
adapter = credentialsAdapter
setHasFixedSize(true)
return credentialsAdapter
private fun deleteAndNavigateBackToAccountList()
with(_account)
viewModel.delete(accountID)
toast(getString(R.string.message_credentials_deleted, accountName))
findNavController().popBackStack()
导航.xml
<fragment
android:id="@+id/Fragment_Account"
android:name="....AccountFragment"
android:label="Accounts"
tools:layout="@layout/fragment_account">
<action
android:id="@+id/action_Fragment_Account_to_Fragment_View_Account"
app:destination="@id/Fragment_View_Account" />
<action
android:id="@+id/action_Fragment_Account_to_BottomSheet_Fragment_Account_Filter"
app:destination="@id/BottomSheet_Fragment_Account_Filter" />
</fragment>
MainActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/Drawer_Main"
android:layout_
android:layout_
tools:context=".ui.main.main.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/Layout_Coordinator_Main"
android:layout_
android:layout_
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/Toolbar_Main"
android:layout_
android:layout_
android:background="@color/colorOnSurface"
android:outlineAmbientShadowColor="@color/colorShadowColor"
android:outlineSpotShadowColor="@color/colorShadowColor">
<TextView
android:id="@+id/Toolbar_Main_Title"
style="@style/Locky.Text.Title6.Toolbar"
android:layout_
android:layout_
android:layout_gravity="center"
android:text="@string/app_name" />
</com.google.android.material.appbar.MaterialToolbar>
<androidx.core.widget.NestedScrollView
android:id="@+id/Nested_Scroll"
android:layout_
android:layout_
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true">
<fragment
android:id="@+id/Navigation_Host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_
android:layout_
app:defaultNavHost="true"
app:navGraph="@navigation/navigation_drawer_main" />
</androidx.core.widget.NestedScrollView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/FAB_Search"
style="@style/Locky.FloatingActionButton.Mini"
android:layout_
android:layout_
android:layout_marginTop="0dp"
android:layout_marginBottom="85dp"
app:layout_anchor="@id/FAB_Add"
app:layout_anchorGravity="top|center_horizontal"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:srcCompat="@drawable/ic_search" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/FAB_Add"
style="@style/Locky.FloatingActionButton.Normal"
android:layout_
android:layout_
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:srcCompat="@drawable/ic_add" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/Navigation_View"
style="@style/Locky.Widget.Custom.NavigationView"
android:layout_
android:layout_
android:layout_gravity="start"
android:clipToPadding="false"
android:paddingStart="0dp"
android:paddingEnd="16dp"
app:headerLayout="@layout/drawer_header"
app:itemTextAppearance="@style/Locky.Text.Body.Drawer"
app:menu="@menu/menu_drawer_main" />
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.kt
class MainActivity : AppCompatActivity()
private lateinit var _binding: ActivityMainBinding
private lateinit var _viewModel: MainActivityViewModel
private lateinit var _appBarConfiguration: AppBarConfiguration
//Fragments that can navigate with the drawer
private val _navigationFragments = setOf(
R.id.Fragment_Card,
R.id.Fragment_Account,
R.id.Fragment_Device
)
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
_binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
_viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
_binding.lifecycleOwner = this
//Set the support action bar to the toolbar
setSupportActionBar(_binding.ToolbarMain)
//Remove the default actionbar title
supportActionBar?.setDisplayShowTitleEnabled(false)
/* Updates the app settings*/
updateAppSettings()
//Setup the navigation components
navigationUISetup()
//Load FABs
listenerForAddFab()
listenerForSearchFab()
//Scroll changes to adjust toolbar elevation accordingly
setUpNestedScrollChangeListener()
override fun onOptionsItemSelected(item: MenuItem) =
item.onNavDestinationSelected(findNavController(R.id.Navigation_Host)) || super.onOptionsItemSelected(
item
)
override fun onSupportNavigateUp() =
findNavController(R.id.Navigation_Host).navigateUp(_appBarConfiguration)
override fun finish()
super.finish()
ActivityNavigator.applyPopAnimationsToPendingTransition(this)
private fun navigationUISetup()
//Fetch the Nav Controller
val navController = findNavController(R.id.Navigation_Host)
//Setup the App Bar Configuration
_appBarConfiguration = AppBarConfiguration(_navigationFragments, _binding.DrawerMain)
//Use Navigation UI to setup the app bar config and navigation view
NavigationUI.setupActionBarWithNavController(this, navController, _appBarConfiguration)
NavigationUI.setupWithNavController(_binding.NavigationView, navController)
//Set the mini FABs with navigation to navigate to fragments accordingly.
Navigation.setViewNavController(_binding.FABAdd, navController)
Navigation.setViewNavController(_binding.FABSearch, navController)
//Add on change destination listener to navigation controller to handle fab visibility
navigationDestinationChangeListener_FAB(navController)
//Add on change destination listener to navigation controller to handle screen title visibility
navigationDestinationChangeListener_ToolbarTitle(navController)
private fun setUpNestedScrollChangeListener() =
_binding.NestedScroll.setOnScrollChangeListener _, _, scrollY, _, _ ->
if (scrollY > 0)
_binding.ToolbarMain.elevation = 12F
else
_binding.ToolbarMain.elevation = 0F
private fun navigationDestinationChangeListener_ToolbarTitle(navController: NavController)
navController.addOnDestinationChangedListener _, nd, _ ->
when (nd.id)
R.id.Fragment_Account -> updateToolbar(getString(R.string.text_title_screen_accounts))
R.id.Fragment_Card -> updateToolbar(getString(R.string.text_title_screen_cards))
R.id.Fragment_Device -> updateToolbar(getString(R.string.text_title_screen_devices))
R.id.Fragment_Settings -> updateToolbar(getString(R.string.text_title_screen_settings))
R.id.Fragment_Profile -> updateToolbar(getString(R.string.text_title_screen_profile))
R.id.Fragment_About -> updateToolbar(getString(R.string.text_title_screen_about))
R.id.Fragment_Donate -> updateToolbar(getString(R.string.text_title_screen_donate))
else ->
//Show the toolbar
updateToolbar(null)
private fun navigationDestinationChangeListener_FAB(navController: NavController)
navController.addOnDestinationChangedListener nc, nd, _ ->
when (nd.id)
nc.graph.startDestination,
R.id.Fragment_Card,
R.id.Fragment_Device ->
_binding.DrawerMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
//Show all the FABs
showFABs()
else ->
_binding.DrawerMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
//Hide all the FABs
hideFABs()
private fun getFadeNavOptions(): NavOptions?
return NavOptions.Builder()
.setEnterAnim(R.anim.anim_fade_in)
.setExitAnim(R.anim.anim_fade_out)
.build()
private fun hideFABs()
_binding.FABSearch.hide()
_binding.FABAdd.hide()
private fun showFABs()
_binding.FABSearch.show()
_binding.FABAdd.show()
showFABFromSlidingBehavior(_binding.FABSearch, _binding.FABSearch.isVisible)
showFABFromSlidingBehavior(_binding.FABAdd, _binding.FABAdd.isVisible)
private fun showFABFromSlidingBehavior(fab: FloatingActionButton, isVisible: Boolean)
val layoutParams: ViewGroup.LayoutParams = fab.layoutParams
if (layoutParams is CoordinatorLayout.LayoutParams)
val behavior = layoutParams.behavior
if (behavior is HideBottomViewOnScrollBehavior)
if (isVisible)
behavior.slideUp(fab)
else
behavior.slideDown(fab)
我附上了一个 gif 来演示这里的问题:
在 GIF 中,我从 3 个片段导航(片段 A > 片段 B > 片段 C) 我在这里做错了什么吗?
【问题讨论】:
这是因为即使您转到另一个片段,您的片段仍然存在。您可能需要将片段从导航堆栈中弹出。或者添加一个 backstack 更改监听器并在返回片段时滚动到 0。 实际上不是什么时候回来的。假设我有两个片段 A 和 B,当我的应用程序启动时,它从 A 开始。假设我开始在 A 上滚动,然后导航到 B。我的应用程序保留 B 上的滚动位置,这不是我想要的。我希望片段 B 从顶部开始。 有人可以帮我解决这个问题吗?我被困住了…… 滚动位置不会保留在另一个片段上。也许您的代码中有一些泄漏。正确检查您的代码。 嗯,这很方便。我有完全相反的问题。稍后我将查看您的代码并将其与我的代码进行比较。希望我能为我们俩找到原因! 【参考方案1】:当您填充不同的片段时,两个片段的layoutmanager
相同;调用相同的layoutmanager
。然后它会尝试恢复相同的位置,并考虑其相同的回收视图,当您考虑它时,这是一种功能。
来自文档:
当
RecyclerView
准备好恢复状态时调用 以前的RecyclerView
。请注意,这可能发生在实际 布局,基于适配器更喜欢如何恢复状态。看RecyclerView.Adapter.getStateRestorationPolicy()
这意味着我们需要的不是恢复可以通过传递完成的状态
PREVENT
到 RecyclerView.Adapter.StateRestorationPolicy
解决方案 1:在您的片段 B 适配器中只需调用 adapter.stateRestorationPolicy = PREVENT
解决方案 2:为片段 B 创建一个不同的layoutmanager
,以防您想为其他东西恢复位置。
编辑 :: QA :: 我如何将视图设置在顶部(靠近状态栏):
好吧,既然您要在 NestedScrollView
中填充片段,当您导航到所需片段时,您应该调用 NestedScrollView.scrollTo(0, 0);
,可能是通过在您的 MainActivity.kt
中等待来自 addOnDestinationChangedListener
的 callback
【讨论】:
我试过了,它从recyclerview的顶部开始,而不是在屏幕的顶部。我在recyclerview的顶部有其他的看法。正常它应该加载在屏幕顶部.... 这发生在应用程序中的所有片段中,而不仅仅是有回收站视图的地方。 你不应该使用单例 LayoutManager 上面是什么意思?你的意思是靠近状态栏?还是在其他片段之上(覆盖)? 好吧,实际上有很多解决方案,您可以DispatchNestedPreScroll
和/或DispatchNestedScroll
,如果您想在调用该特定片段时传递一个新的SavedInstanceBundle
,或者您可以添加NestedScrollView
到片段而不是 MainActivity.kt
类。为什么要禁用嵌套滚动视图的滚动?以上是关于Android Jetpack 导航禁用滚动位置的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Jetpack Compose 的 LazyColumn/LazyRow 中禁用和启用滚动?
android jetpack 导航仪器测试在返回导航上失败
使用 Jetpack 的 Android 导航组件销毁/重新创建的片段
是否可以使用 Android 导航架构组件(Android Jetpack)有条件地设置 startDestination?