Yazdığınız uygulamanın kullanıcı sayısının artmasını sağlayan en önemli konulardan biri kullanıcı deneyimi yüksek ve dikkat çekici tasarıma sahip olmasıdır.
Bu makale Android uygulamalarda RecyclerView’a arama filtresini ekleyerek kullanıcıların hızlıca veriye ulaşabilmesini sağlayan örneği içermektedir.
Örnek projemizin görünümü:
Arama arayüz elementi olarak SearchView kullanacağız. RecyclerView yapısına da arama filtresini ekleyerek verilerimizi listeleyeceğiz.
RecyclerView kütüphanesini projeye ekleme
Projenizin ana dizinindeki build.gradle dosyasını açmalısınız. dependencies blogları arasına aşağıdaki kodu ekleyerek RecyclerView kütüphanesini yükleyelim.
1 |
implementation 'androidx.recyclerview:recyclerview:1.2.1' |
RecyclerView’e Arama Filtresi Ekleme
Android’de bir koşula göre verileri filtrelemek için Filterable adındaki sınıf kullanılır. Oluşturacağımız RecyclerView_Adapter sınıfında Filterable ve RecyclerView yapılarından kalıtım alacağız. Filterable sınıfında bulunan getFilter() metodu filtreleme işini yapan ana kod bölümüdür. RecyclerView_Adapter sınıfının kodlarında daha detaylı açıklamaları inceleyebilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
import android.content.* import android.graphics.Color import android.view.* import android.widget.* import androidx.recyclerview.widget.RecyclerView import com.smality.searchrecyclerview.databinding.RecyclerviewRowBinding import java.util.* import kotlin.collections.ArrayList class RecyclerView_Adapter(private var countryList: ArrayList<String>) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable { var countryFilterList = ArrayList<String>() lateinit var mContext: Context class CountryHolder(var viewBinding: RecyclerviewRowBinding) : RecyclerView.ViewHolder(viewBinding.root) init { countryFilterList = countryList } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val binding = RecyclerviewRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) val sch = CountryHolder(binding) mContext = parent.context return sch } override fun getItemCount(): Int { return countryFilterList.size } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val countryHolder = holder as CountryHolder //Listelenecek ülke isimlerinin ArrayList'den arayüze aktarılması countryHolder.viewBinding.selectCountryContainer.setBackgroundColor(Color.TRANSPARENT) countryHolder.viewBinding.selectCountryText.setTextColor(Color.BLACK) countryHolder.viewBinding.selectCountryText.text = countryFilterList[position] } override fun getFilter(): Filter { return object : Filter() { override fun performFiltering(constraint: CharSequence?): FilterResults { //Aranan yazı val charSearch = constraint.toString() //Arama alanında bir yazı aranmadıysa tüm ülkeleri countryFilterList aktarıyoruz if (charSearch.isEmpty()) { countryFilterList = countryList } else { val resultList = ArrayList<String>() for (row in countryList) { //Aranan ülke varsa add metodu ile listeye eklenir if (row.toLowerCase(Locale.ROOT).contains(charSearch.toLowerCase(Locale.ROOT))) { resultList.add(row) } } countryFilterList = resultList } val filterResults = FilterResults() filterResults.values = countryFilterList return filterResults } @Suppress("UNCHECKED_CAST") override fun publishResults(constraint: CharSequence?, results: FilterResults?) { countryFilterList = results?.values as ArrayList<String> notifyDataSetChanged() } } } } |
Listelenecek ülke isimlerinin arayüz elemanına aktarılması için TextView içeren recyclerview_row adında xml oluşturduk. Bu xml ile RecyclerView_Adapter sınıfı birlikte çalışmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/select_country_container" android:layout_width="match_parent" android:layout_height="57dp" android:background="@color/teal_700" android:orientation="vertical"> <TextView android:id="@+id/select_country_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_centerInParent="true" android:layout_marginLeft="16dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:text="United States ??" android:textColor="@android:color/black" android:textSize="18sp" /> </RelativeLayout> |
SearchView Özelleştirme ve Ülke Bilgilerini Oluşturup Filtreleme
SearchView arayüz elementini görsel olarak zenginleştirmek istiyorum. Bunun için SearchView arayüz elementinde içerisinde bulunan search_mag_icon (arama ikonu idsi), search_close_btn (çarpı şeklinde kapatma idsi) ve search_src_text id bilgilerinden faydalanarak arama ile ilgili ikonlara renkler atadım.
Aranacak kelime yazılırken karakter değişikliklerini dinleyen setOnQueryTextListener adında metod bulunmaktadır. Bu metod SearchView arayüz elementinin içinde barınır.
Son olarak Java’nın Locale sınıfından faydalanarak ülke isim ve bayrak bilgilerini getiren metodu yazdım. Metod sonunda da bütün ülke bilgilerini ArrayList yapısıyla RecyclerView_Adapter sınıfına aktardım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
import android.graphics.Color import android.os.Bundle import android.widget.* import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.SearchView import androidx.recyclerview.widget.* import com.smality.searchrecyclerview.databinding.ActivityMainBinding import java.util.* import kotlin.collections.ArrayList class MainActivity : AppCompatActivity() { lateinit var adapter: RecyclerView_Adapter lateinit var countryrv: RecyclerView private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) val view = binding.root setContentView(view) //SearchView de bulunan Arama iconu tanımlayıp rengini belirledim val searchIcon = binding.countrySearch.findViewById<ImageView>(R.id.search_mag_icon) searchIcon.setColorFilter(Color.BLACK) //SearchView de bulunan kapama iconunu tanımlayıp rengini belirledim val cancelIcon = binding.countrySearch.findViewById<ImageView>(R.id.search_close_btn) cancelIcon.setColorFilter(Color.BLACK) //SearchView alanında aranacak yazının rengini belirlemek için TextView tanımladım. val textView = binding.countrySearch.findViewById<TextView>(R.id.search_src_text) textView.setTextColor(Color.BLACK) countryrv = findViewById(R.id.country_rv) countryrv.layoutManager = LinearLayoutManager(countryrv.context) countryrv.setHasFixedSize(true) //Listelemedeki çizgileri oluşturmak için Recyclerview özellik ekliyorum. val decorator = DividerItemDecoration(applicationContext, LinearLayoutManager.VERTICAL) countryrv.addItemDecoration(decorator) //SearchView alanına aranacak kelime yazılırken karakter değişikliklerini dinleyen metod // Aramada kullanıcı yazarken karakter değişikliklerini dinler. binding.countrySearch.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { return false } override fun onQueryTextChange(newText: String?): Boolean { //Karakter değişikliğini RecyclerView_Adapter sınıfına aktararak filtrelemeyi sağlar adapter.filter.filter(newText) return false } }) //Ülke isim ve bayrak bilgisini getiren metodu çağırdık getListOfCountries() } private fun getListOfCountries() { //getISOCountries metodu ile tüm 2 harfli ülke kodlarının bir listesini alıyoruz val isoCountryCodes = Locale.getISOCountries() val countryListWithEmojis = ArrayList<String>() for (countryCode in isoCountryCodes) { //countryCode ile ülke isimleri elde ediyoruz val locale = Locale("", countryCode) val countryName = locale.displayCountry //countryCode ile ülkenin bayrak görsellerini String halini elde editoruz. val flagOffset = 0x1F1E6 val asciiOffset = 0x41 val firstChar = Character.codePointAt(countryCode, 0) - asciiOffset + flagOffset val secondChar = Character.codePointAt(countryCode, 1) - asciiOffset + flagOffset val flag = (String(Character.toChars(firstChar)) + String(Character.toChars(secondChar))) //Bayrak ve ülke isminide ArrayList ekliyoruz. countryListWithEmojis.add("$flag $countryName") } adapter = RecyclerView_Adapter(countryListWithEmojis) countryrv.adapter = adapter } } |
MainActivity sınıfında kullandığımız SearchView ve RecyclerView arayüz elementlerinin eklendiği xml kodlarımız:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:id="@+id/country_content_id" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/teal_700" android:orientation="vertical" tools:context=".MainActivity"> <androidx.appcompat.widget.SearchView android:id="@+id/country_search" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/teal_200" android:textCursorDrawable="@null" app:iconifiedByDefault="false" app:queryBackground="@null" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/country_rv" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/teal_700" /> </LinearLayout> |
Böylelikle RecyclerView’a arama filtresini ekleyerek kullanımını örnekledim.
Listelenen ülke isimlerine tıkladığınızda başka bir sayfada seçilen ülke ismi ve bayrağını göstermek isterseniz projedeki DetailsActivity adında sınıfı inceleyebilirsiniz. Yalnız RecyclerView_Adapter sınıfında 42-46 satırlar arasındaki kodları da eklemeniz gerekir:)
Tüm proje kodlarına github linkinden ulaşabilirsiniz.