Android AutoCompleteTextView: Gelişmiş Otomatik Tamamlama Alanları Oluşturma Rehberi
Android uygulamalarında kullanıcı deneyimini zenginleştirmenin ve veri girişini kolaylaştırmanın en etkili yollarından biri, otomatik tamamlama özellikli metin giriş alanları sunmaktır. Bu tür alanlar, kullanıcı bir şeyler yazmaya başladığında ilgili önerileri göstererek hem zaman kazandırır hem de olası yazım hatalarını azaltır. Android SDK, bu ihtiyacı karşılamak üzere AutoCompleteTextView bileşenini sunar. Bu kapsamlı rehberde, AutoCompleteTextView‘in temel kullanımından gelişmiş özelleştirmelere, asenkron veri yüklemeden çoklu seçim senaryolarına kadar her yönünü detaylı bir şekilde inceleyeceğiz.
AutoCompleteTextView Nedir ve Neden Kullanılır?
AutoCompleteTextView, EditText sınıfından türetilmiş özel bir metin giriş bileşenidir. Temel farkı, kullanıcının yazmaya başladığı metne göre bir dizi öneri sunabilmesidir. Bu öneriler, bir açılır listede (dropdown) gösterilir ve kullanıcı bu listeden bir öğe seçerek metin kutusunu kolayca doldurabilir.
Kullanım Alanları:
* Arama Çubukları: Uygulama içi içeriklerde veya internet üzerinde arama yaparken kullanıcının arama terimlerini tamamlamak.
* Form Doldurma: Ülke, şehir, eyalet gibi standart veri setlerinden seçim yapmayı gerektiren form alanları.
* Etiketleme/Kategorilendirme: Bir öğeye etiket eklerken mevcut etiketleri önermek.
* Kullanıcı Adı/E-posta Girişi: Daha önce girilmiş veya kaydedilmiş kullanıcı adlarını/e-postaları önermek.
* Dinamik Veri Girişi: API’lerden veya veritabanlarından çekilen dinamik verileri kullanıcıya sunmak.
AutoCompleteTextView kullanarak, uygulamanızın daha sezgisel, hızlı ve hatasız bir kullanıcı deneyimi sunmasını sağlayabilirsiniz.
Temel Yapı ve Bileşenler
AutoCompleteTextView‘i etkili bir şekilde kullanabilmek için bazı temel bileşenleri ve kavramları anlamak önemlidir:
AutoCompleteTextView Bileşeni
Bu, XML layout dosyanızda tanımladığınız ve kullanıcının metin girdiği ana bileşendir. Önerileri göstermek için bir Adapter‘a ihtiyaç duyar.
Adapter Kavramı
Android’deki birçok liste tabanlı görünüm (ListView, RecyclerView, Spinner vb.) gibi, AutoCompleteTextView de öneri listesini doldurmak için bir Adapter kullanır. Adapter, veri kaynağı ile görünüm arasında bir köprü görevi görür. AutoCompleteTextView için genellikle aşağıdaki adapter türleri kullanılır:
* ArrayAdapter: Basit String dizileri veya List gibi veri yapıları için en yaygın ve basit adapter’dır. Verileri bir TextView içinde gösterir.
* SimpleCursorAdapter: Veritabanı sorgularından dönen Cursor nesneleriyle çalışmak için kullanılır. Verileri belirli sütunlardan alıp belirli TextView‘lara eşler.
* BaseAdapter veya Özel Adapter’lar: Daha karmaşık veri yapıları (örneğin, resim ve metin içeren öğeler) veya özel filtreleme mantığı gerektiğinde kendi adapter’ınızı BaseAdapter‘dan türeterek oluşturabilirsiniz.
Veri Kaynakları
AutoCompleteTextView‘e öneri olarak sunulacak veriler çeşitli kaynaklardan gelebilir:
* String Dizileri/Listeleri: Sabit ve küçük veri setleri için idealdir.
* Custom Nesneler: Daha karmaşık bilgiler içeren (ID, isim, açıklama gibi) kendi tanımladığınız nesnelerin listeleri.
* Veritabanları: Uygulama içi bir SQLite veritabanından çekilen veriler.
* Web Servisleri/API’ler: Uzak sunuculardan dinamik olarak çekilen veriler (ülkeler, şehirler, ürünler vb.).
Basit Bir AutoCompleteTextView Uygulaması
Şimdi, AutoCompleteTextView‘in nasıl çalıştığını anlamak için basit bir örnekle başlayalım. Bu örnekte, bir dizi ülke adını kullanıcıya otomatik tamamlama önerileri olarak sunacağız.
1. Layout Dosyası (activity_main.xml)
Açıklamalar:
* android:id: Bu bileşene kod tarafında erişmek için benzersiz bir kimlik.
* android:hint: Metin kutusu boşken görünen ipucu metni.
* android:completionThreshold: Kullanıcının kaç karakter yazdıktan sonra önerilerin gösterilmeye başlanacağını belirler. Burada 1 olarak ayarlandığı için, kullanıcı tek bir karakter yazdığında bile öneriler belirecektir.
2. Java/Kotlin Kodu (MainActivity)
// Java
package com.example.autocompletetutorial;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private AutoCompleteTextView autoCompleteTextView;
private String[] countries = {"Türkiye", "Almanya", "Fransa", "İspanya", "İtalya",
"İngiltere", "Amerika Birleşik Devletleri", "Kanada",
"Meksika", "Brezilya", "Arjantin", "Çin", "Japonya",
"Hindistan", "Avustralya", "Yeni Zelanda", "Güney Afrika"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
autoCompleteTextView = findViewById(R.id.autoCompleteTextView);
// ArrayAdapter oluşturma
// 1. parametre: Context
// 2. parametre: Her bir öneri öğesinin layout'u (Android'in hazır layout'larından biri)
// 3. parametre: Veri kaynağı
ArrayAdapter adapter = new ArrayAdapter<>(
this,
android.R.layout.simple_dropdown_item_1line,
countries
);
// AutoCompleteTextView'e adapter'ı atama
autoCompleteTextView.setAdapter(adapter);
// Opsiyonel: Kullanıcı bir öğeyi seçtiğinde Toast mesajı gösterme
autoCompleteTextView.setOnItemClickListener((parent, view, position, id) -> {
String selectedCountry = (String) parent.getItemAtPosition(position);
Toast.makeText(MainActivity.this, "Seçilen Ülke: " + selectedCountry, Toast.LENGTH_SHORT).show();
});
}
}
// Kotlin
package com.example.autocompletetutorial
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.Toast
class MainActivity : AppCompatActivity() {
private lateinit var autoCompleteTextView: AutoCompleteTextView
private val countries = arrayOf(
"Türkiye", "Almanya", "Fransa", "İspanya", "İtalya",
"İngiltere", "Amerika Birleşik Devletleri", "Kanada",
"Meksika", "Brezilya", "Arjantin", "Çin", "Japonya",
"Hindistan", "Avustralya", "Yeni Zelanda", "Güney Afrika"
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
autoCompleteTextView = findViewById(R.id.autoCompleteTextView)
// ArrayAdapter oluşturma
// 1. parametre: Context
// 2. parametre: Her bir öneri öğesinin layout'u (Android'in hazır layout'larından biri)
// 3. parametre: Veri kaynağı
val adapter = ArrayAdapter(
this,
android.R.layout.simple_dropdown_item_1line,
countries
)
// AutoCompleteTextView'e adapter'ı atama
autoCompleteTextView.setAdapter(adapter)
// Opsiyonel: Kullanıcı bir öğeyi seçtiğinde Toast mesajı gösterme
autoCompleteTextView.setOnItemClickListener { parent, view, position, id ->
val selectedCountry = parent.getItemAtPosition(position) as String
Toast.makeText(this, "Seçilen Ülke: $selectedCountry", Toast.LENGTH_SHORT).show()
}
}
}
Açıklamalar:
* ArrayAdapter: countries dizisindeki String türündeki verileri yönetmek için bir ArrayAdapter oluşturduk.
* android.R.layout.simple_dropdown_item_1line: Bu, Android’in sağladığı basit bir layout dosyasıdır. Her bir öneri öğesini tek satırlık bir metin olarak gösterir. Kendi özel layout’unuzu da kullanabilirsiniz (ilerleyen bölümlerde göreceğiz).
* setAdapter(): Oluşturduğumuz adapter’ı autoCompleteTextView‘e atayarak veri kaynağını ve görünüm formatını tanımladık.
* setOnItemClickListener(): Kullanıcı açılır listeden bir öğeyi tıkladığında tetiklenecek bir dinleyici ekledik. Bu dinleyici sayesinde seçilen öğenin değerine erişebiliriz.
Bu basit kurulum ile uygulamanızı çalıştırdığınızda, AutoCompleteTextView‘e bir harf yazmaya başladığınızda ilgili ülke adlarının bir listesini göreceksiniz.
AutoCompleteTextView’i Özelleştirme
AutoCompleteTextView, görünümünü ve davranışını özelleştirmek için çeşitli özellikler sunar.
1. completionThreshold
Bu özelliği daha önce gördük, ancak önemini vurgulamakta fayda var. android:completionThreshold="N" değeri, kullanıcının metin kutusuna N karakter yazdıktan sonra önerilerin gösterilmeye başlanacağını belirtir. Küçük değerler (1 veya 2 gibi) genellikle kullanıcı deneyimini iyileştirir, ancak çok büyük veri setleri için performansı etkileyebilir.
2. Hint Metni
android:hint="Ör: Türkiye" özelliği, metin kutusu boşken kullanıcıya ne tür bir giriş beklendiğini gösteren gri metni ayarlar.
3. Açılır Liste (Dropdown) Görünümünü Özelleştirme
Açılır listenin kendisinin görünümünü kontrol eden bazı özellikler:
* android:dropDownWidth: Açılır listenin genişliğini belirler. match_parent, wrap_content veya belirli bir dp değeri olabilir.
* android:dropDownAnchor: Açılır listenin hangi görünümün altına demirleneceğini belirler. Genellikle AutoCompleteTextView‘in kendi ID’si verilir.
* android:dropDownVerticalOffset: Açılır listenin demirleme görünümünden dikey olarak ne kadar kaydırılacağını belirler.
* android:dropDownHorizontalOffset: Açılır listenin demirleme görünümünden yatay olarak ne kadar kaydırılacağını belirler.
* android:popupBackground: Açılır listenin arka planını özelleştirmek için bir renk veya drawable kaynağı belirler.
Örnek kullanım:
4. Açılır Liste Öğe Düzenini Özelleştirme (Custom Layout)
android.R.layout.simple_dropdown_item_1line basit bir TextView kullanır. Ancak, öneri öğelerini daha zengin göstermek isteyebilirsiniz (örneğin, bir resim ve iki satır metin). Bunun için kendi özel layout’unuzu ve bir özel adapter’ı kullanmanız gerekir.
Adım 1: Özel Liste Öğesi Layout’u Oluşturma (list_item_autocomplete.xml)
res/layout klasöründe yeni bir XML dosyası oluşturun:
Bu örnekte, her bir öneri öğesi bir bayrak ikonu (ImageView) ve ülke adını (TextView) içerecektir. ic_flag_placeholder drawable’ını res/drawable klasörünüze eklemeniz gerekebilir.
Adım 2: Özel ArrayAdapter Oluşturma (CustomCountryAdapter.java/kt)
Şimdi, bu özel layout’u kullanacak bir adapter oluşturmalıyız. Bu adapter, ArrayAdapter‘dan türetilebilir ve getView metodunu override ederek kendi layout’umuzu şişirebiliriz.
// Java
package com.example.autocompletetutorial;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.List;
public class CustomCountryAdapter extends ArrayAdapter {
private List countries;
private Context context;
public CustomCountryAdapter(@NonNull Context context, @NonNull List countries) {
super(context, 0, countries);
this.context = context;
this.countries = countries;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(
R.layout.list_item_autocomplete, parent, false);
}
ImageView imageViewFlag = convertView.findViewById(R.id.imageViewFlag);
TextView textViewCountryName = convertView.findViewById(R.id.textViewCountryName);
String countryName = getItem(position);
if (countryName != null) {
textViewCountryName.setText(countryName);
// Bayrak ikonunu dinamik olarak ayarlamak için bir mantık eklenebilir.
// Örneğin, ülke adına göre bir drawable kaynağı bulma.
// Şimdilik varsayılan bir ikon kullanıyoruz.
imageViewFlag.setImageResource(R.drawable.ic_flag_placeholder);
}
return convertView;
}
// AutoCompleteTextView'in filtreleme mekanizması için
// getItem() ve getCount() metodları ArrayAdapter tarafından zaten sağlanır.
// Ancak daha karmaşık filtreleme için getFilter() metodunu override etmek gerekebilir.
}
// Kotlin
package com.example.autocompletetutorial
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.TextView
class CustomCountryAdapter(context: Context, private val countries: List) :
ArrayAdapter(context, 0, countries) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context)
.inflate(R.layout.list_item_autocomplete, parent, false)
val imageViewFlag: ImageView = view.findViewById(R.id.imageViewFlag)
val textViewCountryName: TextView = view.findViewById(R.id.textViewCountryName)
val countryName = getItem(position)
countryName?.let {
textViewCountryName.text = it
// Bayrak ikonunu dinamik olarak ayarlamak için bir mantık eklenebilir.
// Örneğin, ülke adına göre bir drawable kaynağı bulma.
// Şimdilik varsayılan bir ikon kullanıyoruz.
imageViewFlag.setImageResource(R.drawable.ic_flag_placeholder)
}
return view
}
}
Adım 3: MainActivity’de Özel Adapter’ı Kullanma
MainActivity‘deki adapter oluşturma kısmını yeni özel adapter’ınızla değiştirin:
// Java
// ...
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
// ...
private List countryList = Arrays.asList(countries); // String[]'i List'e dönüştürdük
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
autoCompleteTextView = findViewById(R.id.autoCompleteTextView);
// Özel ArrayAdapter'ı kullanma
CustomCountryAdapter adapter = new CustomCountryAdapter(this, countryList);
autoCompleteTextView.setAdapter(adapter);
// ... onItemClickListener aynı kalır ...
}
}
// Kotlin
// ...
class MainActivity : AppCompatActivity() {
// ...
private val countryList: List = countries.toList() // Array'i List'e dönüştürdük
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
autoCompleteTextView = findViewById(R.id.autoCompleteTextView)
// Özel ArrayAdapter'ı kullanma
val adapter = CustomCountryAdapter(this, countryList)
autoCompleteTextView.setAdapter(adapter)
// ... onItemClickListener aynı kalır ...
}
}
Artık AutoCompleteTextView‘iniz, her bir öneri öğesini özel olarak tasarladığınız layout ile gösterecektir.
Seçim ve Metin Değişikliklerini Yönetme
Kullanıcının AutoCompleteTextView ile etkileşimini yönetmek için iki ana dinleyici (listener) kullanılır:
1. OnItemClickListener
Bu dinleyici, kullanıcı açılır listeden bir öğeyi seçtiğinde tetiklenir. AutoCompleteTextView‘e doğrudan atanır.
// Java
autoCompleteTextView.setOnItemClickListener((parent, view, position, id) -> {
String selectedItem = (String) parent.getItemAtPosition(position);
Toast.makeText(MainActivity.this, "Seçilen: " + selectedItem, Toast.LENGTH_SHORT).show();
// Seçilen öğe ile ilgili işlemleri burada yapabilirsiniz.
});
// Kotlin
autoCompleteTextView.setOnItemClickListener { parent, view, position, id ->
val selectedItem = parent.getItemAtPosition(position) as String
Toast.makeText(this, "Seçilen: $selectedItem", Toast.LENGTH_SHORT).show()
// Seçilen öğe ile ilgili işlemleri burada yapabilirsiniz.
}
parent.getItemAtPosition(position) metodu, seçilen öğenin veri objesini döndürür. Bu objeyi kendi veri tipinize (örneğin String veya özel bir Country objesi) dönüştürebilirsiniz.
2. TextWatcher
Bu dinleyici, AutoCompleteTextView‘deki metin her değiştiğinde (kullanıcı her karakter yazdığında veya sildiğinde) tetiklenir. Özellikle dinamik filtreleme, anlık doğrulama veya diğer metin tabanlı işlemler için kullanışlıdır. AutoCompleteTextView‘e addTextChangedListener() metodu ile eklenir.
// Java
import android.text.Editable;
import android.text.TextWatcher;
// ...
autoCompleteTextView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Metin değişmeden önce
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Metin değiştiğinde (her karakter girişinde/silinişinde)
// Burada s, o anki metin kutusundaki CharSequence'i temsil eder.
// Eğer adapter'ınızın filtreleme mekanizmasını manuel olarak tetiklemek isterseniz,
// adapter.getFilter().filter(s); çağrısı yapabilirsiniz.
}
@Override
public void afterTextChanged(Editable s) {
// Metin değiştikten sonra
// s.toString() ile metnin son haline erişebilirsiniz.
}
});
// Kotlin
import android.text.Editable
import android.text.TextWatcher
// ...
autoCompleteTextView.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// Metin değişmeden önce
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// Metin değiştiğinde (her karakter girişinde/silinişinde)
// Burada s, o anki metin kutusundaki CharSequence'i temsil eder.
// Eğer adapter'ınızın filtreleme mekanizmasını manuel olarak tetiklemek isterseniz,
// (adapter as? Filterable)?.filter?.filter(s) çağrısı yapabilirsiniz.
}
override fun afterTextChanged(s: Editable?) {
// Metin değiştikten sonra
// s.toString() ile metnin son haline erişebilirsiniz.
}
})
ArrayAdapter ve CustomCountryAdapter gibi sınıflar, Filterable arayüzünü zaten uyguladıkları için onTextChanged içinde manuel olarak filter() metodunu çağırmanıza gerek kalmaz. AutoCompleteTextView otomatik olarak bu adapter’ların filtreleme mekanizmasını tetikler. Ancak, çok özel filtreleme gereksinimleriniz varsa TextWatcher içinde kendi filtreleme mantığınızı uygulayabilirsiniz.
Gelişmiş Senaryolar: Dinamik Veri Kaynakları ve Asenkron İşlemler
Gerçek dünya uygulamalarında öneri verileri genellikle statik bir dizi yerine bir veritabanından veya bir web servisinden dinamik olarak çekilir. Bu tür durumlarda asenkron programlama ve performans yönetimi kritik öneme sahiptir.
Web Servisinden Veri Çekme
Bir web servisinden veri çekmek, ağ istekleri içerdiği için asenkron olmalıdır. Aksi takdirde, ana UI iş parçacığını (main thread) bloke ederek uygulamanın donmasına neden olabilirsiniz. Modern Android geliştirme yaklaşımları, ViewModel, LiveData ve Kotlin Coroutines’i bu tür görevler için önermektedir.
1. Veri Modeli Oluşturma
Öncelikle, web servisinden gelecek verileri temsil edecek bir veri sınıfı (data class) tanımlayalım.
// Kotlin
// data class Country (örneğin, bir API'den gelen veri için)
data class Country(val id: String, val name: String, val code: String)
2. Custom Filterable Adapter Oluşturma
Bu adapter, Filterable arayüzünü uygulayacak ve getFilter() metodunu override ederek kendi filtreleme mantığımızı sağlayacaktır. Filtreleme işlemi, web servisinden veri çekme işlemini tetikleyecektir.
// Kotlin
// CustomFilterableAdapter.kt
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Filter
import android.widget.Filterable
import android.widget.TextView
class CustomFilterableAdapter(
context: Context,
private val onFilter: (String) -> Unit // Filtreleme istendiğinde çağrılacak lambda
) : ArrayAdapter(context, 0), Filterable {
private var currentSuggestions: List = emptyList()
fun setSuggestions(suggestions: List) {
currentSuggestions = suggestions
clear() // Mevcut öğeleri temizle
addAll(suggestions) // Yeni önerileri ekle
notifyDataSetChanged() // Adapter'ı güncelle
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context)
.inflate(android.R.layout.simple_dropdown_item_1line, parent, false)
val country = getItem(position)
(view as TextView).text = country?.name
return view
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
// Bu metot arka planda çalışır.
// Constraint, kullanıcının yazdığı metindir.
// Biz burada asenkron bir çağrı tetikleyeceğiz.
val query = constraint?.toString() ?: ""
onFilter(query) // ViewModel'a filtreleme isteğini gönder
return FilterResults().apply {
values = currentSuggestions
count = currentSuggestions.size
}
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
// Bu metot UI thread'de çalışır.
// setSuggestions() metodu zaten notifyDataSetChanged() çağrısı yapıyor,
// bu yüzden burada ekstra bir şey yapmaya gerek yok.
}
override fun convertResultToString(resultValue: Any?): CharSequence {
// Kullanıcı listeden bir öğeyi seçtiğinde AutoCompleteTextView'e yazılacak metin.
return (resultValue as? Country)?.name ?: ""
}
}
}
}
3. ViewModel ile Veri Yönetimi
ViewModel, UI ile iş mantığını ayırmak ve konfigürasyon değişikliklerinde (ekran döndürme gibi) verilerin kaybolmamasını sağlamak için kullanılır.
// Kotlin
// SearchViewModel.kt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class SearchViewModel : ViewModel() {
private val _searchResults = MutableLiveData>()
val searchResults: LiveData> = _searchResults
private var searchJob: Job? = null
fun searchCountries(query: String) {
searchJob?.cancel() // Önceki arama işini iptal et
if (query.isBlank()) {
_searchResults.value = emptyList()
return
}
searchJob = viewModelScope.launch {
delay(300) // Debounce: Kullanıcının yazmayı bitirmesini bekle (300ms)
// Simüle edilmiş ağ isteği
val results = simulateApiCall(query)
_searchResults.value = results
}
}
private fun simulateApiCall(query: String): List {
// Gerçek bir API çağrısı burada yapılmalı (Retrofit vb. ile)
// Şimdilik sabit bir veri setinden filtreleme yapıyoruz.
val allCountries = listOf(
Country("TR", "Türkiye", "TR"),
Country("DE", "Almanya", "DE"),
Country("FR", "Fransa", "FR"),
Country("US", "Amerika Birleşik Devletleri", "US"),
Country("CA", "Kanada", "CA"),
Country("JP", "Japonya", "JP"),
Country("CN", "Çin", "CN"),
Country("IN", "Hindistan", "IN"),
Country("AU", "Avustralya", "AU")
)
return allCountries.filter {
it.name.contains(query, ignoreCase = true) ||
it.code.contains(query, ignoreCase = true)
}
}
}
Debounce Kavramı: delay(300) satırı bir “debounce” mekanizmasıdır. Kullanıcı her karakter yazdığında API çağrısı yapmak yerine, kullanıcının yazmayı bitirmesi için kısa bir süre bekleriz. Bu, gereksiz ağ isteklerini önleyerek performansı artırır ve sunucu yükünü azaltır.
4. MainActivity’de ViewModel ve Adapter’ı Bağlama
// Kotlin
// MainActivity.kt
package com.example.autocompletetutorial
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.AutoCompleteTextView
import android.widget.Toast
import androidx.activity.viewModels
class MainActivity : AppCompatActivity() {
private lateinit var autoCompleteTextView: AutoCompleteTextView
private lateinit var countryAdapter: CustomFilterableAdapter
private val searchViewModel: SearchViewModel by viewModels() // ViewModel'ı başlat
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
autoCompleteTextView = findViewById(R.id.autoCompleteTextView)
// Adapter'ı başlatırken filtreleme işini ViewModel'a delege ediyoruz.
countryAdapter = CustomFilterableAdapter(this) { query ->
searchViewModel.searchCountries(query)
}
autoCompleteTextView.setAdapter(countryAdapter)
autoCompleteTextView.completionThreshold = 1
// ViewModel'daki searchResults LiveData'sını gözlemle
searchViewModel.searchResults.observe(this) { countries ->
countryAdapter.setSuggestions(countries) // Yeni önerileri adapter'a gönder
}
autoCompleteTextView.setOnItemClickListener { parent, view, position, id ->
val selectedCountry = parent.getItemAtPosition(position) as Country
Toast.makeText(this, "Seçilen Ülke: ${selectedCountry.name}", Toast.LENGTH_SHORT).show()
}
}
}
Bu kurulum ile, kullanıcı AutoCompleteTextView‘e yazdığında:
1. CustomFilterableAdapter‘ın getFilter().performFiltering() metodu tetiklenir.
2. Bu metot, searchViewModel.searchCountries() çağrısı yaparak filtreleme isteğini ViewModel‘a iletir.
3. ViewModel, bir delay ile debounce uyguladıktan sonra simüle edilmiş API çağrısını yapar.
4. API’den gelen sonuçlar _searchResults MutableLiveData‘sına atanır.
5. MainActivity, searchResults‘ı gözlemlediği için, yeni veriler geldiğinde countryAdapter.setSuggestions() metodunu çağırarak UI’yı günceller.
Bu yapı, UI’ın responsive kalmasını sağlar ve karmaşık veri akışını yönetmek için modern Android mimarisini kullanır. Gerçek bir API entegrasyonu için Retrofit, OkHttp gibi kütüphaneleri kullanabilirsiniz.
Veritabanından Veri Çekme (Room ile)
Eğer verileriniz yerel bir veritabanında (örneğin Room) saklanıyorsa, benzer bir asenkron yaklaşım izleyebilirsiniz. ViewModel ve LiveData yine merkezi rol oynar. Room’un DAO (Data Access Object) metotları genellikle suspend fonksiyonlar olarak tanımlanır ve Coroutines ile kolayca çağrılabilir.
Örnek olarak, CountryDao‘nuzda şöyle bir metot olabilir:
// Kotlin
@Query("SELECT * FROM countries WHERE name LIKE '%' || :query || '%' OR code LIKE '%' || :query || '%'")
suspend fun searchCountries(query: String): List
SearchViewModel içindeki simulateApiCall yerine bu DAO metodunu çağırırsınız.
MultiAutoCompleteTextView: Çoklu Seçimler İçin
Bazen kullanıcıların tek bir alana birden fazla öğe girmesini isteyebilirsiniz (örneğin, bir e-posta uygulamasında alıcı listesi veya bir gönderiye birden fazla etiket ekleme). MultiAutoCompleteTextView tam da bu senaryo için tasarlanmıştır. AutoCompleteTextView‘den türetilmiştir ve bir Tokenizer kullanarak giriş metnini ayrı “token”lara (belirteçlere) böler.
Kullanım
1. Layout Dosyası: AutoCompleteTextView yerine MultiAutoCompleteTextView kullanın.
2. Java/Kotlin Kodu: Bir Tokenizer atamanız gerekir. En yaygın olanı CommaTokenizer‘dır, metni virgüllerle ayırır.
// Java
import android.widget.MultiAutoCompleteTextView;
// ...
MultiAutoCompleteTextView multiAutoCompleteTextView = findViewById(R.id.multiAutoCompleteTextView);
ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, new String[]{"ali@example.com", "veli@example.com", "ayse@example.com"});
multiAutoCompleteTextView.setAdapter(adapter);
multiAutoCompleteTextView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
// Kotlin
import android.widget.MultiAutoCompleteTextView
// ...
val multiAutoCompleteTextView: MultiAutoCompleteTextView = findViewById(R.id.multiAutoCompleteTextView)
val adapter = ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, arrayOf("ali@example.com", "veli@example.com", "ayse@example.com"))
multiAutoCompleteTextView.setAdapter(adapter)
multiAutoCompleteTextView.setTokenizer(MultiAutoCompleteTextView.CommaTokenizer())
Kullanıcı MultiAutoCompleteTextView‘e “ali@example.com, v” yazdığında, “v” için öneriler gösterilir ve seçildiğinde “ali@example.com, veli@example.com,” şeklinde metin kutusuna eklenir.
En İyi Uygulamalar ve İpuçları
AutoCompleteTextView‘i kullanırken kullanıcı deneyimini ve performansı optimize etmek için bazı önemli noktalar:
* Performans (Büyük Veri Setleri ve Ağ İstekleri):
* Debounce: Özellikle ağ istekleri yaparken, TextWatcher içinde bir delay kullanarak kullanıcının yazmayı bitirmesini bekleyin. Bu, gereksiz istekleri ve sunucu yükünü azaltır.
* Arka Plan İşlemleri: Ağ istekleri veya yoğun veritabanı sorguları gibi uzun süren işlemleri her zaman ana iş parçacığı dışında (Coroutines, RxJava, ViewModel + LiveData gibi) gerçekleştirin.
* Veri Boyutu: Mümkünse, öneri listesini sunucu tarafında filtreleyerek veya sayfalayarak istemciye gönderilen veri miktarını azaltın.
* Lazy Loading: Tüm veri setini bir kerede yüklemek yerine, yalnızca ihtiyaç duyulduğunda veya kullanıcı belirli bir eşiği aştığında verileri yükleyin.
* Kullanıcı Deneyimi (UX):
* completionThreshold: Makul bir değer seçin (genellikle 1 veya 2). Çok yüksek bir değer, kullanıcının otomatik tamamlama özelliğinden faydalanmasını engeller.
* Hint Metni: Kullanıcıya ne tür bir giriş beklendiğini açıkça belirten bir ipucu metni (android:hint) kullanın.
* Boş Durum ve Hata Yönetimi: Arama sonuçları bulunamadığında veya bir ağ hatası oluştuğunda kullanıcıya bilgilendirici mesajlar gösterin.
* Klavye Yönetimi: Kullanıcı bir öğeyi seçtiğinde klavyeyi otomatik olarak kapatmayı düşünebilirsiniz.
* Görsel Geri Bildirim: Ağdan veri yüklenirken bir ProgressBar veya benzeri bir yükleme göstergesi sunarak kullanıcıya işlemin devam ettiğini belirtin.
* Dokunma Alanı: Açılır listedeki öğelerin yeterince büyük bir dokunma alanına sahip olduğundan emin olun.
* Erişilebilirlik:
* contentDescription: Önemli görünümler için erişilebilirlik açıklamaları sağlayın.
* Klavye Navigasyonu: AutoCompleteTextView‘in klavye ile gezinilebilir olduğundan emin olun.
* InputType: Doğru klavye türünü (android:inputType) ayarlayarak kullanıcının veri girişini kolaylaştırın (örneğin, textEmailAddress, textPersonName, textAutoComplete).
* Bellek Yönetimi: Özellikle özel adapter’lar kullanırken, convertView‘i yeniden kullanarak ve ViewHolder desenini uygulayarak liste öğelerinin şişirilmesini ve görünüm aramalarını optimize edin.
Sonuç
AutoCompleteTextView, Android uygulamalarında kullanıcı etkileşimini ve veri girişini önemli ölçüde geliştiren güçlü ve esnek bir bileşendir. Bu rehberde, basit statik veri setlerinden dinamik web servis entegrasyonlarına, temel özelleştirmelerden MultiAutoCompleteTextView gibi gelişmiş senaryolara kadar AutoCompleteTextView‘in tüm yönlerini ele aldık.
Doğru adapter’ı seçerek, performans ipuçlarını uygulayarak ve kullanıcı deneyimini ön planda tutarak, uygulamanızda verimli ve kullanıcı dostu otomatik tamamlama alanları oluşturabilirsiniz. Unutmayın, modern Android geliştirme prensiplerini (ViewModel, LiveData, Coroutines) benimsemek, özellikle asenkron veri kaynaklarıyla çalışırken kodunuzu daha temiz, sürdürülebilir ve hatasız hale getirecektir. Bu bilgileri kullanarak kendi uygulamalarınızda güçlü otomatik tamamlama özellikleri geliştirmeye hazırsınız!