Only a few months into Kotlin development, so apologies. Been over all the previous threads and nothing has helped with this issue.
Whenever a character is selected and the Firebase DB is updated, the RecyclerView is reset to the top. I want it to retain its position. I don't have a solid understanding of how the parts work yet to know what is causing it to reset.
Activity:
class DailiesActivity : AppCompatActivity(), CharacterAdapter.CharacterItemListener {
private lateinit var binding : ActivityDailiesBinding
private lateinit var auth : FirebaseAuth
var db = FirebaseFirestore.getInstance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDailiesBinding.inflate(layoutInflater)
setContentView(binding.root)
auth = Firebase.auth
val recyclerView = findViewById<RecyclerView>(R.id.charactersRecyclerView)
val viewModel : CharacterViewModel by viewModels()
viewModel.getCharacters().observe(this) {
binding.charactersRecyclerView.adapter = CharacterAdapter(this, it, this, recyclerView)
}
setSupportActionBar(binding.mainToolBar.toolbar)
supportActionBar?.title = ""
}
/**
* Nav Setup
*/
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_to_home -> { startActivity(Intent(applicationContext, MainActivity::class.java)) }
R.id.action_to_critters -> { startActivity(Intent(applicationContext, CrittersActivity::class.java)) }
R.id.action_to_recipes -> { startActivity(Intent(applicationContext, RecipesActivity::class.java)) }
R.id.action_to_settings -> { startActivity(Intent(applicationContext, SettingsActivity::class.java)) }
}
return super.onOptionsItemSelected(item)
}
// updating firebase data when character is selected
// Firebase documentation: https://firebase.google.com/docs/firestore/manage-data/add-data
override fun characterSelected(character: Character, adapterPosition: Int) {
Log.i("Character_Selected", "$character")
val uID = auth.currentUser?.uid
val currentCharacter = uID?.let { db.collection(it).document(character.name + "") }
if (!character.spoken) {
currentCharacter?.update("spoken", true)
}
else {
currentCharacter?.update("spoken", false)
}
binding.charactersRecyclerView.adapter?.notifyItemChanged(adapterPosition)
}
Adapter:
class CharacterAdapter(val context : Context,
private val characters : List<Character>,
private val itemListener : CharacterItemListener,
private val recyclerView: RecyclerView ) : RecyclerView.Adapter<CharacterAdapter.CharacterViewHolder>() {
inner class CharacterViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView){
var characterImageView = itemView.findViewById<ImageView>(R.id.characterImageView)
val characterTextView = itemView.findViewById<TextView>(R.id.characterTextView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CharacterViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.item_character, parent, false)
return CharacterViewHolder(view)
}
override fun onBindViewHolder(viewHolder: CharacterViewHolder, position: Int) {
val character = characters[position]
with (viewHolder) {
// when expression documentation: https://kotlinlang.org/docs/control-flow.html#when-expression
// handling dynamic ImageViews: https://stackoverflow.com/questions/16906528/change-image-of-imageview-programmatically-in-android
// images courtesy of: https://dreamlightvalleywiki.com/Characters
when (character.name) {
"Anna" -> characterImageView.setBackgroundResource(R.drawable.anna)
"Ariel" -> characterImageView.setBackgroundResource(R.drawable.ariel)
"Buzz" -> characterImageView.setBackgroundResource(R.drawable.buzz)
"Donald Duck" -> characterImageView.setBackgroundResource(R.drawable.donald)
"Elsa" -> characterImageView.setBackgroundResource(R.drawable.elsa)
"Eric" -> characterImageView.setBackgroundResource(R.drawable.eric)
"Goofy" -> characterImageView.setBackgroundResource(R.drawable.goofy)
"Kristoff" -> characterImageView.setBackgroundResource(R.drawable.kristoff)
"Maui" -> characterImageView.setBackgroundResource(R.drawable.maui)
"Merlin" -> characterImageView.setBackgroundResource(R.drawable.merlin)
"Mickey Mouse" -> characterImageView.setBackgroundResource(R.drawable.mickey)
"Minnie Mouse" -> characterImageView.setBackgroundResource(R.drawable.minnie)
"Mirabel" -> characterImageView.setBackgroundResource(R.drawable.mirabel)
"Moana" -> characterImageView.setBackgroundResource(R.drawable.moana)
"Mother Gothel" -> characterImageView.setBackgroundResource(R.drawable.gothel)
"Olaf" -> characterImageView.setBackgroundResource(R.drawable.olaf)
"Remy" -> characterImageView.setBackgroundResource(R.drawable.remy)
"Scar" -> characterImageView.setBackgroundResource(R.drawable.scar)
"Scrooge" -> characterImageView.setBackgroundResource(R.drawable.scrooge)
"Stitch" -> characterImageView.setBackgroundResource(R.drawable.stitch)
"Ursula" -> characterImageView.setBackgroundResource(R.drawable.ursula)
"WALL-E" -> characterImageView.setBackgroundResource(R.drawable.walle)
"Woody" -> characterImageView.setBackgroundResource(R.drawable.woody)
else -> {
characterImageView = null
}
}
characterTextView.text = character.name
// retains 'spoken to' background colour if user navigates to different page and returns
if (character.spoken) {
itemView.setBackgroundResource(R.drawable.background_selected)
}
else {
itemView.setBackgroundResource(R.drawable.background)
}
// when pressed, background changes and updates value in list
itemView.setOnClickListener {
itemListener.characterSelected(character, viewHolder.adapterPosition)
character.spoken = !character.spoken
if (character.spoken) {
itemView.setBackgroundResource(R.drawable.background_selected)
} else {
itemView.setBackgroundResource(R.drawable.background)
}
}
}
}
override fun getItemCount(): Int {
return characters.size
}
interface CharacterItemListener {
fun characterSelected(character: Character, adapterPosition: Int)
}
XML:
<androidx.constraintlayout.widget.ConstraintLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="5dp"
android:background="@color/purple_200"
tools:context=".DailiesActivity">
<!-- navigation -->
<include
android:id="@+id/mainToolBar"
layout="@layout/toolbar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/daily_discussions"
android:textSize="25sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.045"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.615" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/charactersRecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="3"
android:paddingTop="10dp"
app:layout_constraintTop_toBottomOf="@id/textView"
app:layout_constraintBottom_toTopOf="@id/mainToolBar"
app:layout_constraintStart_toEndOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
ViewModel:
class CharacterViewModel : ViewModel() {
private var characters = MutableLiveData<List<Character>>()
init {
val userID = Firebase.auth.currentUser?.uid
val db = userID?.let {
FirebaseFirestore.getInstance().collection(it)
.whereEqualTo("type", "character")
.addSnapshotListener{ documents, exception ->
if (exception != null) {
Log.w("DB_Response", "Listen failed ${exception.code}")
return@addSnapshotListener
}
documents?.let {
val charactersList = ArrayList<Character>()
for (document in documents) {
Log.i("DB_Response", "${document.data}")
val character = document.toObject(Character::class.java)
charactersList.add(character)
}
characters.value = charactersList
}
}
}
}
fun getCharacters() : LiveData<List<Character>> {
return characters
}