0

This is my first time posting. I have made a simple counting app. I can connect and add data to the database, but i just cannot figure out how to view the data in the COLUMN_COUNT field. I have created a retrieveCount function, but cannot figure out how to call it on the HistoryAdapter page without making errors.

I have pasted the 4 files with code below.

    MainActivity.kt

package com.example.thesupremecounter

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import java.text.SimpleDateFormat
import java.util.*


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val addButton = findViewById<LinearLayout>(R.id.addButton)
        val reduceButton = findViewById<LinearLayout>(R.id.reduceButton)
        val resetButton = findViewById<LinearLayout>(R.id.resetButton)
        val myTextView = findViewById<TextView>(R.id.textView)
        val saveButton = findViewById<Button>(R.id.saveButton)
        val historyButton = findViewById<Button>(R.id.historyButton)

        var timeClicked = 0

        addButton.setOnClickListener {
            timeClicked += 1
            myTextView.text = timeClicked.toString()
//        Toast.makeText(this@MainActivity, "You clicked me.", Toast.LENGTH_SHORT).show()
        }
        reduceButton.setOnClickListener {
            timeClicked -= 1
            if (timeClicked < 0) {
                timeClicked = 0
            } else {
                myTextView.text = timeClicked.toString()
//            Toast.makeText(this@MainActivity, "You clicked me.", Toast.LENGTH_SHORT).show()
            }
        }
        resetButton.setOnClickListener {
            timeClicked = 0
            myTextView.text = timeClicked.toString()
//            Toast.makeText(this@MainActivity, "You clicked me.", Toast.LENGTH_SHORT).show()
        }

        historyButton.setOnClickListener {
            val intent = Intent(this, HistoryActivity::class.java)
            startActivity(intent)
        }
        saveButton.setOnClickListener {
            val count = timeClicked.toString()
            val c = Calendar.getInstance() // Calender Current Instance
            val dateTime = c.time // Current Date and Time of the system.
            val sdf = SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.getDefault()) // Date Formatter
            val date = sdf.format(dateTime) // dateTime is formatted in the given format.

            val dbHandler = SqliteOpenHelper(this, null)
            dbHandler.addDate(count, date) // Add date function is called.
            Toast.makeText(this@MainActivity, count + date, Toast.LENGTH_SHORT).show()

        }
    }

}

HistoryActivity.kt

package com.example.thesupremecounter
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager

class HistoryActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_history)

        val toolbarPageTwo = findViewById<androidx.appcompat.widget.Toolbar>(R.id.toolbar_history_activity)

        setSupportActionBar(toolbarPageTwo)

        supportActionBar?.setDisplayHomeAsUpEnabled(true) //set back button
        supportActionBar?.title = "HISTORY" // Setting an title in the action bar.

        toolbarPageTwo.setNavigationOnClickListener {
            onBackPressed()
        }

        getAllCompletedDates()
    }

    private fun getAllCompletedDates() {
        val dbHandler = SqliteOpenHelper(this, null)
        val allCompletedDatesList = dbHandler.getAllCompletedDatesList()
        val tvHistory = findViewById<TextView>(R.id.tvHistory)
        val rvHistory = findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.rvHistory)
        val tvNoDataAvailable = findViewById<TextView>(R.id.tvNoDataAvailable)

        if (allCompletedDatesList.size > 0) {
            tvHistory.visibility = View.VISIBLE
            rvHistory.visibility = View.VISIBLE
            tvNoDataAvailable.visibility = View.GONE

            rvHistory.layoutManager = LinearLayoutManager(this)

            val historyAdapter = HistoryAdapter(this, allCompletedDatesList)

            rvHistory.adapter = historyAdapter
        } else {
            tvHistory.visibility = View.GONE
            rvHistory.visibility = View.GONE
            tvNoDataAvailable.visibility = View.VISIBLE
        }
    }
}

SqliteOpenHelper.kt

package com.example.thesupremecounter

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper

class SqliteOpenHelper(
    context: Context,
    factory: SQLiteDatabase.CursorFactory?
) :
    SQLiteOpenHelper(
        context, DATABASE_NAME,
        factory, DATABASE_VERSION
    ) {

    override fun onCreate(db: SQLiteDatabase) {
        val CREATE_HISTORY_TABLE = ("CREATE TABLE " +  TABLE_HISTORY +
               "(" + COLUMN_ID + " INTEGER PRIMARY KEY,"
               +   COLUMN_COUNT   + " TEXT,"
                + COLUMN_COMPLETED_DATE  + " TEXT" + ")")
        db.execSQL(CREATE_HISTORY_TABLE)
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY) // It drops the existing history table
        onCreate(db) // Calls the onCreate function so all the updated table will be created.
    }

    fun addDate(count: String, date: String ) {
        val values = "INSERT INTO history " +
                "( 'count', 'completed_date')" +
                " VALUES " +
                "( '$count', '$date')"
        val db = this.writableDatabase
        db.execSQL(values)
        db.close()
    }

    fun retrieveCount(): ArrayList<String> {
        val countList = ArrayList<String>() // ArrayList is initialized
        val db = this.readableDatabase
        val cursor = db.rawQuery("SELECT * FROM $TABLE_HISTORY", null)
        while (cursor.moveToNext())
        {countList.add(cursor.getString(cursor.getColumnIndex(COLUMN_COUNT)))
        }
        cursor.close()
        return countList

    }

    fun getAllCompletedDatesList(): ArrayList<String> {
        val list = ArrayList<String>() // ArrayList is initialized
        val db = this.readableDatabase
        val cursor = db.rawQuery("SELECT * FROM $TABLE_HISTORY", null)
        while (cursor.moveToNext())
        {list.add(cursor.getString(cursor.getColumnIndex(COLUMN_COMPLETED_DATE)))
        }
        cursor.close()
        return list
    }

    companion object {
        const val DATABASE_VERSION = 1 // This DATABASE Version
        const val DATABASE_NAME = "TheCountDatabase9.db" // Name of the DATABASE
        const val TABLE_HISTORY = "history" // Table Name
        const val COLUMN_ID = "_id" // Column Id
        const val COLUMN_COUNT = "count" // Count
        const val COLUMN_COMPLETED_DATE = "completed_date" // Column for Completed Date
    }
}

HistoryAdapter.kt

package com.example.thesupremecounter
import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_history_row.view.*


class HistoryAdapter(val context: Context, val items: ArrayList<String>) :
    RecyclerView.Adapter<HistoryAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(context).inflate(
                R.layout.item_history_row,
                parent,
                false
            )
        )
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

        val date: String = items[position]

        holder.tvPosition.text = (position + 1).toString()
        holder.tvCount.text = "unsure what code to write here to display count values from COLUMN_COUNT + also not sure how to call the retrieve count function"
        holder.tvItem.text = date

        if (position % 2 == 0) {
            holder.llHistoryItemMain.setBackgroundColor(
                Color.parseColor("#EBEBEB")
            )
        } else {
            holder.llHistoryItemMain.setBackgroundColor(
                Color.parseColor("#FFFFFF")
            )
        }
    }

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

class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    val llHistoryItemMain = view.ll_history_item_main!!
    val tvItem = view.tvItem!!
    val tvPosition = view.tvPosition!!
    val tvCount = view.tvCount!!
    }
}

Any help will be greatly appreciated.

Thanks

  • You cannot access DB from the main thread. You can see here more info: https://stackoverflow.com/questions/44167111/android-room-simple-select-query-cannot-access-database-on-the-main-thread. Then you have a list called items in your adapter, that's the list you should use to show data in the recycler. – Grela Sep 03 '21 at 07:55

1 Answers1

0

A recyclerview is designed to display the contents of the list that is passed to it (items in your case).

so :-

holder.tvCount.text = "unsure what code to write here to display count values from COLUMN_COUNT + also not sure how to call the retrieve count function"

Should be the count in items, however items is just the date. You need items to be a little more complex i.e. a list of more than just the one String.

So create a class such as HistoryRow with members for each of the components e.g.

data class HistoryRow(
    val id: String,
    val count: String,
    val date: String
)
  • in the working example below I just added this as a sub class of SqliteOpenHelper

The instead create a List in your getAllCompletedDatesList() you create a List and pass this to your Adapter.

Then in the onBindViewHolder method/function you then assign the values from the respective elements.

The following is a working example based upon the above.

First SqliteOpenHelper :-

class SqliteOpenHelper(
    context: Context,
    factory: SQLiteDatabase.CursorFactory?
) :
    SQLiteOpenHelper(
        context, DATABASE_NAME,
        factory, DATABASE_VERSION
    ) {

    override fun onCreate(db: SQLiteDatabase) {
        val CREATE_HISTORY_TABLE = ("CREATE TABLE " +  TABLE_HISTORY +
                "(" + COLUMN_ID + " INTEGER PRIMARY KEY,"
                +   COLUMN_COUNT   + " TEXT,"
                + COLUMN_COMPLETED_DATE  + " TEXT" + ")")
        db.execSQL(CREATE_HISTORY_TABLE)
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY) // It drops the existing history table
        onCreate(db) // Calls the onCreate function so all the updated table will be created.
    }

    fun addDate(count: String, date: String ) {
        val values = "INSERT INTO history " +
                "( 'count', 'completed_date')" +
                " VALUES " +
                "( '$count', '$date')"
        val db = this.writableDatabase
        db.execSQL(values)
        db.close()
    }

    fun retrieveCount(): ArrayList<String> {
        val countList = ArrayList<String>() // ArrayList is initialized
        val db = this.readableDatabase
        val cursor = db.rawQuery("SELECT * FROM $TABLE_HISTORY", null)
        while (cursor.moveToNext())
        {countList.add(cursor.getString(cursor.getColumnIndex(COLUMN_COUNT)))
        }
        cursor.close()
        return countList

    }

    fun getAllCompletedDatesList(): ArrayList<HistoryRow> {
        val list = ArrayList<HistoryRow>() // ArrayList is initialized
        val db = this.readableDatabase
        val cursor = db.rawQuery("SELECT * FROM $TABLE_HISTORY", null)
        while (cursor.moveToNext())
             list.add(
                 HistoryRow(
                 cursor.getString(cursor.getColumnIndex(COLUMN_ID)),
                     cursor.getString(cursor.getColumnIndex(COLUMN_COUNT)),
                     cursor.getString(cursor.getColumnIndex(COLUMN_COMPLETED_DATE))
             )
             )
        cursor.close()
        return list
    }

    companion object {
        const val DATABASE_VERSION = 1 // This DATABASE Version
        const val DATABASE_NAME = "TheCountDatabase9.db" // Name of the DATABASE
        const val TABLE_HISTORY = "history" // Table Name
        const val COLUMN_ID = "_id" // Column Id
        const val COLUMN_COUNT = "count" // Count
        const val COLUMN_COMPLETED_DATE = "completed_date" // Column for Completed Date
    }
    data class HistoryRow(
        val id: String,
        val count: String,
        val date: String
    )
}
  • HistoryRow class added (could be elsewhere but added here for brevity)
  • Note the changes to the getAllCompletedDatesList() method/function to build and return an ArrayList<HistoryRow> instead of the ArrayList<String>.

The other changes required are for the HistoryAdapter which was changed to :-

class HistoryAdapter(val context: Context, val items: ArrayList<SqliteOpenHelper.HistoryRow>) :
    RecyclerView.Adapter<HistoryAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(context).inflate(
                R.layout.item_history_row,
                parent,
                false
            )
        )
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        
        holder.tvPosition.setText((position + 1).toString())
        holder.tvCount.setText(items[position].count)
        holder.tvItem.setText(items[position].date)

        if (position % 2 == 0) {
            holder.llHistoryItemMain.setBackgroundColor(
                Color.parseColor("#EBEBEB")
            )
        } else {
            holder.llHistoryItemMain.setBackgroundColor(
                Color.parseColor("#FFFFFF")
            )
        }
    }

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

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val llHistoryItemMain = view.findViewById<LinearLayout>(R.id.ll_history_item_main)
        val tvItem = view.findViewById<TextView>(R.id.tvItem)
        val tvPosition = view.findViewById<TextView>(R.id.tvPosition)
        val tvCount = view.findViewById<TextView>(R.id.tvCount)
    }
}
  • items changed from ArrayList to ArrayList
  • onBindViewHolder assigns values from the elements of items
  • I'm not sure why I had to change the ViewHolder to use view.FindViewById but I had to.

As I had to create the layouts I have commented out setting the toolbar so as not to have to go to the pains of creating appropriate layouts. Obviously the layouts may differ. However when run the following results were achieved:-

When first run :-

enter image description here

When History is clicked (no data as yet, as expected) :-

enter image description here

Going back to Main and clicking SAVE 3 times and then to History :-

enter image description here

i.e. the 3 rows are now displayed with the respective data (albeit the same data)

MikeT
  • 51,415
  • 16
  • 49
  • 68
  • Thank you very much for this. Much appreciated. Just before checking your response, I had actually resolved this issue. I will read over your response and compare it with my solution. Thanks again :) – Georgew980 Sep 04 '21 at 02:31