List Views

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: completed to-do list app

Setting up the main activity UI

  1. Create a new application in Android Studio called 'ToDo', for Phone and Tablet (API 14 is fine) with a Basic Activity as the starting point, leaving it with the default name
  2. A basic activity provides us with a menu, which by default includes a option for settings. Open the res/menu/menu_main.xml file and remove this existing menu item (either by deleting it from the design view, or by removing the item element from the XML
  3. Add a new menu item, set the id to "menu_insert", the title to a string resource with the text "Add new task", the icon to "@android:drawable/ic_menu_add" and the showAsAction property to ifRoom|withText. This can either be done in the design view, or by setting the XML as follows:
    <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
  4. Because we have removed a menu item which was referenced in MainActivity.kt, locate this file, and remove the line of code shown below
    R.id.action_settings -> true
    It should be easy to identify as the missing resource it references should appear in red
  5. Run the application and check that it appears and shows an insert button on the menu bar
  6. The basic activity also includes a floating action button (with an id of 'fab') and associated code (this appears as a round button with an envelope icon). Open activity_main.xml and remove this using either the design or XML view
  7. Return to MainActivity.kt and remove the code that creates the onClickListener for the floating action button you've just removed (four lines of code including the closing brace)
  8. Verify the app runs and the action button no longer appears

Adding an activity to create new tasks

  1. Add a new empty activity called "TaskEditActivity"
  2. Return to MainActivity.kt and add the following request identifier:
    private val ACTIVITY_CREATE = 0
  3. Add the following method which can be used to start the TaskEdit Activity
    private fun createTask(){
        val intent = Intent(this,TaskEditActivity::class.java)
        startActivityForResult(intent,ACTIVITY_CREATE)
    }
    Import android.content.Intent as required
  4. This activity should be triggered by the menu_insert button, so modify the code in the onOptionsItemSelected() method as follows:
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.menu_insert -> {
            createTask()
            true
        }
        else -> super.onOptionsItemSelected(item)
        }
    }
    
  5. Run the application and check that when the add button is pressed, the new empty activity appears
  6. Open activity_task_edit.xml and add an EditText element (labelled Plain Text in the Pallette) and a button beneath it
  7. Remove the existing text from the EditText and set the hint property to a String resource with the value "Task"
  8. Set the button's text to a string resource with the value "Save"
  9. Set the button's onClick attribute to "onSaveClick", which is the name of a method we will add later
  10. Verify that the layout looks similar to that shown. edit text view with editText and button elements
    The XML can also be seen here (click to expand)
    <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>
  11. The next stage is to add the functionality to the TaskEditActivity. Add the following method stub, adding the appropriate import statement for android.view.View:
    fun onSaveClick(view: View){
    
    }
    
  12. Add the following code to the new method to create an intent, and pass the text from the editText element back to the MainActivity as an Extra in the intent and then notify the activity that it's finished and should be closed as follows:
    val intent = Intent()
    intent.putExtra("task_text", editText.text.toString())
    setResult(Activity.RESULT_OK, intent)
    finish()
    Add import statements as required

Setting up the list view

  1. Open the content_main.xml file and remove the TextView element that appears within it
  2. From the legacy section of the palette, add a ListView to the activity such that it fills the screen. Set or infer constraints as required and set the id to 'listView'
  3. Add a new Layout resource to the res/layout folder named list_layout. Set the Root element to be a TextView and set the id to itemTextView
  4. As 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:

    • context (i.e. the current activity)
    • The resource Id for the layout for each item in the list
    • A list of objects (in our case it will be the MutableList<String>
    The adapter is then assigned as a property of the ListView

    Add the following properties to the MainActivity.kt class:
    private val listItems = mutableListOf<String>()
    private lateinit var adapter: ArrayAdapter<String>
  5. Add the following code to the onCreate method (adding the import statement when reqired):
    adapter = ArrayAdapter<String>(this,R.layout.list_layout, listItems)
    listView.adapter = adapter
  6. The final stage is to ensure that when the TaskEditActivity finishes, the list and listview are updated. Override the onActivityResult method as follows:
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK && requestCode == ACTIVITY_CREATE) {
            val task = data!!.getStringExtra("task_text")
            listItems.add(task)
            adapter.notifyDataSetChanged()
        }
    }
    Add the import statement for Activty as prompted
  7. Run the application and verify that you can add items to the list

Removing items from the list

So 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

  1. Add a new Menu resource file called longpress.xml in the res/menu folder
  2. Set the title property to a string resource with the text of "Delete" and set the id to menu_delete. The resulting XML should look like this:
    <?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>
  3. In MainActivity.kt, add the following code to the onCreate method to enable context menus for the listview:
    registerForContextMenu(listView)
  4. Next, override the onCreateContextMenu method as follows to display the meny item following a long press
    override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
        super.onCreateContextMenu(menu, v, menuInfo)
        menuInflater.inflate(R.menu.longpress, menu)
    }
  5. Next, add the following method which will remove an item from the ArrayList<String> given an item from the 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 trivial
  6. Finally, override the onContextItemSelected 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)
    }
  7. Test the application, adding a few items, and verify they can be removed using a long press and the menu item that appears

Additional tasks

Implement functionality so the programme retains the items in the list when it is terminated