Android Kotlin:旋转设备后保持RecyclerView中收藏图标可见
解决方案
一、收藏信息的存储方式
因为要求同一时间仅能设置一个收藏项,直接用SharedPreferences存储当前收藏图片的唯一URL地址即可(空值代表无收藏),无需额外存储状态,既简洁又能通过地址直接匹配RecyclerView中的对应项。
二、关键修改步骤
1. 重构Adapter,让它能感知并同步收藏状态
给Adapter添加收藏地址变量,并在绑定视图时自动判断是否显示收藏图标:
// 修改后的Adapter.kt package com.example.bottomnavigationapp.fragimages.ui import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.recyclerview.widget.RecyclerView import com.example.bottomnavigationapp.R import com.squareup.picasso.Picasso class GridItemAdapter( val cardImages: Array<String>, private val listener: OnItemClickListener, // 添加收藏地址变量,初始为空 var favoriteAddress: String? = null ) : RecyclerView.Adapter<GridItemAdapter.ViewHolder>() { inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener{ val cardImage : ImageView = itemView.findViewById(R.id.cardImage) val favImage : ImageView = itemView.findViewById(R.id.favImage) init { cardImage.setOnClickListener(this) } override fun onClick(v: View?) { val position = adapterPosition val address = cardImages[position] if (position != RecyclerView.NO_POSITION) { listener.onItemClick(position, favImage, address) } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.grid_item_view, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val currentAddress = cardImages[position] Picasso.get().load(currentAddress).into(holder.cardImage) // 绑定视图时自动判断是否显示收藏图标 holder.favImage.visibility = if (currentAddress == favoriteAddress) View.VISIBLE else View.GONE } override fun getItemCount(): Int = cardImages.size // 提供更新收藏地址的方法,仅刷新变化的项 fun updateFavoriteAddress(newAddress: String?) { val oldPosition = favoriteAddress?.let { cardImages.indexOf(it) } ?: -1 favoriteAddress = newAddress val newPosition = newAddress?.let { cardImages.indexOf(it) } ?: -1 if (oldPosition != -1) notifyItemChanged(oldPosition) if (newPosition != -1) notifyItemChanged(newPosition) } interface OnItemClickListener { fun onItemClick(position: Int, favImage: ImageView, address: String) } }
2. 修改Fragment,完成SharedPreferences读写与状态同步
在Fragment初始化时读取已保存的收藏地址,点击事件中更新SharedPreferences并通知Adapter刷新:
// 修改后的Fragment.kt package com.example.bottomnavigationapp.fragimages.ui import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.recyclerview.widget.GridLayoutManager import com.example.bottomnavigationapp.R import com.example.bottomnavigationapp.databinding.FragmentImagesBinding class ImagesFragment : Fragment(), GridItemAdapter.OnItemClickListener { private lateinit var binding: FragmentImagesBinding private lateinit var adapter: GridItemAdapter // 懒加载SharedPreferences实例 private val sharedPrefs by lazy { requireActivity().applicationContext.getSharedPreferences("myPref", 0) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentImagesBinding.inflate(inflater, container, false) val cardImages : Array<String> = resources.getStringArray(R.array.cardImages) // 读取已保存的收藏地址 val savedFavAddress = sharedPrefs.getString("favAddress", null) adapter = GridItemAdapter(cardImages, this, savedFavAddress) binding.gridItems.layoutManager = GridLayoutManager(context, 2) binding.gridItems.adapter = adapter return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) (activity as AppCompatActivity?)!!.supportActionBar!!.title = "Images Fragment" } override fun onItemClick(position: Int, favImage: ImageView, address: String) { val currentFavAddress = adapter.favoriteAddress when { // 无收藏时设置新收藏 currentFavAddress == null -> { sharedPrefs.edit().putString("favAddress", address).apply() adapter.updateFavoriteAddress(address) Toast.makeText(context, "Item $position 设为收藏", Toast.LENGTH_SHORT).show() } // 点击已收藏项,取消收藏 currentFavAddress == address -> { sharedPrefs.edit().remove("favAddress").apply() adapter.updateFavoriteAddress(null) Toast.makeText(context, "取消收藏 Item $position", Toast.LENGTH_SHORT).show() } // 已有其他收藏,提示用户 else -> { val oldPosition = adapter.cardImages.indexOf(currentFavAddress) Toast.makeText(context, "已在位置 $oldPosition 设置收藏", Toast.LENGTH_SHORT).show() } } } }
三、核心逻辑说明
- 存储逻辑:用SharedPreferences的
favAddress键保存收藏图片的URL,取消收藏时移除该键即可。 - 图标显示时机:在Adapter的
onBindViewHolder方法中判断当前项地址是否与收藏地址匹配,自动设置图标可见性——这是RecyclerView绑定视图的核心入口,设备旋转后Fragment重建,Adapter会重新绑定所有视图,此时会自动读取SharedPreferences中的收藏状态,保证图标不会消失。 - 状态同步:点击收藏/取消收藏时,先更新SharedPreferences,再调用Adapter的
updateFavoriteAddress方法刷新对应项的视图,避免全局刷新带来的性能损耗。
内容的提问来源于stack exchange,提问作者Dario Santos
相关产品推荐
相关产品推荐

