RecyclerView List Item Does Not Show When Entries Are Made To The Room Database
I am trying to rewrite an existing app in Kotlin for the purpose of learning and getting used to the language. The app allows the user to enter and modify entries and each entry is hosted in a RecyclerView
list with a custom list item. Although entries are successfully added to the database (confirming that with Toast
messages), there isn't a list item present for the said entry.
This is my adapter:
class EntryAdapter : androidx.recyclerview.widget.ListAdapter<Entry, EntryAdapter.ViewHolder>(DIFF_CALLBACK){
private var listener: OnItemLongClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val currentEntry = getItem(position)
holder.hint.text = currentEntry.hint
holder.username.text = currentEntry.username
holder.password.text = currentEntry.password
}
fun getEntryAt(position: Int): Entry{return getItem(position)}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val username: TextView = itemView.findViewById(R.id.username_display)
val password: TextView = itemView.findViewById(R.id.password_display)
val hint: TextView = itemView.findViewById(R.id.hint_display)
init {
itemView.setOnLongClickListener{
val position = adapterPosition
if (listener != null && position != RecyclerView.NO_POSITION){listener!!.onItemLongClick(getItem(position))}
true
}
}
}
interface OnItemLongClickListener{fun onItemLongClick(entry: Entry)}
fun setOnItemLongClickListener(listener: OnItemLongClickListener){this.listener = listener}
companion object{
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Entry>(){
override fun areItemsTheSame(oldItem: Entry, newItem: Entry): Boolean{return oldItem.id == newItem.id}
override fun areContentsTheSame(oldItem: Entry, newItem: Entry): Boolean{return oldItem.username == newItem.username && oldItem.hint == newItem.hint && oldItem.password == newItem.password}
}
}
}
This is my AddEditEntry.kt
activity. The way it works is that when the user wishes to make an entry, he/she clicks on the FAB button which invokes this activity. This activity is where the user enters the entry and adds it to the database (and by extension, the RecyclerView
) by clicking the saveEntry
button:
class AddEditEntryActivity : AppCompatActivity() {
private var usernameEditText: EditText? = null
private var passwordEditText: EditText? = null
private var hintEditText: EditText? = null
private var passwordABCD: CheckBox? = null
private var passwordabcd: CheckBox? = null
private var password0123: CheckBox? = null
private var passwordSymbols: CheckBox? = null
private var radio4: RadioButton? = null
private var radio8: RadioButton? = null
private var radio12: RadioButton? = null
private var radio16: RadioButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_addedit_entry)
usernameEditText = findViewById(R.id.username_field)
passwordEditText = findViewById(R.id.password_field)
hintEditText = findViewById(R.id.hint_field)
passwordABCD = findViewById(R.id.upp_checkbox)
passwordabcd = findViewById(R.id.low_checkbox)
password0123 = findViewById(R.id.num_checkbox)
passwordSymbols = findViewById(R.id.sym_checkbox)
radio4 = findViewById(R.id.four)
radio8 = findViewById(R.id.eight)
radio12 = findViewById(R.id.twelve)
radio16 = findViewById(R.id.sixteen)
val generatePassword = findViewById<Button>(R.id.btn_password_generate)
val saveEntry = findViewById<Button>(R.id.btn_save)
val intent = intent
if (intent.hasExtra(EXTRA_ID)) {
title = getString(R.string.edit_entry)
saveEntry.setText(R.string.update_entry)
usernameEditText!!.setText(getIntent().getStringExtra(EXTRA_USERNAME))
passwordEditText!!.setText(getIntent().getStringExtra(EXTRA_PASSWORD))
hintEditText!!.setText(getIntent().getStringExtra(EXTRA_HINT))
}
else {title = "Add Entry"}
Objects.requireNonNull<ActionBar>(supportActionBar).setHomeAsUpIndicator(R.drawable.ic_close_white_24dp)
generatePassword.setOnClickListener { passwordEditText!!.setText(generatedPassword()) }
saveEntry.setOnClickListener {
val data = Intent()
data.putExtra(EXTRA_USERNAME, usernameEditText!!.text.toString())
data.putExtra(EXTRA_HINT, hintEditText!!.text.toString())
data.putExtra(EXTRA_PASSWORD, passwordEditText!!.text.toString())
val id = getIntent().getIntExtra(EXTRA_ID, -1)
if (id != -1) {data.putExtra(EXTRA_ID, id)}
setResult(Activity.RESULT_OK, data)
finish()
Toast.makeText(this, "data.putExtra() from AddEditEntryActivity", Toast.LENGTH_SHORT).show()
Toast.makeText(this, usernameEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
Toast.makeText(this, hintEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
Toast.makeText(this, passwordEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
}
}
private fun generatedPassword(): String? {
var length = 0
val generatedString = StringBuilder()
val rand = Random()
val capitalLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
val lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"
val numbers = "0123456789"
val characters = "[email protected]#$%^&*()"
if (radio4!!.isChecked) {length = 4}
else if (radio8!!.isChecked) {length = 8}
else if (radio12!!.isChecked) {length = 12}
else if (radio16!!.isChecked) {length = 16}
var totalCharacters = ""
if (passwordABCD!!.isChecked) {totalCharacters += capitalLetters}
if (passwordabcd!!.isChecked) {totalCharacters += lowercaseLetters}
if (password0123!!.isChecked) {totalCharacters += numbers}
if (passwordSymbols!!.isChecked) {totalCharacters += characters}
if (!totalCharacters.trim { it <= ' ' }.isEmpty() && length > 0) {
for (i in 0 until length) {generatedString.append(totalCharacters[rand.nextInt(totalCharacters.length)])}
return generatedString.toString()
}
else {Toast.makeText(this, "Not a valid password!", Toast.LENGTH_SHORT).show()}
return null
}
companion object {
val EXTRA_USERNAME = "com.ozbek.cryptpass.EXTRA_USERNAME"
val EXTRA_HINT = "com.ozbek.cryptpass.EXTRA_HINT"
val EXTRA_PASSWORD = "com.ozbek.cryptpass.EXTRA_PASSWORD"
val EXTRA_ID = "com.ozbek.cryptpass.EXTRA_ID"
}
}
And this is the MainActivity.kt
file where the user enters new entries. The first if
block in onActivityResult()
is the code that retrieves the entries from AddEditEntry.kt
file and adds it to the entity class:
class MainActivity : AppCompatActivity(), LifecycleOwner {
private lateinit var recyclerView: RecyclerView
internal lateinit var adapter: EntryAdapter
private lateinit var floatingActionButton: FloatingActionButton
private lateinit var layoutManager: RecyclerView.LayoutManager
private lateinit var addEditEntryActivity: AddEditEntryActivity
internal lateinit var entry: Entry
private lateinit var viewModel: EntryViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val ctx = this.applicationContext
val sentryDsn = "https://[email protected]/1454826:port/1?options"
Sentry.init(sentryDsn, AndroidSentryClientFactory(ctx))
Sentry.init(AndroidSentryClientFactory(ctx))
viewModel = ViewModelProviders.of(this).get(EntryViewModel::class.java)
viewModel.allEntries.observe(this, Observer { entries -> adapter.submitList(entries) })
adapter = EntryAdapter()
layoutManager = LinearLayoutManager(this)
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
layoutManager = layoutManager
adapter = adapter
}
addEditEntryActivity = AddEditEntryActivity()
entry = Entry()
floatingActionButton = findViewById<FloatingActionButton>(R.id.generate_fab)
floatingActionButton.setOnClickListener {
val intent = Intent([email protected], AddEditEntryActivity::class.java)
Toast.makeText(this, "AddEditActivity started", Toast.LENGTH_SHORT).show()
startActivityForResult(intent, ADD_ENTRY_REQUEST)
}
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {return false}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {viewModel.delete(adapter.getEntryAt(viewHolder.adapterPosition))}
}).attachToRecyclerView(recyclerView)
adapter.setOnItemLongClickListener (object: EntryAdapter.OnItemLongClickListener {
override fun onItemLongClick(entry: Entry){
val intent = Intent([email protected], AddEditEntryActivity::class.java)
intent.putExtra(AddEditEntryActivity.EXTRA_ID, entry.id)
intent.putExtra(AddEditEntryActivity.EXTRA_USERNAME, entry.username)
intent.putExtra(AddEditEntryActivity.EXTRA_HINT, entry.hint)
intent.putExtra(AddEditEntryActivity.EXTRA_PASSWORD, entry.password)
startActivityForResult(intent, EDIT_ENTRY_REQUEST)
}
})
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val itemId = item.itemId
if (itemId == R.id.delete_all) {viewModel.deleteAll()}
return super.onOptionsItemSelected(item)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ADD_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_USERNAME), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_HINT), Toast.LENGTH_SHORT).show()
val username = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_USERNAME)
val password = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD)
val hint = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_HINT)
val entry = Entry(username, hint, password)
viewModel.insert(entry)
Toast.makeText(this, "Entry added!", Toast.LENGTH_SHORT).show()
} else if (requestCode == EDIT_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, data!!.getIntExtra(AddEditEntryActivity.EXTRA_ID, -1), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_USERNAME), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_HINT), Toast.LENGTH_SHORT).show()
val id = Objects.requireNonNull<Intent>(data).getIntExtra(AddEditEntryActivity.EXTRA_ID, -1)
if (id == -1) {
Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show()
return
}
val username = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_USERNAME)
val password = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD)
val hint = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_HINT)
val entry = Entry(username, hint, password, id)
entry.id = id
viewModel.update(entry)
Toast.makeText(this, "Entry updated", Toast.LENGTH_SHORT).show()
} else {Toast.makeText(this, "Entry not added!", Toast.LENGTH_SHORT).show()}
}
companion object {
const val ADD_ENTRY_REQUEST = 1
const val EDIT_ENTRY_REQUEST = 2
}
}
I can add more code per request. This is the full Github repo.
Answer
Here is the problem:
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
layoutManager = layoutManager
adapter = adapter
}
Idk what's going on but it seems like you're initializing them to themselves. Don't do that. Its good practice to keep your variable names distinct. Refactor the objects like this:
internal lateinit var entryAdapter: EntryAdapter
private lateinit var linearLayoutManager: RecyclerView.LayoutManager
And make the following changes in your onCreate()
:
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
adapter = entryAdapter
layoutManager = linearLayoutManager
}
Related Questions
- → should I choose reactjs+f7 or f7+vue.js?
- → Phonegap Android write to sd card
- → Local reference jquery script in nanohttpd (Android)
- → Click to navigate on mobile devices
- → How to allow api access to android or ios app only(laravel)?
- → Access the Camera and CameraRoll on Android using React Native?
- → React native change listening port
- → What is the default unit of style in React Native?
- → Google play market autocomplete icon
- → Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `ListView`
- → Using Laravel with Genymotion
- → react native using like web-based ajax function
- → react native pdf View