Ad

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.

Ad

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
    }
Ad
source: stackoverflow.com
Ad