Within many applications there is a requirement to display lists of items. One of the simplest ways to do this in Android is to use a ListView, which works well for small sets of data that don't change very often. For other scenarios a RecyclerView is likely to be a better, more efficient choice, but for the purposes of this tutorial we will use a ListView
This tutorial will use a list view to keep track of a simple to-do list like the one shown here:
android
element so that ViewBinding is available:buildFeatures {
viewBinding = true
}
item
element from the XML<item android:id="@+id/menu_insert"
android:icon="@android:drawable/ic_menu_add"
android:title="@string/add_new_task"
app:showAsAction="ifRoom|withText" />
In either case, verify that the above XML appears as an element between the opening and closing menu tags. If you copy the XML, be sure to create a corresonding String resourceR.id.action_settings -> true
It should be easy to identify as the missing resource it references should appear in redval navController = findNavController(R.id.nav_host_fragment_content_main)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
'appBarConfiguration
objectprivate val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
result ->
//todo: handle result
}
private fun createTask(){
val intent = Intent(this,TaskEditActivity::class.java)
resultLauncher.launch(intent)
}
Import android.content.Intent
as requiredonOptionsItemSelected()
method as follows:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_insert -> {
createTask()
true
}
else -> super.onOptionsItemSelected(item)
}
}
private lateinit var binding : ActivityTaskEditBinding
setContentView()
inside the onCreate method with the following code to setup the binding:binding = ActivityTaskEditBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
<android.support.constraint.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"
tools:context=".TaskEditActivity">
<EditText android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:ems="10"
android:hint="@string/task"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.502"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:onClick="onSaveClick"
android:text="@string/save"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText" />
</android.support.constraint.ConstraintLayout>
fun onSaveClick(view: View){
}
val intent = Intent()
intent.putExtra("task_text", binding.editText.text.toString())
setResult(Activity.RESULT_OK, intent)
finish()
Add import statements as requiredAs you may have gathered, everytime we add a task a string will be created, so the data source for our list view will be a simple collection of strings (such as List<String>). In order to get the text to appear in the list, we need to connect the list view with the ArrayList. To do this we can use an ArrayAdapter
. The constructor for ArrayAdapter takes the following parameters:
private val listItems = mutableListOf<String>()
private lateinit var adapter: ArrayAdapter<String>
onCreate
method (adding the import statement when reqired):adapter = ArrayAdapter<String>(this,R.layout.list_layout, listItems)
binding.content.listView.adapter = adapter
resultLauncher
variable and replace the //todo...
statenent with the followingif (result.resultCode == Activity.RESULT_OK) {
val task = result.data?.getStringExtra("task_text")!!
listItems.add(task)
adapter.notifyDataSetChanged()
}
Add the import statement for Activty as promptedSo far we can add items to the list, but they cannot be removed (short of restarting the application). It would be handy to provide a delete option if we press and hold on an item
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_delete"
android:title="@string/delete" />
</menu>
onCreate
method to enable context menus for the listview:registerForContextMenu(binding.content.listView)
onCreateContextMenu
method as follows to display the meny item following a long pressoverride fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
super.onCreateContextMenu(menu, v, menuInfo)
menuInflater.inflate(R.menu.longpress, menu)
}
private fun deleteTask(item: MenuItem){
val info = item.menuInfo as AdapterView.AdapterContextMenuInfo
listItems.removeAt(info.position)
adapter.notifyDataSetChanged()
}
Add the import statement as required. Note that the position of the menu item will match the index of the item in the ArrayList making removal trivialonContextItemSelected
method to call the deleteItem method when the appropriate context menu item is selected:
override fun onContextItemSelected(item: MenuItem?): Boolean {
when(item.itemId) {
R.id.menu_delete -> {
deleteTask(item)
return true
}
}
return super.onContextItemSelected(item)
}
A completed version of this project is available on GitHub
Implement functionality so the programme retains the items in the list when it is terminated (consider using ObjectOutputStream)