You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
            }
        }
    }
}

三、核心逻辑说明

  1. 存储逻辑:用SharedPreferences的favAddress键保存收藏图片的URL,取消收藏时移除该键即可。
  2. 图标显示时机:在Adapter的onBindViewHolder方法中判断当前项地址是否与收藏地址匹配,自动设置图标可见性——这是RecyclerView绑定视图的核心入口,设备旋转后Fragment重建,Adapter会重新绑定所有视图,此时会自动读取SharedPreferences中的收藏状态,保证图标不会消失。
  3. 状态同步:点击收藏/取消收藏时,先更新SharedPreferences,再调用Adapter的updateFavoriteAddress方法刷新对应项的视图,避免全局刷新带来的性能损耗。

内容的提问来源于stack exchange,提问作者Dario Santos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 11:15:36