Android Programming 201 : Lists

ibrahimcanerdogan
6 min readNov 23, 2022

--

Photo by Pathum Danthanarayana on Unsplash

In this section, we will learn the “Listing” process, which is both the basic and very important subject of Android programming.

Adapter

Views whose content changes dynamically while the application is running need adapter object. In general, the content is populated dynamically in list operations.

If there was no dynamic filling, the application would try to fill even the list element that the user did not see, thus causing performance problems.

Adapter objects provide the operations of filling the source data into the list structures and the management of the data visible on the screen.

A simple adapter can be created as follows.

val adapter = ArrayAdapter<String>(
this,
com.google.android.material.R.layout.support_simple_spinner_dropdown_item,
arrayOf("Eleman1", "Eleman2", "Eleman3")
)
Photo by NASA on Unsplash

Spinner

Spinner has a drop-down list when pressed. Operations can be performed by selecting elements from the list.

After adding a “Spinner” component on the View;

var letters = arrayListOf<String>("A", "B", "C")
val adapter = ArrayAdapter<String>(
this,
com.google.android.material.R.layout.support_simple_spinner_dropdown_item,
letters
)

val spin = findViewById<Spinner>(R.id.spinner)
spin.adapter = adapter

Or if we want to pull data from a “<string-array>” built on a strings.xml;

val adapter = ArrayAdapter.createFromResource(
this,
R.array.countries_array,
android.R.layout.simple_spinner_item
)

adapter.setDropDownViewResource(
androidx.appcompat.R.layout.support_simple_spinner_dropdown_item
)

val spin = findViewById<Spinner>(R.id.spinner)
spin.adapter = adapter
Photo by NASA on Unsplash

ListView

ListView is a view structure that allows listing elements one after the other and flowing with scroll.

Unlike the Spinner component, the ListView has a constantly visible list structure.

After adding “ListView” component on View;

val listview = findViewById<ListView>(R.id.listview)
var list = ArrayList<String>()

for (i in 1..50){
list.add("Item $i")
}

val adapter = ArrayAdapter<String>(
this,
com.google.android.material.R.layout.support_simple_spinner_dropdown_item,
list
)

listview.adapter = adapter
Photo by SpaceX on Unsplash

RecyclerView

ListView poses performance issues when moving lists containing larger data and designs. Accordingly, RecyclerView is used in the listing process for big data and designs.

Thanks to its LayoutManager structure, RecyclerView allows us to create the ordering and flow of elements in different ways.

Thanks to the “LinearLayoutManager”, “GridLayoutManager”, “StaggeredGridLayoutManager” it contains in the LayoutManager, different and modern listings can be made.

RecyclerView Requirements

RecyclerView has some requirements before it can be used in a project.

  • First of all, it should be added as a “dependencies” in build.gradle.
  • Thanks to the ViewHolder, the image of each element can be created. RecyclerView needs ViewHolder as a necessity.
  • A LayoutManager structure should be created to determine the flows. A default LayoutManager can be created as well as the use of a default LayoutManager.
  • With RecyclerViewAdapter, data is filled and managed in RecyclerView.

ViewHolder

ViewHolder is the structure in which we perform matching operations with ID in Adapter works. Definitions and adjustments of objects that the user sees on the screen are performed in this class.

A ViewHolder class in its simplest form looks like this:

class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
lateinit var textView: TextView

init {
textView = itemView.findViewById(R.id.textV)
}
}

As you can see, we are connecting the components in the .xml file that we created over the view it contains.

Adapter

Thanks to the Adapter, we will perform the filling process of the components kept under the ViewHolder header. Therefore, when an Adapter class is created over RecyclerView.Adapter, ViewHolder is mandatory.

For example, an Adapter class looks like this:

class RVAdapter(var list: ArrayList<String>): RecyclerView.Adapter<ViewHolder>(){

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
TODO("Not yet implemented")
}

override fun getItemCount(): Int {
TODO("Not yet implemented")
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
TODO("Not yet implemented")
}
}
  • onCreateViewHolder
  • onBindViewHolder: Method to load the data of the created ViewHolder object
  • getItemCount: It is the method to return how many data will be filled.

This class is as follows;

class RVAdapter(var list: ArrayList<String>): RecyclerView.Adapter<ViewHolder>(){

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater
.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return ViewHolder(v)
}

override fun getItemCount(): Int {
return list.size
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.text = list[position]
}
}

View

The Adapter class created to display the data on the Activity is defined. This adapter is assigned to the RecyclerView component created on the View. In addition, RecyclerView assignment is made in the LayourManager specified in the requirements.

class MainActivity : AppCompatActivity() {
val countries = ArrayList<String>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)

countries.add("Turkey")
countries.add("England")

val adapter = RVAdapter(countries)
recyclerView.adapter = adapter

val layoutManager = LinearLayoutManager(this)
layoutManager.orientation = LinearLayoutManager.VERTICAL
layoutManager.scrollToPosition(0)
recyclerView.layoutManager = layoutManager

recyclerView.addItemDecoration(
DividerItemDecoration(this, layoutManager.orientation)
)
}
}

Since RecyclerView lists big data, “Paginiation” should be done in order to keep the performance optimal and to be user-friendly! In this way, it is aimed to load data as you scroll down the list.

Photo by Shot by Cerqueira on Unsplash

RecyclerView — Filtering

SearchView component must be placed on .xml for filtering. The data in the list is instantly filtered through the text written on the SearchView.

Adapter class is used in RecyclerView list for filtering. The “Filterable” property is implemented in the Adapter class. Then the list holding the filtered data to be used for the whole listing process is created and the whole list is added to this list at the beginning. The filtering is overridden with the “getFilter()” function to complete the list.

  • performFiltering: This method determines how to filter.
  • publishResults: Thanks to this method, how the results will be displayed and how they will be completed is given information.
class RVAdapter(var list: ArrayList<String>): RecyclerView.Adapter<ViewHolder>(), 
Filterable {
val filteredList = ArrayList<String>()

init {
filteredList.addAll(list)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater
.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return ViewHolder(v)
}

override fun getItemCount(): Int {
return filteredList.size
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.text = filteredList[position]
}

override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(p0: CharSequence?): FilterResults {
val search = p0.toString()
filteredList.clear()

if (search.isEmpty()) {
filteredList.addAll(list)
} else {
for (member in list) {
if (member.toLowerCase(Locale.ROOT).contains(search)) {
filteredList.add(member)
}
}
}

val filterResult = FilterResults()
filterResult.values = filteredList
return filterResult
}

override fun publishResults(p0: CharSequence?, p1: FilterResults?) {
notifyDataSetChanged()
}

}
}
}

The words required for filtering must be obtained from the user. For this, the value entered by the user is taken with the **setOnQueryTextListener** function over the SearchView and it is thrown for the filter, which is its own property of the Adapter class. Thus, the search is performed according to the given values.

  • onQueryTextSubmit: With this method, it is necessary to tell what to do if the user confirms what he typed on the keyboard. It is stated that he should not take any action with “false”.
  • onQueryTextChange: This method is triggered as the text changes. In this method, filtering will be done by sending every write to the adapter.
class MainActivity : AppCompatActivity() {
val countries = ArrayList<String>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val searchView = findViewById<SearchView>(R.id.searchView)

val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
countries.add("Turkey")
countries.add("England")

val adapter = RVAdapter(countries)
recyclerView.adapter = adapter

val layoutManager = LinearLayoutManager(this)
layoutManager.orientation = LinearLayoutManager.VERTICAL
layoutManager.scrollToPosition(0)
recyclerView.layoutManager = layoutManager

recyclerView.addItemDecoration(
DividerItemDecoration(this, layoutManager.orientation)
)

searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(p0: String?): Boolean {
return false
}

override fun onQueryTextChange(p0: String?): Boolean {
adapter.filter.filter(p0)
return false
}

})

adapter.filter.filter(searchView.query)
}
}

With this and more, a major milestone in modern application development has been completed.

IBRAHIM CAN ERDOGAN

Linkedin: https://www.linkedin.com/in/ibrahimcanerdogan/

--

--

ibrahimcanerdogan
ibrahimcanerdogan

Written by ibrahimcanerdogan

Hi, My name is Ibrahim, I am developing ebebek android app within Ebebek. I publish various articles in the field of programming and self-improvement.

No responses yet