
ГУАП
КАФЕДРА № 42
ОТЧЕТ ЗАЩИЩЕН С ОЦЕНКОЙ
ПРЕПОДАВАТЕЛЬ
Доцент, канд. техн. наук |
|
|
|
В.А. Ушаков |
должность, уч. степень, звание |
|
подпись, дата |
|
инициалы, фамилия |
ОТЧЕТ О ПРАКТИЧЕСКОЙ РАБОТЕ №4 |
СУБД И ИСПОЛЬЗОВАНИЕ СЕТЕВЫХ СЕРВИСОВ |
по курсу: Разработка мобильных приложений. Разработка мобильных приложений на Kotlin |
|
|
РАБОТУ ВЫПОЛНИЛ
СТУДЕНТ гр. № |
4116 |
|
|
|
|
|
|
|
подпись, дата |
|
инициалы, фамилия |
Санкт-Петербург 2024
Цель работы: выполнить проектирование и разработку мобильного приложения под ОС Android на языке программирования высокого уровня Kotlin.
Вариант 10: Класс «Таксопарк»
Ход работы:
В проект из прошлой практической работы были подключены необходимые зависимости и установлен плагин (Листинг 1, 2).
Листинг 1 - Подключение зависимостей
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.recyclerview:recyclerview:1.2.1")
Листинг 2 – Установка плагина
id("kotlin-kapt")
Созданы макеты страниц для заказа такси, отображения всех поездок в RecyclerView и макет элемента списка (Рисунок 1 - 3).
Рисунок 1 – Макет страницы заказа
Рисунок 2 – Макет RecyclerView
Рисунок 3 – Макет элемента списка
На странице заказа пользователю предоставляются 3 поля для заполнения: количество пассажиров, откуда и куда будет поездка. Под полями расположены кнопки для заказа, при нажатии данные заносятся в БД, и для просмотра всех поездок, при нажатии происходит переход на страницу с RecyclerView. Вид страницы в приложении представлен на рисунке 4.
Рисунок 4 – Страница для заказа
На рисунках 5 и 6 представлен процесс ввода числовых и текстовых данных.
Рисунок 5 – Числовой ввод
Рисунок 6 – Текстовый ввод
После нажатия кнопки “Заказать” происходит занесение данных в БД и высвечивается уведомление об успешном добавлении данных (Рисунок 7).
Рисунок 7 – Успешное добавление данных
На странице со всеми поездками находится RecyclerView, в котором отображаются все поездки из БД (Рисунок 8).
Рисунок 8 – Страница со всеми поездками
Рядом с каждой поездкой отображен значок удаления, при нажатии на него появляется кнопка для удаления поездки (Рисунок 9, 10).
Рисунок 9 – Кнопка для удаления
Рисунок 10 – Список поездок после удаления
Была создана БД используя библиотеку Room (Листинг 3).
Листинг 3 – Создание БД
@Database(
version = 1,
entities = [
RidesDbEntity::class
]
)
abstract class AppDatabase : RoomDatabase() {
abstract fun getRidesDao(): RidesDAO
}
Реализована модель данных поездки (Листинг 4).
Листинг 4 – Модель данных поездки
data class Ride(
val passengers: Int,
val start: String,
val destination: String
)
Создана таблица “rides” для хранения поездок в БД (Листинг 5).
Листинг 5 – Создание таблицы
@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
)
Для взаимодействия с БД разработан DAO интерфейс (Листинг 6).
Листинг 6 – DAO интерфейс
@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)
}
Разработаны функции для взаимодействия с БД (Листинг 7).
Листинг 7 – Функции для работы с БД
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)
}
}
}
Разработана функция для считывания данных с полей при нажатии на кнопку (Листинг 8). При успешном добавлении или, если заполнены не все поля выводится соответствующее уведомление.
Листинг 8 – Считывание данных из полей
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()
}
Для отображения данных о поездках из БД в RecyclerView создан адаптер (Листинг 9).
Листинг 9 - Адаптер
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)
}
Вывод: в ходе выполнения практической работы добавлена страница с заказом такси, на которой расположены поля для заполнения, кнопка для добавления заказа в БД и кнопка для перехода к странице с отображение всех поездок из БД через RecyclerView. Создана БД соответственно варианту, определена модель данных, таблица. Разработан DAO интерфейс, реализован адаптер для отображения данных из БД в RecyclerView.
Приложение
Листинг - AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
<activity
android:name=".Car1Activity"
android:exported="true" />
<activity
android:name=".Car2Activity"
android:exported="true" />
<activity
android:name=".Car3Activity"
android:exported="true" />
<activity
android:name=".Car4Activity"
android:exported="true" />
<activity
android:name=".Car5Activity"
android:exported="true" />
<activity
android:name=".OrderActivity"
android:exported="true" />
<activity
android:name=".NothificationsFragment"
android:exported="false" />
<activity
android:name=".OrderFragment"
android:exported="false" />
<activity
android:name=".ProfileFragment"
android:exported="false" />
<activity
android:name=".CarFragment"
android:exported="false" />
<activity
android:name=".AppActivity"
android:exported="true" />
<activity
android:name=".TaxiActivity"
android:exported="true"
android:label="@string/app_name"/>
<activity
android:name=".AllRidesActivity"
android:exported="true"/>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Листинг - build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
Id("kotlin-kapt")
}
android {
namespace = "com.example.myapplication"
compileSdk = 34
defaultConfig {
applicationId = "com.example.myapplication"
minSdk = 28
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
kapt {
arguments {arg("room.schemaLocation", "$projectDir/schemas")}
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
}
}
dependencies {