👨‍💻
Sagar R. Kothari
  • README
  • _posts
    • 2019-08-13-Start-apache-server
    • Preview
    • 2020-02-12-SwiftUICardView
    • 2019-04-10-12-Swift5
    • 2021-07-11-Android-Basic-Setup
    • 2021-01-25-Android-Retrofit
    • 2020-02-05-BinaryTree-PostOrder-Traversal
    • 2020-02-06-Array-Rotation
    • 2019-04-12-14-JekyllGenerateCategoriesPage
    • 2020-09-28-nodejs
    • 2020-07-03-Android-Kotlin-Clickable-RecyclerView
    • 2021-01-24-Android-Kotlin-hideKeyboard
    • 2020-07-17-Android-NavigationComponent-SafeArgs
    • 2020-01-31-SinglyLinkList-RemoveThoseTotalZero
    • 2020-02-18-SwiftJustDayValue
    • 2021-05-29-Android-BottomSheet
    • 2020-02-05-BinaryTree-Search
    • 2020-02-03-FontForNumbersAndCurrency
    • 2020-07-01-Anroid-Kotlin-ListView
    • 2020-01-30-SinglyLinkList-Display
    • 2020-01-31-SinglyLinkList-Delete-Head
    • 2020-07-03-Anroid-Kotlin-RecyclerView
    • MainActivity.kt
    • 2021-05-29-Android-DropDown
    • Example 1
    • 2020-02-20-Swift-Form-Data-Request
    • 2020-01-31-SinglyLinkList-Delete-Before
    • 2020-02-19-UndoLastCommit
    • 2020-02-24-Swift-UIImagePickerController
    • Sample Fastlane Script to upload to fabric/crashlytics
    • 2021-02-09-Android-Switch
    • 2020-02-13-SwiftUI-ListViewWithSwipeDelete
    • 2020-07-05-Anroid-Kotlin-RecyclerView-Retrofit
    • 2021-01-25-Android-Configs
    • Convert SVG to png macOS command
    • build.gradle (Module: app)
    • 2020-07-15-Android-Kotlin-NavDrawer
    • 2020-02-05-BinaryTree-InOrder-Traversal
    • 2021-01-25-Android-NavPop
    • 2019-04-10-16-XcodeShortCuts
    • 2021-02-10-Android-Button-icon
    • 2019-08-12-CloudKit-DataSync
    • 2020-02-10-DSLinkListSwapNodes
    • 2020-02-18-SwiftDarkOrLightMode
    • 2020-02-02-Swift-Array-Chunked-Stride
    • 2020-02-16-OpenShareSheet
    • 2020-07-19-Android-NavigationComponent-NavDrawer
    • 2019-04-12-13-Swift-SHA256
    • 2020-07-18-Android-Share-Intent-Text
    • 2021-07-11-Android-Read-SMS
    • 2020-07-10-Anroid-RootCheck
    • Important Folder Locations for iOS App Developer
    • 2021-01-25-Android-Navigation-Animation
    • Sample Fastlane Script to upload to MS App Center
    • 2021-01-25-Android-UDID
    • 2020-07-14-FCM-ForeBack
    • 2019-04-10-18-TimezoneFromISO8601Date
    • 2020-01-31-SinglyLinkList-Delete-After
    • MainActivity.kt
    • 2019-04-12-11-DateTimeFormatterJekyll
    • 2020-07-01-Anroid-Kotlin-ListView-ImageDownload
    • 2020-01-31-SinglyLinkList-Delete-End
    • 2020-01-31-Fastlane-build-ios-simulator
    • 2019-04-10-19-Useful-tools
    • Pre-commit script for iOS projects for automated code review
    • How do I Validate email address?
    • 2019-04-12-15-SwiftDateExtension
    • 2020-02-05-BinaryTree-PreOrder-Traversal
    • 2020-02-05-BinaryTree-HeightOfTree
    • 2019-04-12-12-FormattingDoubleAsCurrency
    • 2020-02-05-BinaryTree-LevelOrder-Traversal
    • 2020-07-13-Anroid-KillApp
    • 2020-07-20-Android-Share-Intent-Image
    • 2020-02-15-AskForAppReview
    • 2019-04-12-10-TableViewHideSearchBar
    • 2020-02-18-SwiftSizeClass
    • Preview
    • 2020-02-05-BinaryTree-Insert
    • 2021-02-07-Android-Device-Lock
    • How do I show or hide password?
    • 2020-01-30-SinglyLinkList-Middle
    • 2020-07-16-Android-NavigationComponent
    • 2021-02-12-Android-Regex
    • 2020-01-31-SinglyLinkList-Insert-Before
    • 2021-02-10-Android-Button-Background
    • 2020-01-30-SinglyLinkList-Insert-atEnd
    • 2019-05-24-codeSnipsForVue
    • 2020-07-12-Anroid-EventBus
    • 2020-07-11-Anroid-Background-Foreground-Check
    • 2020-02-19-macOSCatalyst-AvoidPods
    • 2021-02-07-Android-Is-Internet-connected
    • 2020-02-07-SwiftUI-ImagePicker
    • 2020-02-04-Swift-IAP-Receipt-Validation
    • 2021-02-08-Android-hard-space
    • 2020-01-30-SinglyLinkList-Inverse
    • 2020-01-29-Swift-iCloud-check
    • 2021-01-24-Android-Kotlin-FragmentLayoutBinding
    • 2019-08-08-Pods-I-use
    • 2019-04-12-18-AddAcknowledgement
    • 2020-02-17-macOSCatalyst-HideTitleBar
    • 2019-04-12-16-isLive
    • 2020-07-02-Anroid-Kotlin-Volley-Get-GitUsers
    • 2019-04-13-11-SwiftRelativeDate
    • 2019-04-12-19-ChangeBarButtonFont
    • 2020-02-09-Swift-Equatable
    • 2021-02-13-Android-show-hide-menu
    • 2020-02-12-SwiftUI-List-withCardView
    • 2020-02-12-SwiftUIFabAction
    • 2019-08-08-zsh-git-commands
    • 2020-07-04-Anroid-Kotlin-RecyclerView-SwipeRefresh
    • 2020-01-30-PlayVideo
    • 2020-02-08-Swift-Reduce-Example
    • Generating App Icons from Single image named 1024x1024.png
    • Mocking data with Mocky and randomuser
    • 2020-02-14-Swift-PerformTaskInBackground
    • 2020-01-31-Github-Actions-Fastlane
    • 2020-02-25-Swift-UIImage-Resize
    • Preview
    • 2020-02-11-macOS-Mouse-Hover-Highlight
    • 2021-01-26-Android-Secure-Pref
    • 2020-02-07-SwiftUI-List-RemoveSeparator
    • 2020-01-30-SinglyLinkList-Insert-atHead
    • 2020-01-31-SinglyLinkList-Insert-After
    • 2020-01-31-SinglyLinkList-RemoveLoop
    • 2020-02-10-DSRecursionReverse
    • 2020-02-01-Swift-Reachability
    • 2021-02-11-Android-Copy-to-Clipboard
    • 2019-04-12-17-UsingCocoaLumberjack
    • 2020-02-07-SwiftUI-Pop-Navigation
    • 2020-07-05-Anroid-Kotlin-RecyclerView-ScrollToEnd
    • 2020-09-26-nodejs
    • 2020-07-03-Android-Kotlin-RecyclerView-PullToRefresh
    • Sample fastlane script to upload your app to TestFlight
  • tag
    • code
    • jekyll
    • customizations
    • analytics
    • installation
    • tags
    • images
    • comments
  • Tags
  • MyBookmarks
  • admin
  • Some info
Powered by GitBook
On this page
  • AndroidManifest.xml
  • build.gradle (Module: app)
  • ListItem.kt
  • MainListAdapter.kt
  • list_main.xml
  • NameTable.kt
  • NameDAO.kt
  • NameRepository.kt
  • NameRootDatabase.kt
  • NameViewModel.kt
  • activity_main.xml
  • activity_additem.xml
  • MainActivity.kt
  • AddItemActivity.kt

Was this helpful?

  1. _posts

Preview

Previous2020-02-18-SwiftSizeClassNext2020-02-05-BinaryTree-Insert

Last updated 4 years ago

Was this helpful?

  • Q: What are we trying to achieve here?

  • A: We're trying to store & show list of Names.

AndroidManifest.xml

Let's add permissions first.

<uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

build.gradle (Module: app)

  1. And time to add dependencies.

apply plugin: 'kotlin-kapt'
dependencies {
    ...
    // UI Component
    implementation 'androidx.recyclerview:recyclerview:1.1.0'

    // Material design
    implementation "com.google.android.material:material:1.1.0"

    // LifeStyle Components
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
    kapt "androidx.lifecycle:lifecycle-compiler:2.2.0"

    // Kotlin Components
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.4"
    api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4"

    // Room Components
    implementation "androidx.room:room-runtime:2.2.5"
    kapt "androidx.room:room-compiler:2.2.5"
    implementation "androidx.room:room-ktx:2.2.5"
    ...
}

ListItem.kt

List item data class

data class ListItem (
    val title: String,
    val subtitle: String
)

MainListAdapter.kt

Create Adapter for RecyclerView List - which will display list of words.

class MainListAdapter : RecyclerView.Adapter<MainListAdapter.ViewHolder>() {
    private var list: List<ListItem> = emptyList()

    class ViewHolder(view: View): RecyclerView.ViewHolder(view)

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

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.itemView.title.text = list[position].title
        holder.itemView.subtitle.text = list[position].subtitle
        holder.itemView.setOnClickListener {
            println("Clicked on position $position")
        }
        println("Recycling view - $position")
    }

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

    internal fun setListItems(items: List<ListItem>) {
        this.list = items
        notifyDataSetChanged()
    }
}

list_main.xml

  • Layout file for single row - which is used in above adapter.

  • ImageView we'll be using in next article.

<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    android:focusable="true"
    android:clickable="true"
    android:foreground="?android:attr/selectableItemBackground">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/ic_launcher" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="5dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageView"
        app:layout_constraintTop_toTopOf="@+id/imageView">

        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Sagar"
            android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

        <TextView
            android:id="@+id/subtitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Kothari"
            android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

NameTable.kt

Create table. For this article, we're creating name table.

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "names_table")
class Name(
    @PrimaryKey(autoGenerate = true) val id: Int? = null,
    @ColumnInfo(name = "name") val name: String
)

NameDAO.kt

Data Access Object - An interface - with which we'll be able to access Name table.

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query

@Dao
interface NameDao {
    @Query("SELECT * from names_table ORDER BY name ASC")
    fun getAlphabetizedNames(): LiveData<List<Name>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(name: Name)

    @Query("DELETE FROM names_table")
    suspend fun deleteAll()
}

NameRepository.kt

Repository with which we'll be able to insert / delete / update / read.

import androidx.lifecycle.LiveData

class NameRepository(private val nameDao: NameDao) {

    val allNames: LiveData<List<Name>> = nameDao.getAlphabetizedNames()

    suspend fun insert(name: Name) {
        nameDao.insert(name)
    }
}

NameRootDatabase.kt

Database connection

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [Name::class], version = 1, exportSchema = false)
public abstract class NameRoomDatabase : RoomDatabase() {

    abstract fun nameDao(): NameDao

    companion object {
        @Volatile
        private var INSTANCE: NameRoomDatabase? = null

        fun getDatabase(context: Context): NameRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    NameRoomDatabase::class.java,
                    "name_database"
                ).build()
                INSTANCE = instance
                return instance
            }
        }
    }
}

NameViewModel.kt

ViewModel for async, easy, quick access to Name table.

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class NameViewModel(application: Application) : AndroidViewModel(application) {

    private val repository: NameRepository
    val allNames: LiveData<List<Name>>

    init {
        val namesDao = NameRoomDatabase.getDatabase(application).nameDao()
        repository = NameRepository(namesDao)
        allNames = repository.allNames
    }

    fun insert(name: Name) = viewModelScope.launch(Dispatchers.IO) {
        repository.insert(name)
    }
}

activity_main.xml

Layout for MainActivity

<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"
tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:src="@drawable/ic_add"
        app:layout_constraintBottom_toBottomOf="@id/listView"
        app:layout_constraintEnd_toEndOf="@id/listView"
        app:rippleColor="@color/colorPrimary" />

</androidx.constraintlayout.widget.ConstraintLayout>

activity_additem.xml

Layout for AddItemActivity

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <EditText
        android:id="@+id/editTextWord"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:layout_margin="10dp"
        android:autofillHints="@string/enter_name"
        android:hint="@string/enter_name"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/saveButton"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/save_button"
        android:layout_margin="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private lateinit var nameViewModel: NameViewModel
    private val newNameActivityRequestCode = 1

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val listAdapter = MainListAdapter()
        listView.adapter = listAdapter

        nameViewModel = ViewModelProvider(this).get(NameViewModel::class.java)
        nameViewModel.allNames.observe(this, Observer { names ->
            names?.let { listAdapter.setListItems(it.map { ListItem(it.name, "ID: ${it.id}") }) }
        })

        fab.setOnClickListener {
            startActivityForResult(
                Intent(this@MainActivity, AddItemActivity::class.java),
                newNameActivityRequestCode)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        val text = data?.getStringExtra(AddItemActivity.EXTRA_REPLY)
        if (newNameActivityRequestCode == requestCode && resultCode == Activity.RESULT_OK && text != null) {
            nameViewModel.insert(Name(null, text!!))
        } else {
            Toast.makeText(applicationContext, R.string.save_error, Toast.LENGTH_LONG)
        }
    }
}

AddItemActivity.kt

class AddItemActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_additem)
        saveButton.setOnClickListener {
            val replyIntent = Intent()
            if (TextUtils.isEmpty(editTextWord.text)) {
                setResult(Activity.RESULT_CANCELED, replyIntent)
            } else {
                replyIntent.putExtra(EXTRA_REPLY, editTextWord.text.toString())
                setResult(Activity.RESULT_OK, replyIntent)
            }
            finish()
        }
    }

    companion object {
        const val EXTRA_REPLY = "com.sagar_r_kothari.mycardsholder.AdditemActivity.Reply"
    }
}
Preview Image
Preview Image