यह उदाहरण दर्शाता है कि Kotlin में SQLite डेटाबेस, RecyclerView, और कस्टम एडाप्टर का उपयोग करके संपर्क प्रबंधन ऐप कैसे बनाएं (CRUD ऑपरेशन के साथ)।
चरण 1 - नया प्रोजेक्ट बनाएं
Android Studio: File → New Project।
चरण 2 - build.gradle निर्भरताएं जोड़ें
dependencies {
implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "androidx.cardview:cardview:1.0.0"
implementation "com.google.android.material:material:1.11.0"
}
चरण 3 - activity_main.xml लेआउट
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewContacts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="60dp"
android:paddingBottom="8dp" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="संपर्क जोड़ें"
android:src="@drawable/ic_baseline_add_24" />
</FrameLayout>
चरण 4 - डेटा मॉडल: Contact.kt
data class Contact(
var id: Int = 0,
var name: String = "",
var phoneNumber: String = ""
)
चरण 5 - डेटाबेस हेल्पर: ContactDatabase.kt
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import java.util.ArrayList
class ContactDatabase(context: Context) : SQLiteOpenHelper(
context, "ContactsDB", null, 1
) {
companion object {
const val TABLE_CONTACTS = "contacts"
const val COL_ID = "_id"
const val COL_NAME = "name"
const val COL_PHONE = "phone_number"
}
override fun onCreate(db: SQLiteDatabase) {
val createTable = "CREATE TABLE $TABLE_CONTACTS (" +
"$COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"$COL_NAME TEXT, " +
"$COL_PHONE TEXT)"
db.execSQL(createTable)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS $TABLE_CONTACTS")
onCreate(db)
}
fun addContact(contact: Contact): Long {
val db = writableDatabase
val values = ContentValues().apply {
put(COL_NAME, contact.name)
put(COL_PHONE, contact.phoneNumber)
}
return db.insert(TABLE_CONTACTS, null, values)
}
fun getAllContacts(): ArrayList {
val contacts = ArrayList()
val db = readableDatabase
val cursor = db.rawQuery("SELECT * FROM $TABLE_CONTACTS ORDER BY $COL_NAME", null)
if (cursor.moveToFirst()) {
do {
contacts.add(Contact(
id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_ID)),
name = cursor.getString(cursor.getColumnIndexOrThrow(COL_NAME)),
phoneNumber = cursor.getString(cursor.getColumnIndexOrThrow(COL_PHONE))
))
} while (cursor.moveToNext())
}
cursor.close()
return contacts
}
fun updateContact(contact: Contact): Int {
val db = writableDatabase
val values = ContentValues().apply {
put(COL_NAME, contact.name)
put(COL_PHONE, contact.phoneNumber)
}
return db.update(TABLE_CONTACTS, values, "$COL_ID = ?", arrayOf(contact.id.toString()))
}
fun deleteContact(id: Int): Int {
val db = writableDatabase
return db.delete(TABLE_CONTACTS, "$COL_ID = ?", arrayOf(id.toString()))
}
}
चरण 6 - व्यू होल्डर: ContactViewHolder.kt
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class ContactViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvName: TextView = itemView.findViewById(R.id.tvContactName)
val tvPhone: TextView = itemView.findViewById(R.id.tvPhoneNumber)
val ivEdit: ImageView = itemView.findViewById(R.id.ivEdit)
val ivDelete: ImageView = itemView.findViewById(R.id.ivDelete)
}
चरण 7 - एडाप्टर: ContactAdapter.kt
import android.app.Activity
import android.content.Context
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.EditText
import android.widget.Filter
import android.widget.Filterable
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView
import java.util.ArrayList
class ContactAdapter(
private val context: Context,
private var contacts: ArrayList
) : RecyclerView.Adapter(), Filterable {
private val originalContacts = ArrayList()
private val db = ContactDatabase(context)
init {
originalContacts.addAll(contacts)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_contact, parent, false)
return ContactViewHolder(view)
}
override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
val contact = contacts[position]
holder.tvName.text = contact.name
holder.tvPhone.text = contact.phoneNumber
holder.ivEdit.setOnClickListener { showEditDialog(contact) }
holder.ivDelete.setOnClickListener {
db.deleteContact(contact.id)
refreshData()
}
}
override fun getItemCount() = contacts.size
private fun showEditDialog(contact: Contact) {
val view = LayoutInflater.from(context).inflate(R.layout.dialog_add_edit_contact, null)
val etName = view.findViewById(R.id.etName)
val etPhone = view.findViewById(R.id.etPhone)
etName.setText(contact.name)
etPhone.setText(contact.phoneNumber)
AlertDialog.Builder(context)
.setTitle("संपर्क संपादित करें")
.setView(view)
.setPositiveButton("सहेजें") { _, _ ->
val name = etName.text.toString().trim()
val phone = etPhone.text.toString().trim()
if (TextUtils.isEmpty(name)) {
Toast.makeText(context, "नाम दर्ज करें", Toast.LENGTH_SHORT).show()
return@setPositiveButton
}
contact.name = name
contact.phoneNumber = phone
db.updateContact(contact)
refreshData()
}
.setNegativeButton("रद्द करें", null)
.show()
}
private fun refreshData() {
contacts.clear()
contacts.addAll(db.getAllContacts())
notifyDataSetChanged()
}
override fun getFilter(): Filter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val query = constraint?.toString()?.lowercase() ?: ""
val filtered = if (query.isEmpty()) {
originalContacts
} else {
originalContacts.filter { it.name.lowercase().contains(query) }.toCollection(ArrayList())
}
return FilterResults().apply { values = filtered; count = filtered.size }
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
@Suppress("UNCHECKED_CAST")
val filtered = results?.values as? ArrayList ?: ArrayList()
contacts.clear()
contacts.addAll(filtered)
notifyDataSetChanged()
}
}
}
चरण 8 - लेआउट फाइलें
item_contact.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginHorizontal="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvContactName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvPhoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#666" />
</LinearLayout>
<ImageView
android:id="@+id/ivEdit"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:contentDescription="संपादित करें"
android:src="@drawable/ic_baseline_edit_24" />
<ImageView
android:id="@+id/ivDelete"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:contentDescription="हटाएं"
android:src="@drawable/ic_baseline_delete_24" />
</LinearLayout>
</androidx.cardview.widget.CardView>
dialog_add_edit_contact.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/etName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="नाम"
android:inputType="textPersonName"
android:maxLines="1" />
<EditText
android:id="@+id/etPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:hint="फोन नंबर"
android:inputType="phone"
android:maxLines="1" />
</LinearLayout>
चरण 9 - MainActivity.kt पूर्ण
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var fabAdd: FloatingActionButton
private lateinit var etSearch: EditText
private val db = ContactDatabase(this)
private lateinit var adapter: ContactAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "संपर्क प्रबंधक"
recyclerView = findViewById(R.id.recyclerViewContacts)
fabAdd = findViewById(R.id.fabAdd)
// खोज फील्ड एक्शन बार में या लेआउट में जोड़ें
// यहां सरलता के लिए FAB क्लिक पर डायलॉग दिखाते हैं
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
loadContacts()
fabAdd.setOnClickListener { showAddDialog() }
}
private fun loadContacts() {
val contacts = db.getAllContacts()
adapter = ContactAdapter(this, contacts)
recyclerView.adapter = adapter
}
private fun showAddDialog() {
val view = layoutInflater.inflate(R.layout.dialog_add_edit_contact, null)
val etName = view.findViewById(R.id.etName)
val etPhone = view.findViewById(R.id.etPhone)
AlertDialog.Builder(this)
.setTitle("नया संपर्क जोड़ें")
.setView(view)
.setPositiveButton("जोड़ें") { _, _ ->
val name = etName.text.toString().trim()
val phone = etPhone.text.toString().trim()
if (name.isNotEmpty()) {
db.addContact(Contact(name = name, phoneNumber = phone))
loadContacts()
}
}
.setNegativeButton("रद्द करें", null)
.show()
}
override fun onDestroy() {
super.onDestroy()
// db.close() // SQLiteOpenHelper स्वतः प्रबंधित करता है
}
}
चरण 10 - AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.contactmanager">
<application ...>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
ऐप चलाएं
ऐप चलाएं। FAB (+) दबाकर नए संपर्क जोड़ें। सूची में संपर्क दिखेंगे। खोज के लिए टूलबार में SearchView जोड़ें। संपादित/हटाएं आइकन पर टैप करके संपर्क प्रबंधित करें।
आधुनिक सर्वोत्तम अभ्यास:
- Room Database: SQLiteOpenHelper के बजाय Room (ORM) का उपयोग करें - कम बॉयलरप्लेट, कंपाइल-टाइम SQL जांच, LiveData/Flow समर्थन
- ViewBinding: findViewById के बजाय ViewBinding उपयोग करें
- DiffUtil + ListAdapter: RecyclerView एडाप्टर में notifyDataSetChanged() के बजाय DiffUtil का उपयोग करें
- Coroutines/Flow: डेटाबेस ऑपरेशन IO थ्रेड पर करें