为什么我的列表在返回片段时总是滚动到顶部?
Why does my list always scroll to the top when going back into the fragment?
事情是这样的
我才发现你看不到触摸事件
所以第一个我摸了misc,第二个我摸了意大利面
我不认为这是正确的行为,对吧?
我离开时是否应该保存位置,并在onresume中滚动列表?
这是我的适配器
class CategoryListAdapter(private val onClickListener: OnClickListener) :
ListAdapter<Category, CategoryListAdapter.CategoryViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder =
CategoryViewHolder(
ItemListVerticalBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
val category = getItem(position)
holder.bindCategory(category)
holder.itemView.setOnClickListener {
onClickListener.onClick(category)
}
}
companion object DiffCallback : DiffUtil.ItemCallback<Category>() {
override fun areItemsTheSame(oldItem: Category, newItem: Category): Boolean =
oldItem == newItem
override fun areContentsTheSame(oldItem: Category, newItem: Category): Boolean =
oldItem.id == newItem.id
&& oldItem.name == newItem.name
&& oldItem.lastAccessed == newItem.lastAccessed
&& oldItem.thumbnailUrl == newItem.thumbnailUrl
}
inner class CategoryViewHolder(private var binding: ItemListVerticalBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindCategory(category: Category) {
binding.itemImageView.load(category.thumbnailUrl)
binding.itemTextView.text = category.name
}
}
class OnClickListener(val clickListener: (category: Category) -> Unit) {
fun onClick(category: Category) = clickListener(category)
}
}
这是我在片段中的填充方法
private fun setTheCategories(){
lifecycleScope.launchWhenStarted {
val adapter = CategoryListAdapter(
CategoryListAdapter.OnClickListener{
lifecycleScope.launchWhenStarted {
viewModel.displayCategoryMeals(it)
}
}
)
binding.categoryRecyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.categoryRecyclerView.adapter = adapter
viewModel.categories.collect { resource ->
when (resource.status) {
Resource.Status.LOADING -> {
Log.v("CATEGORY", "LOADING")
}
Resource.Status.SUCCESS -> {
Log.v("CATEGORY", "SUCCESS")
val categories = resource.data!!
adapter.submitList(categories)
}
Resource.Status.ERROR -> {
val categories = resource.data!!
if(categories[0].name.isNotEmpty()){
adapter.submitList(categories)
}
Log.v("CATEGORY", "ERROR")
}
}
}
}
}
这就是我导航到下一个片段的方式
private fun goToCategoryMeals(){
lifecycleScope.launchWhenStarted {
viewModel.category.collect {
if(it != null) {
this@CategoryFragment.findNavController().navigate(
CategoryFragmentDirections.actionCategoryFragmentToCategoryMealsFragment(
categoryName = it.name,
savedDate = it.lastAccessed
)
)
viewModel.navigatedToCategoryMeals()
}
}
}
}
这是我的视图模型的一部分
init {
getAllCategories(sharedPrefRepo.getString(AppConstants.CURR_DATE_CAT))
}
private fun getAllCategories(savedDate: String) {
viewModelScope.launch {
repo
.getCategories(savedDate)
.collect {
_categories.value = it
if (it.status == Resource.Status.SUCCESS && currDate != savedDate) {
sharedPrefRepo.putString(AppConstants.CURR_DATE_CAT, currDate)
}
}
}
}
这是我的回购协议的一部分
override suspend fun getCategories(savedDate: String): Flow<Resource<List<Category>>> =
flow {
emit(Resource.loading())
if (currDate!=savedDate){
try{
val netCategories = networkTransaction.getCategories()
dbTransaction.upsertMiniCategories(netCategories.mapToDbCategories())
}
catch (e: Exception){
if (e is HttpException){
try {
dbTransaction.getAllCategories().collect {
emit(
Resource.error(
"Something is wrong when connecting to network\n" +
"Using the old data inside local storage",
it.mapToCategories()
)
)
}
} catch (e2: IOException) {
emit(Resource.error(e2.localizedMessage!!, listOf(Category())))
}
} else {
emit(Resource.error(e.localizedMessage!!, listOf(Category())))
}
}
}
try {
dbTransaction.getAllCategories().collect {
emit(Resource.success(it.mapToCategories()))
}
}
catch (e: IOException){
emit(Resource.error(e.localizedMessage!!, listOf(Category())))
}
}.flowOn(ioDispatcher)
这是我道的一部分
@Query("select * from DbCategory")
abstract fun getAllCategories(): Flow<List<DbCategory>>
我的数据库接口实现的一部分
override suspend fun getAllCategories(): Flow<List<DbCategory>> =
dbCategoryDao.getAllCategories().distinctUntilChanged()
编辑 1
fragment_category.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/category_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
您可以使用“StateRestorationPolicy”来解决这个问题,recyclerview:1.2.0-alpha02 版本已引入 StateRestorationPolicy。这可能是解决给定问题的更好方法。
另外,@rubén-viguera 在下面的回答中分享了更多细节。
事情是这样的
我才发现你看不到触摸事件
所以第一个我摸了misc,第二个我摸了意大利面
我不认为这是正确的行为,对吧? 我离开时是否应该保存位置,并在onresume中滚动列表?
这是我的适配器
class CategoryListAdapter(private val onClickListener: OnClickListener) :
ListAdapter<Category, CategoryListAdapter.CategoryViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder =
CategoryViewHolder(
ItemListVerticalBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
val category = getItem(position)
holder.bindCategory(category)
holder.itemView.setOnClickListener {
onClickListener.onClick(category)
}
}
companion object DiffCallback : DiffUtil.ItemCallback<Category>() {
override fun areItemsTheSame(oldItem: Category, newItem: Category): Boolean =
oldItem == newItem
override fun areContentsTheSame(oldItem: Category, newItem: Category): Boolean =
oldItem.id == newItem.id
&& oldItem.name == newItem.name
&& oldItem.lastAccessed == newItem.lastAccessed
&& oldItem.thumbnailUrl == newItem.thumbnailUrl
}
inner class CategoryViewHolder(private var binding: ItemListVerticalBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bindCategory(category: Category) {
binding.itemImageView.load(category.thumbnailUrl)
binding.itemTextView.text = category.name
}
}
class OnClickListener(val clickListener: (category: Category) -> Unit) {
fun onClick(category: Category) = clickListener(category)
}
}
这是我在片段中的填充方法
private fun setTheCategories(){
lifecycleScope.launchWhenStarted {
val adapter = CategoryListAdapter(
CategoryListAdapter.OnClickListener{
lifecycleScope.launchWhenStarted {
viewModel.displayCategoryMeals(it)
}
}
)
binding.categoryRecyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.categoryRecyclerView.adapter = adapter
viewModel.categories.collect { resource ->
when (resource.status) {
Resource.Status.LOADING -> {
Log.v("CATEGORY", "LOADING")
}
Resource.Status.SUCCESS -> {
Log.v("CATEGORY", "SUCCESS")
val categories = resource.data!!
adapter.submitList(categories)
}
Resource.Status.ERROR -> {
val categories = resource.data!!
if(categories[0].name.isNotEmpty()){
adapter.submitList(categories)
}
Log.v("CATEGORY", "ERROR")
}
}
}
}
}
这就是我导航到下一个片段的方式
private fun goToCategoryMeals(){
lifecycleScope.launchWhenStarted {
viewModel.category.collect {
if(it != null) {
this@CategoryFragment.findNavController().navigate(
CategoryFragmentDirections.actionCategoryFragmentToCategoryMealsFragment(
categoryName = it.name,
savedDate = it.lastAccessed
)
)
viewModel.navigatedToCategoryMeals()
}
}
}
}
这是我的视图模型的一部分
init {
getAllCategories(sharedPrefRepo.getString(AppConstants.CURR_DATE_CAT))
}
private fun getAllCategories(savedDate: String) {
viewModelScope.launch {
repo
.getCategories(savedDate)
.collect {
_categories.value = it
if (it.status == Resource.Status.SUCCESS && currDate != savedDate) {
sharedPrefRepo.putString(AppConstants.CURR_DATE_CAT, currDate)
}
}
}
}
这是我的回购协议的一部分
override suspend fun getCategories(savedDate: String): Flow<Resource<List<Category>>> =
flow {
emit(Resource.loading())
if (currDate!=savedDate){
try{
val netCategories = networkTransaction.getCategories()
dbTransaction.upsertMiniCategories(netCategories.mapToDbCategories())
}
catch (e: Exception){
if (e is HttpException){
try {
dbTransaction.getAllCategories().collect {
emit(
Resource.error(
"Something is wrong when connecting to network\n" +
"Using the old data inside local storage",
it.mapToCategories()
)
)
}
} catch (e2: IOException) {
emit(Resource.error(e2.localizedMessage!!, listOf(Category())))
}
} else {
emit(Resource.error(e.localizedMessage!!, listOf(Category())))
}
}
}
try {
dbTransaction.getAllCategories().collect {
emit(Resource.success(it.mapToCategories()))
}
}
catch (e: IOException){
emit(Resource.error(e.localizedMessage!!, listOf(Category())))
}
}.flowOn(ioDispatcher)
这是我道的一部分
@Query("select * from DbCategory")
abstract fun getAllCategories(): Flow<List<DbCategory>>
我的数据库接口实现的一部分
override suspend fun getAllCategories(): Flow<List<DbCategory>> =
dbCategoryDao.getAllCategories().distinctUntilChanged()
编辑 1
fragment_category.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/category_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
您可以使用“StateRestorationPolicy”来解决这个问题,recyclerview:1.2.0-alpha02 版本已引入 StateRestorationPolicy。这可能是解决给定问题的更好方法。
另外,@rubén-viguera 在下面的回答中分享了更多细节。