Библиотека 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>
У вас в EditActivity инициализирована переменная val myDbManager = MyDbManager(this),
а как её же можно инициализировать в фрагменте ?
Пробовал
val myDbManager = MyDbManager(activity as Context)
Приложения крашится и выдает ошибку
null cannot be cast to non-null type android.content.Context
Из фрагмента: val myDbManager = MyDbManager(requireContext())
Урок № 6.
После добавлении строки val dataId = cursor?.getInt(cursor.getColumnIndex(BaseColumns._ID))
приложение крашится. Проверял дебагом. Что не так? Как исправить?
Это в функции ReadData()