Gelişmiş Not Defteri Uygulaması - Kotlin (Android)
Aşağıda, Android için Kotlin dilinde gelişmiş bir not defteri uygulamasının temel yapısını içeren bir örnek bulunmaktadır. Bu örnekte XML layout dosyası ve temel Kotlin kodu yer almaktadır.
1. activity_main.xml (Ana Layout)
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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" tools:context=".MainActivity"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/Theme.NotDefteri.AppBarOverlay"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/Theme.NotDefteri.PopupOverlay" app:title="@string/app_name" /> </com.google.android.material.appbar.AppBarLayout> <include layout="@layout/content_main" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab_add_note" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" app:srcCompat="@drawable/ic_add" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
2. content_main.xml (İçerik Layout)
<?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" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".MainActivity" tools:showIn="@layout/activity_main"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_notes" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:padding="8dp" app:layoutManager="androidx.recyclerview.widget.StaggeredGridLayoutManager" app:spanCount="2" tools:listitem="@layout/item_note" /> <TextView android:id="@+id/text_empty_notes" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/no_notes_found" android:textSize="18sp" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
3. item_note.xml (Not Öğesi Layout)
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="4dp" app:cardCornerRadius="8dp" app:cardElevation="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/text_note_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@android:color/black" android:textSize="18sp" android:textStyle="bold" /> <TextView android:id="@+id/text_note_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:maxLines="3" android:textColor="@android:color/darker_gray" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:orientation="horizontal"> <TextView android:id="@+id/text_note_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="@android:color/darker_gray" android:textSize="12sp" /> <ImageView android:id="@+id/image_note_pin" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="end" android:src="@drawable/ic_pin" android:visibility="gone" /> </LinearLayout> </LinearLayout> </androidx.cardview.widget.CardView>
4. activity_note_detail.xml (Not Detay Sayfası)
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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" tools:context=".NoteDetailActivity"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/Theme.NotDefteri.AppBarOverlay"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/Theme.NotDefteri.PopupOverlay" /> </com.google.android.material.appbar.AppBarLayout> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <EditText android:id="@+id/edit_note_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/title" android:textSize="24sp" android:textStyle="bold" /> <EditText android:id="@+id/edit_note_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:gravity="top" android:hint="@string/content" android:inputType="textMultiLine" android:minHeight="200dp" /> </LinearLayout> </ScrollView> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab_save_note" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" app:srcCompat="@drawable/ic_save" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
5. MainActivity.kt (Ana Aktivite)
import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import com.google.android.material.floatingactionbutton.FloatingActionButton class MainActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView private lateinit var fabAddNote: FloatingActionButton private lateinit var adapter: NotesAdapter private lateinit var noteDatabase: NoteDatabase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(findViewById(R.id.toolbar)) // Initialize views recyclerView = findViewById(R.id.recycler_notes) fabAddNote = findViewById(R.id.fab_add_note) // Initialize database noteDatabase = NoteDatabase.getInstance(this) // Setup RecyclerView adapter = NotesAdapter { note -> openNoteDetail(note.id) } recyclerView.adapter = adapter // Load notes loadNotes() // Set click listeners fabAddNote.setOnClickListener { openNoteDetail(null) } } private fun loadNotes() { val notes = noteDatabase.noteDao().getAllNotes() adapter.submitList(notes) findViewById<TextView>(R.id.text_empty_notes).visibility = if (notes.isEmpty()) View.VISIBLE else View.GONE } private fun openNoteDetail(noteId: Long?) { val intent = Intent(this, NoteDetailActivity::class.java) if (noteId != null) { intent.putExtra(NoteDetailActivity.EXTRA_NOTE_ID, noteId) } startActivity(intent) } override fun onResume() { super.onResume() loadNotes() } }
6. NoteDetailActivity.kt (Not Detay Aktivitesi)
import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch class NoteDetailActivity : AppCompatActivity() { companion object { const val EXTRA_NOTE_ID = "extra_note_id" } private lateinit var editTitle: EditText private lateinit var editContent: EditText private lateinit var fabSave: FloatingActionButton private lateinit var noteDatabase: NoteDatabase private var noteId: Long? = null private var currentNote: Note? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_note_detail) setSupportActionBar(findViewById(R.id.toolbar)) supportActionBar?.setDisplayHomeAsUpEnabled(true) // Initialize views editTitle = findViewById(R.id.edit_note_title) editContent = findViewById(R.id.edit_note_content) fabSave = findViewById(R.id.fab_save_note) // Initialize database noteDatabase = NoteDatabase.getInstance(this) // Get note ID from intent noteId = intent.getLongExtra(EXTRA_NOTE_ID, -1).takeIf { it != -1L } // Load note if editing if (noteId != null) { loadNote() } // Set click listeners fabSave.setOnClickListener { saveNote() } } private fun loadNote() { lifecycleScope.launch { currentNote = noteDatabase.noteDao().getNoteById(noteId!!) currentNote?.let { editTitle.setText(it.title) editContent.setText(it.content) } } } private fun saveNote() { val title = editTitle.text.toString().trim() val content = editContent.text.toString().trim() if (title.isEmpty()) { editTitle.error = "Başlık boş olamaz" return } val note = currentNote?.copy( title = title, content = content, updatedAt = System.currentTimeMillis() ) ?: Note( title = title, content = content, createdAt = System.currentTimeMillis(), updatedAt = System.currentTimeMillis() ) lifecycleScope.launch { if (noteId != null) { noteDatabase.noteDao().updateNote(note) } else { noteDatabase.noteDao().insertNote(note) } finish() } } override fun onSupportNavigateUp(): Boolean { onBackPressed() return true } }
7. NotesAdapter.kt (RecyclerView Adapter)
import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView class NotesAdapter(private val onNoteClicked: (Note) -> Unit) : ListAdapter<Note, NotesAdapter.NoteViewHolder>(NoteDiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_note, parent, false) return NoteViewHolder(view) } override fun onBindViewHolder(holder: NoteViewHolder, position: Int) { val note = getItem(position) holder.bind(note) holder.itemView.setOnClickListener { onNoteClicked(note) } } class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val titleView: TextView = itemView.findViewById(R.id.text_note_title) private val contentView: TextView = itemView.findViewById(R.id.text_note_content) private val dateView: TextView = itemView.findViewById(R.id.text_note_date) private val pinView: ImageView = itemView.findViewById(R.id.image_note_pin) fun bind(note: Note) { titleView.text = note.title contentView.text = note.content val dateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.getDefault()) dateView.text = dateFormat.format(Date(note.updatedAt)) pinView.visibility = if (note.isPinned) View.VISIBLE else View.GONE } } } class NoteDiffCallback : DiffUtil.ItemCallback<Note>() { override fun areItemsTheSame(oldItem: Note, newItem: Note): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: Note, newItem: Note): Boolean { return oldItem == newItem } }
8. Note.kt (Veri Modeli)
import androidx.room.Entity import androidx.room.PrimaryKey @Entity(tableName = "notes") data class Note( @PrimaryKey(autoGenerate = true) val id: Long = 0, val title: String, val content: String, val createdAt: Long, val updatedAt: Long, val isPinned: Boolean = false, val color: Int = 0 )
9. NoteDatabase.kt (Room Database)
import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import android.content.Context @Database(entities = [Note::class], version = 1) abstract class NoteDatabase : RoomDatabase() { abstract fun noteDao(): NoteDao companion object { @Volatile private var INSTANCE: NoteDatabase? = null fun getInstance(context: Context): NoteDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, NoteDatabase::class.java, "note_database" ).build() INSTANCE = instance instance } } } }
10. NoteDao.kt (Room DAO)
import androidx.room.Dao import androidx.room.Insert import androidx.room.Query import androidx.room.Update import kotlinx.coroutines.flow.Flow @Dao interface NoteDao { @Insert suspend fun insertNote(note: Note): Long @Update suspend fun updateNote(note: Note) @Query("SELECT * FROM notes ORDER BY isPinned DESC, updatedAt DESC") fun getAllNotes(): List<Note> @Query("SELECT * FROM notes WHERE id = :noteId") suspend fun getNoteById(noteId: Long): Note? @Query("DELETE FROM notes WHERE id = :noteId") suspend fun deleteNote(noteId: Long) }
Bu örnek, temel bir not defteri uygulamasının tüm ana bileşenlerini içermektedir. Uygulamayı daha da geliştirmek için aşağıdaki özellikleri ekleyebilirsiniz:
Notları renklendirme özelliği
Kategorilere göre filtreleme
Arama fonksiyonu
Notları sabitleme (pin) özelliği
Geri dönüşüm kutusu ve geri yükleme
Bulut senkronizasyonu
Biçimlendirme seçenekleri (kalın, italik, vs.)
Resim ekleme desteği
Uygulamayı çalıştırabilmek için build.gradle dosyanıza Room, Coroutines ve Material Design bağımlılıklarını eklemeyi unutmayın.
Hiç yorum yok:
Yorum Gönder