SQLite на Kotlin. Урок 10

Библиотека Coroutines

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'

MainActivity

import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.SearchView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.lessonsqlitekotlin.db.MyAdapter
import com.example.lessonsqlitekotlin.db.MyDbManager
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

val myDbManager = MyDbManager(this)
val myAdapter = MyAdapter(ArrayList(), this)
private var job: Job? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
initSearchView()

}
override fun onDestroy() {
super.onDestroy()
myDbManager.closeDb()
}

override fun onResume() {
super.onResume()
myDbManager.openDb()
fillAdapter("")

}

fun onClickNew(view: View) {

val i = Intent(this,EditActivity::class.java)
startActivity(i)
}

fun init(){

rcView.layoutManager = LinearLayoutManager(this)
val swapHelper = getSwapMg()
swapHelper.attachToRecyclerView(rcView)
rcView.adapter = myAdapter

}

private fun initSearchView(){
searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
return true
}

override fun onQueryTextChange(text: String?): Boolean {
fillAdapter(text!!)
return true
}
})
}

private fun fillAdapter(text: String){

job?.cancel()
job = CoroutineScope(Dispatchers.Main).launch{

val list = myDbManager.readDbData(text)
myAdapter.updateAdapter(list)
if(list.size > 0){

tvNoElements.visibility = View.GONE
} else {

tvNoElements.visibility = View.VISIBLE
}
}


}
private fun getSwapMg(): ItemTouchHelper{
return ItemTouchHelper(object:ItemTouchHelper.
SimpleCallback(0,ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT){
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
myAdapter.removeItem(viewHolder.adapterPosition, myDbManager)

}
})
}


}

EditActivity

import android.app.Activity
import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import com.example.lessonsqlitekotlin.db.MyDbManager
import com.example.lessonsqlitekotlin.db.MyIntentConstants
import kotlinx.android.synthetic.main.edit_activity.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*

class EditActivity : AppCompatActivity() {

var id = 0
var isEditState = false
val imageRequestCode = 10
var tempImageUri = "empty"
val myDbManager = MyDbManager(this)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.edit_activity)
getMyIntents()
Log.d("MyLog","Time : ${getCurrentTime()}")
}
override fun onDestroy() {
super.onDestroy()
myDbManager.closeDb()
}

override fun onResume() {
super.onResume()
myDbManager.openDb()


}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK && requestCode == imageRequestCode){

imMainImage.setImageURI(data?.data)
tempImageUri = data?.data.toString()
contentResolver.takePersistableUriPermission(data?.data!!, Intent.FLAG_GRANT_READ_URI_PERMISSION)

}

}

fun onClickAddImage(view: View) {

mainImageLayout.visibility = View.VISIBLE
fbAddImage.visibility = View.GONE

}

fun onClickDeleteImage(view: View) {

mainImageLayout.visibility = View.GONE
fbAddImage.visibility = View.VISIBLE
tempImageUri = "empty"
}

fun onClickChooseImage(view: View) {

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "image/*"
startActivityForResult(intent, imageRequestCode)

}
fun onClickSave(view: View) {

val myTitle = edTitle.text.toString()
val myDesc = edDesc.text.toString()

if(myTitle != "" && myDesc != ""){

CoroutineScope(Dispatchers.Main).launch {

if(isEditState){
myDbManager.updateItem(myTitle, myDesc, tempImageUri, id, getCurrentTime())
} else {
myDbManager.insertToDb(myTitle, myDesc, tempImageUri, getCurrentTime())
}

finish()
}

}



}

fun onEditEnable(view:View){
edTitle.isEnabled = true
edDesc.isEnabled = true
fbEdit.visibility = View.GONE
fbAddImage.visibility = View.VISIBLE
if(tempImageUri == "empty")return
imButtonEditImage.visibility = View.VISIBLE
imButtonDeleteImage.visibility = View.VISIBLE

}

fun getMyIntents() {
fbEdit.visibility = View.GONE
val i = intent

if (i != null) {


if (i.getStringExtra(MyIntentConstants.I_TITLE_KEY) != null) {

fbAddImage.visibility = View.GONE
edTitle.setText(i.getStringExtra(MyIntentConstants.I_TITLE_KEY))
isEditState = true
edTitle.isEnabled = false
edDesc.isEnabled = false
fbEdit.visibility = View.VISIBLE
edDesc.setText(i.getStringExtra(MyIntentConstants.I_DESC_KEY))
id = i.getIntExtra(MyIntentConstants.I_ID_KEY, 0)
if (i.getStringExtra(MyIntentConstants.I_URI_KEY) != "empty") {

mainImageLayout.visibility = View.VISIBLE
tempImageUri = i.getStringExtra(MyIntentConstants.I_URI_KEY)!!
imMainImage.setImageURI(Uri.parse(tempImageUri))
imButtonDeleteImage.visibility = View.GONE
imButtonEditImage.visibility = View.GONE

}

}

}

}

private fun getCurrentTime():String{
val time = Calendar.getInstance().time
val formatter = SimpleDateFormat("dd-MM-yy kk:mm",Locale.getDefault())
return formatter.format(time)
}
}

MyDbManager

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.provider.BaseColumns
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class MyDbManager(context: Context) {
val myDbHelper = MyDbHelper(context)
var db: SQLiteDatabase? = null

fun openDb(){
db = myDbHelper.writableDatabase
}
suspend fun insertToDb( title: String, content: String, uri: String, time:String) = withContext(Dispatchers.IO){
val values = ContentValues().apply {

put(MyDbNameClass.COLUMN_NAME_TITLE, title)
put(MyDbNameClass.COLUMN_NAME_CONTENT, content)
put(MyDbNameClass.COLUMN_NAME_IMAGE_URI, uri)
put(MyDbNameClass.COLUMN_NAME_TIME, time)

}
db?.insert(MyDbNameClass.TABLE_NAME,null, values)
}

suspend fun updateItem( title: String, content: String, uri: String, id:Int, time:String) = withContext(Dispatchers.IO){
val selection = BaseColumns._ID + "=$id"
val values = ContentValues().apply {

put(MyDbNameClass.COLUMN_NAME_TITLE, title)
put(MyDbNameClass.COLUMN_NAME_CONTENT, content)
put(MyDbNameClass.COLUMN_NAME_IMAGE_URI, uri)
put(MyDbNameClass.COLUMN_NAME_TIME, time)

}
db?.update(MyDbNameClass.TABLE_NAME, values, selection, null)
}

fun removeItemFromDb( id: String){
val selection = BaseColumns._ID + "=$id"
db?.delete(MyDbNameClass.TABLE_NAME,selection, null)
}

suspend fun readDbData(searchText:String): ArrayList<ListItem> = withContext(Dispatchers.IO) {
val dataList = ArrayList<ListItem>()
val selection = "${MyDbNameClass.COLUMN_NAME_TITLE} like ?"
val cursor = db?.query(
MyDbNameClass.TABLE_NAME, null, selection, arrayOf("%$searchText%"),
null, null, null
)

while (cursor?.moveToNext()!!) {
val dataTitle = cursor.getString(cursor.getColumnIndex(MyDbNameClass.COLUMN_NAME_TITLE))
val dataContent =
cursor.getString(cursor.getColumnIndex(MyDbNameClass.COLUMN_NAME_CONTENT))
val dataUri =
cursor.getString(cursor.getColumnIndex(MyDbNameClass.COLUMN_NAME_IMAGE_URI))
val dataId =
cursor.getInt(cursor.getColumnIndex(BaseColumns._ID))
val time =
cursor.getString(cursor.getColumnIndex(MyDbNameClass.COLUMN_NAME_TIME))
val item = ListItem()
item.title = dataTitle
item.desc = dataContent
item.uri = dataUri
item.id = dataId
item.time = time
dataList.add(item)
}
cursor.close()
return@withContext dataList
}

fun closeDb(){
myDbHelper.close()
}


}

MyDbHelper

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

class MyDbHelper(context: Context) : SQLiteOpenHelper(context, MyDbNameClass.DATABASE_NAME,
null, MyDbNameClass.DATABASE_VERSION) {
override fun onCreate(db: SQLiteDatabase?) {

db?.execSQL(MyDbNameClass.CREAT_TABLE)
}

override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {

db?.execSQL(MyDbNameClass.SQL_DELETE_TABLE)
onCreate(db)
}
}

MyAdapter

import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.lessonsqlitekotlin.EditActivity
import com.example.lessonsqlitekotlin.R

class MyAdapter( listMain:ArrayList<ListItem>, contextM: Context) : RecyclerView.Adapter<MyAdapter.MyHolder>() {
var listArray = listMain
var context = contextM


class MyHolder(itemView: View, contextV:Context) : RecyclerView.ViewHolder(itemView) {

val tvTitle:TextView = itemView.findViewById(R.id.tvTitle)
val tvTime:TextView = itemView.findViewById(R.id.tvTime)
val context = contextV

fun setData(item:ListItem){

tvTitle.text = item.title
tvTime.text = item.time
itemView.setOnClickListener {

val intent = Intent(context,EditActivity::class.java).apply {

putExtra(MyIntentConstants.I_TITLE_KEY, item.title)
putExtra(MyIntentConstants.I_DESC_KEY, item.desc)
putExtra(MyIntentConstants.I_URI_KEY, item.uri)
putExtra(MyIntentConstants.I_ID_KEY, item.id)

}
context.startActivity(intent)

}


}

}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val inflater = LayoutInflater.from(parent.context)
return MyHolder(inflater.inflate(R.layout.rc_item, parent, false), context)
}

override fun getItemCount(): Int {

return listArray.size

}

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

holder.setData(listArray.get(position))
}

fun updateAdapter(listItems:List<ListItem>){

listArray.clear()
listArray.addAll(listItems)
notifyDataSetChanged()

}

fun removeItem(pos:Int, dbManager: MyDbManager){

dbManager.removeItemFromDb(listArray[pos].id.toString())
listArray.removeAt(pos)
notifyItemRangeChanged(0, listArray.size)
notifyItemRemoved(pos)


}
}

ListItem

class ListItem {

var title = "empty"
var desc = "empty"
var uri = "empty"
var time = ""
var id = 0
}

MyDbNameClass

import android.provider.BaseColumns

object MyDbNameClass {

const val TABLE_NAME = "my_table"
const val COLUMN_NAME_TITLE = "title"
const val COLUMN_NAME_CONTENT = "content"
const val COLUMN_NAME_IMAGE_URI = "uri"
const val COLUMN_NAME_TIME = "time"

const val DATABASE_VERSION = 2
const val DATABASE_NAME = "MyLessonDb.db"

const val CREAT_TABLE = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" +
"${BaseColumns._ID} INTEGER PRIMARY KEY,$COLUMN_NAME_TITLE TEXT,$COLUMN_NAME_CONTENT TEXT," +
"$COLUMN_NAME_IMAGE_URI TEXT,$COLUMN_NAME_TIME TEXT)"
const val SQL_DELETE_TABLE = "DROP TABLE IF EXISTS $TABLE_NAME"


}

MyIntentConstants

object MyIntentConstants {

const val I_TITLE_KEY = "title_key"
const val I_DESC_KEY = "desc_key"
const val I_URI_KEY = "uri_key"
const val I_ID_KEY = "id_key"

}

edit_text_color_state.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@color/edit_text_dark"/>
<item android:color="@color/edit_text_dark"/>
</selector>

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:background="@color/dark_bg"
tools:context=".MainActivity">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rcView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/searchView"
app:layout_constraintVertical_bias="1.0" />

<TextView
android:id="@+id/tvNoElements"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Empty"
android:textColor="@color/grey"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fbNew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:clickable="true"
android:onClick="onClickNew"
app:backgroundTint="@color/dark_blue"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_add" />

<SearchView
android:id="@+id/searchView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

edit_activity.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:background="@color/dark_bg"
tools:context=".EditActivity">

<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.33" />

<EditText
android:id="@+id/edTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="@android:drawable/editbox_background"
android:ems="10"
android:hint="@string/ed_title_hint"
android:inputType="textPersonName"
android:padding="10dp"
android:textColor="@color/edit_text_color_state"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mainImageLayout" />

<EditText
android:id="@+id/edDesc"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="@android:drawable/editbox_background"
android:ems="10"
android:gravity="top"
android:hint="@string/ed_desc_hint"
android:inputType="textMultiLine|textPersonName"
android:padding="10dp"
android:textColor="@color/edit_text_color_state"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/edTitle"
app:layout_constraintStart_toStartOf="@+id/edTitle"
app:layout_constraintTop_toBottomOf="@+id/edTitle" />

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fbSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:alpha="0.6"
android:clickable="true"
android:onClick="onClickSave"
app:backgroundTint="@color/dark_blue"
app:layout_constraintBottom_toBottomOf="@+id/edDesc"
app:layout_constraintEnd_toEndOf="@+id/edDesc"
app:srcCompat="@drawable/ic_save" />

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fbAddImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:alpha="0.6"
android:clickable="true"
android:onClick="onClickAddImage"
app:layout_constraintBottom_toTopOf="@+id/fbSave"
app:layout_constraintEnd_toEndOf="@+id/fbSave"
app:srcCompat="@drawable/ic_add_image" />

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/mainImageLayout"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:background="@android:drawable/editbox_background"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="@+id/edTitle"
app:layout_constraintStart_toStartOf="@+id/edTitle"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">

<ImageView
android:id="@+id/imMainImage"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="10dp"
android:scaleType="centerCrop"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_add_image"
tools:srcCompat="@tools:sample/avatars" />

<ImageButton
android:id="@+id/imButtonDeleteImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:onClick="onClickDeleteImage"
app:layout_constraintBottom_toBottomOf="@+id/imMainImage"
app:layout_constraintEnd_toEndOf="@+id/imMainImage"
app:srcCompat="@android:drawable/ic_menu_delete" />

<ImageButton
android:id="@+id/imButtonEditImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:onClick="onClickChooseImage"
app:layout_constraintEnd_toEndOf="@+id/imMainImage"
app:layout_constraintTop_toTopOf="@+id/imMainImage"
app:srcCompat="@android:drawable/ic_menu_edit" />

</androidx.constraintlayout.widget.ConstraintLayout>

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fbEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:clickable="true"
android:onClick="onEditEnable"
app:layout_constraintBottom_toBottomOf="@+id/edDesc"
app:layout_constraintStart_toStartOf="@+id/edDesc"
app:srcCompat="@android:drawable/ic_menu_edit" />

</androidx.constraintlayout.widget.ConstraintLayout>

rc_item.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:layout_marginStart="3dp"
android:layout_marginTop="3dp"
android:layout_marginEnd="3dp"
android:background="@color/blue_dark">

<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="TextView"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/tvTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
android:textColor="@color/grey2"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvTitle" />
</androidx.constraintlayout.widget.ConstraintLayout>

strings.xml

<resources>
<string name="app_name">LessonSQLIteKotlin</string>
<string name="ed_title_hint">Заголовок</string>
<string name="ed_desc_hint">Описание</string>
</resources>

4 комментария для “SQLite на Kotlin. Урок 10”

  1. У вас в EditActivity инициализирована переменная val myDbManager = MyDbManager(this),
    а как её же можно инициализировать в фрагменте ?
    Пробовал
    val myDbManager = MyDbManager(activity as Context)
    Приложения крашится и выдает ошибку
    null cannot be cast to non-null type android.content.Context

  2. Урок № 6.
    После добавлении строки val dataId = cursor?.getInt(cursor.getColumnIndex(BaseColumns._ID))
    приложение крашится. Проверял дебагом. Что не так? Как исправить?

Добавить комментарий

Ваш адрес email не будет опубликован.