
Implementation("androidx.Room:room-ktx:2.5.0")
Implementation("androidx.Room:room-runtime:2.5.0")
kapt("androidx.room:room-compiler:2.5.0")
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
Implementation ("androidx.Recyclerview:recyclerview:1.2.1")
implementation ("com.github.javafaker:javafaker:1.0.2")
implementation ("com.github.bumptech.glide:glide:4.14.2")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
Листинг - AllRidesActivity.kt
package com.example.myapplication
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.myapplication.databinding.ActivityAllRidesBinding
class AllRidesActivity : AppCompatActivity() {
private lateinit var binding: ActivityAllRidesBinding
private val viewModel by lazy { AllRidesViewModel(Dependencies.ridesRepository) }
override fun onCreate(savedInstanceState: Bundle?) {
Dependencies.init(applicationContext)
super.onCreate(savedInstanceState)
binding = ActivityAllRidesBinding.inflate(layoutInflater).also { setContentView(it.root) }
viewModel.allStatic.observe(this) { allRides ->
val adapter = RideAdapter(viewModel.rideItemListener)
adapter.data = allRides.reversed()
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
}
}
}
Листинг - AllRidesViewModel.kt
package com.example.myapplication
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class AllRidesViewModel(private val ridesRepository: RidesRepository) : ViewModel() {
private val _allRides = MutableLiveData<List<RideDataTuple>>()
val allStatic: LiveData<List<RideDataTuple>> = _allRides
init {
getAllRides()
}
val rideItemListener = object : RideItemListener{
override fun getInfoAboutRide(id: Long) {
val rideId = _allRides.value?.indexOfFirst { it.id == id }
if (rideId == -1) return
val rideInfo = _allRides.value?.get(rideId!!)
println(rideInfo)
}
override fun removeRide(id: Long) {
viewModelScope.launch {
ridesRepository.removeRideDataById(id)
getAllRides()
}
}
}
private fun getAllRides() {
viewModelScope.launch {
_allRides.value = ridesRepository.getAllStatisticData()
}
}
}
Листинг – AppDatabase.kt
package com.example.myapplication
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(
version = 1,
entities = [
RidesDbEntity::class
]
)
abstract class AppDatabase : RoomDatabase() {
abstract fun getRidesDao(): RidesDAO
}
Листинг - Dependencies.kt
package com.example.myapplication
import android.content.Context
import androidx.room.Room
object Dependencies {
private lateinit var applicationContext: Context
fun init(context: Context) {
applicationContext = context
}
private val appDatabase: AppDatabase by lazy {
Room.databaseBuilder(applicationContext, AppDatabase::class.java, "database.db")
.createFromAsset("rides.db")
.build()
}
val ridesRepository: RidesRepository by lazy { RidesRepository(appDatabase.getRidesDao()) }
}
Листинг MainActivity.kt:
package com.example.myapplication
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import com.example.myapplication.databinding.ActivityMainBinding
public class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel by lazy { MainViewModel(Dependencies.ridesRepository) }
override fun onCreate(savedInstanceState: Bundle?) {
Dependencies.init(applicationContext)
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater).also { setContentView(it.root) }
binding.addRideButton.setOnClickListener { addRideButtonPressed() }
binding.allRidesButton.setOnClickListener { allRidesButtonPressed() }
}
private fun addRideButtonPressed() {
if (isEditTextsEmpty()) {
viewModel.insertNewRideDataInDatabase(
passengers = binding.passengersEditText.text.toString().toInt(),
start = binding.startEditText.text.toString(),
destination = binding.destinationEditText.text.toString(),
)
Toast.makeText(this, "Данные добавлены в таблицу!", Toast.LENGTH_SHORT)
.show()
} else Toast.makeText(this, "Не все поля заполнены!", Toast.LENGTH_SHORT)
.show()
}
private fun allRidesButtonPressed() {
val intent = Intent(this, AllRidesActivity::class.java)
startActivity(intent)
}
private fun isEditTextsEmpty(): Boolean {
val isPassengers = if (binding.passengersEditText.text.toString().isNotBlank()) {
binding.valueTextInputPassengers.error = null
true
} else {
binding.valueTextInputPassengers.error = "Поле не должно быть пустым!"
false
}
val isStart = if (binding.startEditText.text.toString().isNotBlank()) {
binding.valueTextInputStart.error = null
true
} else {
binding.valueTextInputStart.error = "Поле не должно быть пустым!"
false
}
val isDestination = if (binding.destinationEditText.text.toString().isNotBlank()) {
binding.valueTextInputDestination.error = null
true
} else {
binding.valueTextInputDestination.error = "Поле не должно быть пустым!"
false
}
return isPassengers && isStart && isDestination
}
}
Листинг - MainViewModel.kt
package com.example.myapplication
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class MainViewModel(private val ridesRepository: RidesRepository) : ViewModel() {
fun insertNewRideDataInDatabase(passengers: Int, start: String, destination: String) {
viewModelScope.launch {
val newRide = Ride(passengers, start, destination)
ridesRepository.insertNewRideData(newRide.toRidesDbEntity())
}
}
}
Листинг - Ride.kt
package com.example.myapplication
data class Ride(
val passengers: Int,
val start: String,
val destination: String
) {
fun toRidesDbEntity(): RidesDbEntity = RidesDbEntity(
id = 0,
passengers = passengers,
start = start,
destination = destination,
)
}
Листинг - RideAdapter:
package com.example.myapplication
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.example.myapplication.databinding.ItemRideBinding
class RideDiffUtil(
private val oldList: List<RideDataTuple>,
private val newList: List<RideDataTuple>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
return oldItem == newItem
}
}
interface RideItemListener {
fun getInfoAboutRide(id: Long)
fun removeRide(id: Long)
}
class RideAdapter(private val rideItemListener: RideItemListener) :
RecyclerView.Adapter<RideAdapter.RideViewHolder>(), View.OnClickListener {
var data: List<RideDataTuple> = emptyList()
set(newValue) {
val diffUtil = RideDiffUtil(field, newValue)
val diffUtilResult = DiffUtil.calculateDiff(diffUtil)
field = newValue
diffUtilResult.dispatchUpdatesTo(this@RideAdapter)
}
override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RideViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemRideBinding.inflate(inflater, parent, false)
binding.delete.setOnClickListener(this)
return RideViewHolder(binding)
}
override fun onBindViewHolder(holder: RideViewHolder, position: Int) {
val ride = data[position]
with(holder.binding) {
delete.tag = ride
passengersTextView.text = ride.passengers.toString()
startTextView.text = ride.start
destinationTextView.text = ride.destination
}
}
override fun onClick(view: View) {
showPopupMenu(view)
}
private fun showPopupMenu(view: View) {
val popupMenu = PopupMenu(view.context, view)
val ride = view.tag as RideDataTuple
popupMenu.menu.add(0, ID_REMOVE, Menu.NONE, "Удалить поездку")
popupMenu.setOnMenuItemClickListener {
when (it.itemId) {
ID_INFORMATION -> rideItemListener.getInfoAboutRide(ride.id)
ID_REMOVE -> rideItemListener.removeRide(ride.id)
}
return@setOnMenuItemClickListener true
}
popupMenu.show()
}
companion object {
private const val ID_INFORMATION = 1
private const val ID_REMOVE = 2
}
class RideViewHolder(val binding: ItemRideBinding) :
RecyclerView.ViewHolder(binding.root)
}
Листинг - RidesDAO.kt
package com.example.myapplication
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
@Dao
interface RidesDAO {
@Insert(entity = RidesDbEntity::class)
fun insertRideData(ride: RidesDbEntity)
@Query("SELECT rides.id, passengers, start, destination FROM rides\n")
fun getAllRideData(): List<RideDataTuple>
@Query("DELETE FROM rides WHERE id = :rideId")
fun deleteRideDataById(rideId: Long)
}
Листинг - RidesDbEntity
package com.example.myapplication
import androidx.room.*
@Entity(
tableName = "rides",
indices = [Index("id")],
)
data class RidesDbEntity(
@PrimaryKey(autoGenerate = true) val id: Long,
@ColumnInfo(name = "Passengers") val passengers: Int,
@ColumnInfo(name = "Start") val start: String,
@ColumnInfo(name = "Destination") val destination: String
)
Листинг - RidesRepository.kt
package com.example.myapplication
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class RidesRepository(private val ridesDAO: RidesDAO) {
suspend fun insertNewRideData(ridesDbEntity: RidesDbEntity) {
withContext(Dispatchers.IO) {
ridesDAO.insertRideData(ridesDbEntity)
}
}
suspend fun getAllStatisticData(): List<RideDataTuple> {
return withContext(Dispatchers.IO) {
return@withContext ridesDAO.getAllRideData()
}
}
suspend fun removeRideDataById(id: Long) {
withContext(Dispatchers.IO) {
ridesDAO.deleteRideDataById(id)
}
}
}
Листинг - Tuples.kt
package com.example.myapplication
import androidx.room.ColumnInfo
data class RideDataTuple(
val id: Long,
@ColumnInfo(name = "Passengers") val passengers: Int,
@ColumnInfo(name = "Start") val start: String,
@ColumnInfo(name = "Destination") val destination: String
)
Листинг - activity_all_rides.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/light_yellow"/>
Листинг - activity_main.xml
<?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="match_parent"
android:paddingTop="50dp"
android:background="@color/light_yellow"
tools:context=".MainActivity">
<TextView
android:id="@+id/title"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_new_ride"
android:textStyle="normal|bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/valueTextInputPassengers"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="Количество пассажиров"
android:minWidth="250dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintVertical_chainStyle="packed">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/passengersEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="numberDecimal"
android:lines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/valueTextInputStart"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="Откуда"
android:minWidth="250dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/valueTextInputPassengers"
app:layout_constraintVertical_chainStyle="packed">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/start_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="text"
android:lines="1"
android:textColorLink="#000000" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/valueTextInputDestination"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="Куда"
android:minWidth="250dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/valueTextInputStart"
app:layout_constraintVertical_chainStyle="packed">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/destination_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="text"
android:lines="1"
android:textColorLink="#000000" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/add_ride_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:minWidth="250dp"
android:text="@string/add_ride"
android:textAllCaps="true"
android:textColor="@color/light_yellow"
app:backgroundTint="#000000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/valueTextInputDestination"
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.None" />
<com.google.android.material.button.MaterialButton
android:id="@+id/all_rides_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:minWidth="250dp"
android:text="Все поездки"
android:textAllCaps="true"
android:textColor="@color/light_yellow"
app:backgroundTint="#000000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.496"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/add_ride_button"
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.None" />
<ImageView
android:id="@+id/imageView"
android:layout_width="233dp"
android:layout_height="355dp"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.496"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/add_ride_button"
app:srcCompat="@drawable/taxicar" />
</androidx.constraintlayout.widget.ConstraintLayout>
Листинг - item_ride.xml
<?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:padding="15dp"
android:background="@color/light_yellow">
<TextView
android:id="@+id/passengers_text_view"
style="@style/TextAppearance.AppCompat.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBaseline_toBaselineOf="@id/passengersTitle"
app:layout_constraintStart_toEndOf="@id/passengersTitle"
tools:text=" " />
<TextView
android:id="@+id/passengersTitle"
style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="15dp"
android:paddingEnd="5dp"
android:text="@string/passengers"
android:textSize="15sp"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="3dp"
tools:ignore="MissingConstraints" />
<TextView
android:id="@+id/destinationTitle"
style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="15dp"
android:paddingEnd="5dp"
android:text="@string/destination"
android:textSize="15sp"
app:layout_constraintTop_toBottomOf="@id/startTitle"
tools:layout_editor_absoluteX="3dp"
tools:ignore="MissingConstraints" />
<TextView
android:id="@+id/start_text_view"
style="@style/TextAppearance.AppCompat.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBaseline_toBaselineOf="@id/startTitle"
app:layout_constraintStart_toEndOf="@id/startTitle"
tools:text=" " />
<TextView
android:id="@+id/destination_text_view"
style="@style/TextAppearance.AppCompat.Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBaseline_toBaselineOf="@id/destinationTitle"
app:layout_constraintStart_toEndOf="@id/destinationTitle"
tools:text=" " />
<TextView
android:id="@+id/startTitle"
style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="15dp"
android:paddingEnd="5dp"
android:text="@string/start"
android:textSize="15sp"
app:layout_constraintTop_toBottomOf="@id/passengersTitle"
tools:layout_editor_absoluteX="3dp"
tools:ignore="MissingConstraints" />
<ImageView
android:id="@+id/delete"
android:layout_width="68dp"
android:layout_height="55dp"
android:src="@drawable/baseline_delete_outline_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>