FROMDEV

Android ListViews Tutorial

Tutorial of android list view feature for android developers

One of the most utilized UI-elements in Android apps is ListViews. Whether you are displaying a list of songs in an album, ingredients in a recipe, or search results, almost every Android app needs to render lists. Many newer developers are lulled into a false sense of security because it is pretty easy to get started with ListViews.

However, they will eventually learn the hard way that as ListView datasets grow, or their data becomes increasingly complicated to fetch and render, ListViews can be quite tricky to master. This article is meant to help Android developers, who have come or will soon come to this realization, and will include several tips to help solve common ListView pitfalls.

Checkout more android tutorials at this page : list of best android tutorials.

Understanding View Lifecycles

ListViews often contain more items than can be displayed on a screen at one time. Although the logic of rendering each individual item in the list might be trivial, iterating through all of them is overkill and could lead to serious memory issues. For this reason, ListViews only render what is on screen and slightly off-screen in both directions.

Additionally, properly optimized ListViews can reuse effort spent rendering a previously visible ListView item when rendering a newly visible one. This process is known as view recycling. To utilize it, all you need to do is determine if your new view is actually new or if it is being recycled. As an example, check out the null check in the following code snippet:

@Override
public View getView(int pos, View recycledView, ViewGroup parent) {

if (recycledView == null) {

recycledView = View.inflate(getActivity(), R.layout.item, parent);

}

...

return recycledView;

}

If you make the mistake of not including this null check and inflate a new view each and every time, your app will have rogue views taking up vital memory. If a user scrolls this list up-and-down enough, it is very likely your app will eventually crash with an out of memory exception.

Cache Your View Data with the View Holder Pattern

Another issue that plagues larger ListViews is the potential for sluggish scrolling when your ListView items take too long to retrieve and/or render their data. Common issues here are items that require non-trivial calculations or database queries to populate. A great approach to solve problems like this is the View Holder pattern. The basic idea is to create a class that can be used to cache the data you have already retrieved or calculated for any future renderings. The following class is a very simplified example of this:

public class MyViewHolder {

TextView difficultToCalculateValue;

...

}

Now, when it comes time to render a ListView item, we start by populating this class. The following getView function provides an example of this:

@Override
public View getView(int pos, View recycledView, ViewGroup parent) {

MyViewHolder myViewHolder;

if (recycledView == null) {

recycledView = View.inflate(getActivity(), R.layout.item, parent);
myViewHolder = new MyViewHolder();
myViewHolder.difficultToCalculateValue = convertView.findViewById(R.id.text_id);

...

recycledView.setTag(myViewHolder);

} else {

myViewHolder = (MyViewHolder)convertView.getTag();

}

myViewHolder.difficultToCalculateValue.setText("This took forever to calculate.");

...

return recycledView;
}

Learning when to use ListView.invalidateViews() and Adapter.notifyDatasetChanged()

invalidatedViews() and notifyDatasetChanged() are two of the main ways to inform your ListView that its child views need to be updated. Some common scenarios of this include handling the removal or addition of a list item, bulk updating the style of list items, or updating the value displayed on several list items. 

The basic difference is that invalidateViews() only looks at views currently on the screen and does not take into account underlying changes to the dataset. 
So, it is not what you need when adding or removing items from the underlying dataset. notifyDatasetChanged() informs your ListView that it needs to redraw and take into account the updated dataset.

Properly Handing Asynchronous Tasks

As any Android developer knows, accessing a database or waiting for an API response takes time and should be handled off the main UI-thread. For this reason, neither of these actions should be performed inside your getView functions. A simple solution is to load all of your data into memory prior to rendering your list. If that is not possible, you can enlist the help of AsyncTasks.

The following code snippet shows one example of this. Please note that inline AsyncTasks are typically a bad idea, but one is shown here for the simplicity of the illustration:

@Override
public View getView(int pos, View recycledView, ViewGroup parent) {

...

new AsyncTask() {

@Override
public Bitmap doInBackground(Void... params) {
return BitmapFactory.decodeFile(...);
}

@Override
public void onPostExecute(Bitmap bitmap) {
imageView.setBitmap(bitmap);
}

}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return recycledView;
}

It is very likely that your specific implementation will be more involved than this example, but this should help you get started.

Also, keep in mind that you need to ensure your solution can handle users flinging through your ListView. If you spawn a new thread for each item in your list and never cancel them, your app will run into several issues. 

To handle this potential problem, you need to keep track of your spawned AsyncTasks and call cancel() on any that are no longer needed. This is a step in the right direction, but it is important to know that cancel simply prevents your AsyncTasks’ onPostExecute from firing; it does not stop the thread itself. 
If you run into issues due to having too many threads, you can consider conditionally spawning threads based on the speed a user is scrolling or some other mechanism that works for your specific list. 
Ultimately, this can get pretty complicated. If at all possible, try to find a way to not have to spawn threads inside your getViews.

Conclusion

Every Android developer will eventually have to create a ListView. Most are fairly simple, but as your lists’ complexity grows, so must your understanding of ListViews. Sluggish ListViews can severely harm your user experience and implementing these tips can go a long way to alleviate these issues. One final thing, it is important to consider all available tools before beginning to code.

If a ListView is not the proper tool for the job, optimizing can only go so far. If you are about to add a ListView to your app, make sure you consider if something else (including the powerful RecyclerView) could be a better fit.

Paul is a Developer Advocate at [The BHW Group]. He works with clients on custom Android apps and web-based solutions. He frequently writes about mobile development, technology, and business-related topics.

Exit mobile version