Tech Armada Dev's Blog


News, Tutorials, Solutions for mobile development, and other stuffs


Implementation of BaseAdapter.notifyDataSetChanged() in Android

Android Application development tips and suggestion series by Tech Armada.

Proper use of notifyDataSetChanged in your Android application

I have seen code written by many people and I observed that many people write weird code when it comes down to something they think doesn't work right out of the box.

Take for instance Android's famous notifyDataSetChanged for an ArrayAdapter or a BaseAdapter; little is known about it to many people.

The definition of notifyDataSetChanged in the Android's documentation is as follows

Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.

Despite of the definition being in plain and easily understandable English, many fail to understand the purpose of its existence.

Recently I saw a piece of code where a developer had to update his ListView everytime he added a String to his DataSet.

He followed the following approach.

  1. He changed his data by adding newly formed String to his List

    mList.add("freshdata"); 
    
  2. Then he nullified his Adapter and again instantiated it like this

     adapter = null;  
     adapter = new MyAdapter(this, myData); 
    
  3. Finally he set the resultant adapter to his ListView.

     mListView.setAdapter(adapter);  
    

Look at the poor choices he had to make here, the above three costly operations happen frequently and though this doesn't affect performance for a small data set, the performance would suffer hugely for large ones.

Seeing the above example, I thought many others would do the same or might have done the same, so I decided to write about it.

Here is a brief tutorial on how to incorporate the underlying nuts and bolts of Adapter implemented by Google into our application effectively.

This is an implementation of an Adapter which extends BaseAdapter.

 package com.arif.adapters;  
 import java.util.List;  
 import android.app.Activity;  
 import android.content.Context;  
 import android.view.LayoutInflater;  
 import android.view.View;  
 import android.view.ViewGroup;  
 import android.widget.BaseAdapter;  
 import android.widget.TextView;  
 import com.arif.listviewexample.R;  
 public class CompleteListAdapter extends BaseAdapter {  
      private Activity mContext;  
      private List<String> mList;  
      private LayoutInflater mLayoutInflater = null;  
      public CompleteListAdapter(Activity context, List<String> list) {  
           mContext = context;  
           mList = list;  
           mLayoutInflater = (LayoutInflater) mContext.
          getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
      }  
      @Override  
      public int getCount() {  
           return mList.size();  
      }  
      @Override  
      public Object getItem(int pos) {  
           return mList.get(pos);  
      }  
      @Override  
      public long getItemId(int position) {  
           return position;  
      }  
      @Override  
      public View getView(int position, View convertView, ViewGroup parent) {  
           View v = convertView;  
           CompleteListViewHolder viewHolder;  
           if (convertView == null) {  
                v = mLayoutInflater.inflate(R.layout.list_layout, null);  
                viewHolder = new CompleteListViewHolder(v);  
                v.setTag(viewHolder);  
           } else {  
                viewHolder = (CompleteListViewHolder) v.getTag();  
           }  
           viewHolder.mTVItem.setText(mList.get(position));  
           return v;  
      }  
 }  

 private class CompleteListViewHolder {  
      public TextView mTVItem;  
      public CompleteListViewHolder(View base) {  
           mTVItem = (TextView)    base.findViewById(R.id.listTV);  
      }  
 }  

This is list_layout.xml file's code:

 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:layout_width="match_parent"  
   android:layout_height="60dp"  
   android:orientation="vertical" >  
   <TextView  
     android:id="@+id/listTV"  
     android:layout_width="match_parent"  
     android:layout_height="60dp"  
     android:gravity="center_vertical"  
     android:textSize="42sp"  
     android:typeface="sans" />  
 </LinearLayout>  

This is the layout for Activity which I named as activity_complete_list.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
 xmlns:tools="http://schemas.android.com/tools"  
 android:layout_width="match_parent"  
 android:layout_height="match_parent"  
 tools:context=".CompleteListActivity" >  
 <ListView  
   android:id="@+id/completeList"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent"  
   android:layout_alignParentTop="true"  
   android:layout_marginBottom="80dp" >  
 </ListView>  
 <Button  
   android:id="@+id/addItemToList"  
   android:layout_width="match_parent"  
   android:layout_height="wrap_content"  
   android:layout_alignParentBottom="true"  
   android:layout_margin="10dp"  
   android:text="@string/add_item" />  
</RelativeLayout> 

And finally this here is my Activity named CompleteListActivity.java.

 package com.arif.listviewexample;  
 import java.util.ArrayList;  
 import java.util.List;  
 import android.app.Activity;  
 import android.os.Bundle;  
 import android.view.View;  
 import android.view.View.OnClickListener;  
 import android.widget.Button;  
 import android.widget.ListView;  
 import com.arif.adapters.CompleteListAdapter;  
 public class CompleteListActivity extends Activity implements OnClickListener {  
      private ListView mCompleteListView;  
      private Button mAddItemToList;  
      private List<String> mItems;  
      private CompleteListAdapter mListAdapter;  
      private static final int MIN = 0, MAX = 10000;  
      @Override  
      protected void onCreate(Bundle savedInstanceState) {  
           super.onCreate(savedInstanceState);  
           setContentView(R.layout.activity_complete_list);  
           initViews();  
           mItems = new ArrayList<String>();  
           mListAdapter = new CompleteListAdapter(this, mItems);  
           mCompleteListView.setAdapter(mListAdapter);  
      }  
      private void initViews() {  
           mCompleteListView = (ListView) findViewById(R.id.completeList);  
           mAddItemToList = (Button) findViewById(R.id.addItemToList);  
           mAddItemToList.setOnClickListener(this);  
      }  
      private void addItemsToList() {  
           int randomVal = MIN + (int) (Math.random() * ((MAX - MIN) + 1));  
           mItems.add(String.valueOf(randomVal));  
           mListAdapter.notifyDataSetChanged();  
      }  
      @Override  
      public void onClick(View v) {  
           switch (v.getId()) {  
           case R.id.addItemToList:  
                addItemsToList();  
                break;  
           }  
      }  
 }  

Observe carefully addItemsToList method in CompleteListActivity, I changed my data by adding a random number to my mItems List and then called notifyDataSetChanged on my mListAdapter as mListAdapter.notifyDataSetChanged();

That is all to it, one line of code brings your application a massive performance gain and also prevents random crashes.

This is the correct method of notifying your adapter that underlying data has changed. Please do not do it any other way, unless it is better than what I explain here:)

Please leave comments if it helped you.

Blogged by.

profile for Arif Nadeem at Stack Overflow, Q&A for professional and enthusiast programmers

Arif Nadeem LinkedIn
Senior Software Engineer - Mobile
Tech Armada Asia IT Consultant Pvt Ltd.


comments powered by Disqus