ListViews are great!
All you gotta do is just create an ArrayAdapter for the type of object you want to display and pass a list of that object, and then boom, your ListView is done.
ArrayList carsArray = new ArrayList<>( Arrays.asList("Tesla S","Volkswagen Passat","Corvette")) ArrayAdapter adapter = new ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, carsArray); ListView listView = (ListView) findViewById(R.id.cars); listView.setAdapter(adapter);
But let’s say you want to fetch your list of cars from a database, that’s where things get tough.
The Naive approach:
ArrayList carsArray = dbHandler.getAllCars(this); ArrayAdapter adapter = new ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, carsArray); ListView listView = (ListView) findViewById(R.id.cars); listView.setAdapter(adapter);
Unlike when using a statically initialized list, fetching from a database takes a lot of time thus blocking the UI thread. You cannot simply make the user wait for the screen to render, cause it would make the screen go blank, and that might be the last time that user ever uses your app.
So how do you go about implementing this? Well, one thing you could do is use a RecyclerView which is a more flexible yet a complicated version of the ListView. It binds the data to each view element, and only fetches the data when the element is on display. Pretty cool huh?
But here is the problem with that. You might not always want to complicate things to get super efficient, and would be satisfied with an approach that just doesn’t block the UI thread while simultaneously fetching from the database. Here is where we are going to use MULTITHREADING.
Multithreading in Android is achieved in different ways, one of them being AsyncTask. AsyncTask is simply an abstract class that acts as a template to do a background task asynchronously, branching from the main thread on which the UI runs.
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
The three types used by an asynchronous task are the following:
Params
, the type of the parameters sent to the task upon execution.Progress
, the type of the progress units published during the background computation.Result
, the type of the result of the background computation.
ArrayAdapter adapter; List carsArray = null; ListView listView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listView = findViewById(R.id.cars); adapter = new ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, carsArray); listView.setAdapter(adapter); new FetchFromDatabase().execute(); } private class FetchFromDatabase extends AsyncTask<Void,Void,Void> { @Override protected Void doInBackground(Void... params) { DBHandler dbHandler = new DBHandler(getApplicationContext()); carsArray = dbHandler.getAllCars(); cars.setAdapter(); return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); adapter.notifyDataSetChanged(); } }
Notice that I simply set the adapter with a null pointer in the onCreate method. This makes sure that the UI thread first renders and displays an empty list before the AsyncTask fetches and populates the array. The AsyncTask is started by simply creating an instance of it and calling execute().
You will have to override two methods:
- doInBackground(), which is the method where the background task actually executes. You would want to put your database calls here.
- onPostExecute(), this is simply the method that is executed after doInBackground() is completed. Notice here that I called notifyDataSetChanged() on the adapter, which populates the ListView with the updated array.
Bingo! You’re done. Until next time.