Search code examples
androidandroid-recyclerviewsharedpreferencesandroid-listfragment

How to access Shared Preferences for each item in Recycler View in Fragment


I want to store Shared Preferences of a Button in a Recycler View Adapter that is used by Fragment.

class RecyclerViewAdapter : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {

private val name = arrayOf("Android Dev W1",
    "Android Dev W2", "Web Dev W1", "Learn Kotlin B1",
    "Android Dev W3", "Android Dev W4", "Web Dev W2",
    "Learn DSA")

private val date = arrayOf("20:00 18th May, 2021", "04:00 18th May, 2021",
    "07:00 19th May, 2021", "04:00 20th May, 2021",
    "01:00 21th May, 2021", "10:00 21th May, 2021",
    "20:00 27th May, 2021", "10:00 30th May, 2021")

private val btn = arrayOf(false, false, false, false, false, false, false, false)

inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    var itemName: TextView = itemView.findViewById(R.id.workshopName)
    var itemDate: TextView = itemView.findViewById(R.id.workshopDate)
    var button: Button = itemView.findViewById(R.id.applyBtn)

    val prefs = itemView.context.getSharedPreferences("prefs", MODE_PRIVATE)

    init {

        for(i in 0 .. 7) {
            //checking via sharedPreferences if this app is being run first time or not
            btn[i] = prefs.getBoolean("registered$i", false)
            if (btn[i]) {
                hideButton()
            } else {
                showButton()
            }
        }

    }

     fun hideButton() {
         button.text = "Applied"
         button.isClickable = false
         button.setBackgroundColor(Color.parseColor("#FF3700E9"))
    }

    private fun showButton() {
        button.text = "Apply"
        button.isClickable = true
        button.setBackgroundColor(Color.parseColor("#FF6200EE"))
    }
}

override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
    val v = LayoutInflater.from(viewGroup.context)
        .inflate(R.layout.frame_textview, viewGroup, false)
    return ViewHolder(v)
}

override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
    viewHolder.itemName.text = name[i]
    viewHolder.itemDate.text = date[i]
    viewHolder.button.setOnClickListener {
        viewHolder.prefs.edit().putBoolean("registered$i", true).apply()
        btn[i] = true
        viewHolder.hideButton()
    }

}

override fun getItemCount(): Int {
        return name.size
    }
}

The fragment that is using this adapter

class AvailableWorkshops : Fragment() {

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_available_workshops, container, false)
}

override fun onViewCreated(itemView: View, savedInstanceState: Bundle?) {
    super.onViewCreated(itemView, savedInstanceState)
    val recyclerView = itemView.findViewById<RecyclerView>(R.id.recycler_view)
    recyclerView.apply {
        // set a LinearLayoutManager to handle Android
        // RecyclerView behavior
        layoutManager = LinearLayoutManager(activity)
        // set the custom adapter to the RecyclerView
        adapter = RecyclerViewAdapter()
        }
    }
}

For example, there are 8 item in this adapter and I click on 5th item it will call onClickListener() and change button property that is completely fine.

But, when we restart the app is changes back to default button properties(Shared Preference is not working)

Again, If I click on every item and restart the app , It will now show new changes on every item(Shared Preference is working here).

I want to store that if I click on 5th Item, then on app restart it should change display of only 5th item, not for every item.


Solution

  • I want to store that if I click on 5th Item, then on app restart it should change display of only 5th item, not for every item.

    Your Problem

    A ViewHolder represents one item in the RecyclerView. When you initialize your ViewHolder you apply the preference 7 times to the same button.

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
      
     //...
    
     init {
            // ** THIS IS NOT HIDING OR SHOWING SEVEN DIFFERENT BUTTONS - THIS IS HIDING OR
            // SHOWING THIS VIEWHOLDER'S ONE BUTTON SEVEN TIMES **
    
            for(i in 0 .. 7) {
                //checking via sharedPreferences if this app is being run first time or not
                btn[i] = prefs.getBoolean("registered$i", false)
                if (btn[i]) {
                    hideButton()
                } else {
                    showButton()
                }
            }
    
        }
    

    A Solution

    Delete your init block in the ViewHolder and move your hide / show logic to the onBindViewHolder method where you set up each single instance.

    override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
        viewHolder.itemName.text = name[i]
        viewHolder.itemDate.text = date[i]
    
        // ** SHOW OR HIDE THIS INSTANCE'S BUTTON - AND ONLY THIS INSTANCE'S BUTTON **
        if (viewHolder.prefs.getBoolean("registered$i", false)) {
            viewHolder.hideButton()
        } else {
            viewHolder.showButton()
        }
    
        viewHolder.button.setOnClickListener {
            viewHolder.prefs.edit().putBoolean("registered$i", true).apply()
            btn[i] = true
            viewHolder.hideButton()
        }
    }