Archive for category Custom ListView in Android Using Custom ArrayAdapter

Custom ListView in Android Using Custom ArrayAdapter


ListView:
ListView is a view group. It displays a list of scrollable items. The list items may be stored as an array or database query. These list items are converted into a view that is placed into the list by Adapters. Adapter automatically insert these list items into the list.

Adapter:
The Adapter behaves as a middle-man between the data source and the AdapterView layout. Adapter retrieves the data and converts each entry into a view that can be addad into the AdapterView layout.

Building Layouts With an Adapter:
Layouts are build with an Adapter to dynamically insert views using an Adapter. ListView are used frequently to display dynamic data extracted from the web or database. In such situations, the layout content for the layout of the list must be dynamic. We can use a layout that subclasses AdapterView to populate the layout with views at run time. A subclass of the AdapterView class uses an Adpater to bind data to its layout.

Filling an adapter view with data:
We can populate an AdapterView such as ListView by binding the AdapterView instance to an Adapter. ArrayAdapter, CursorAdapter are the subclasses of Adapter. They are useful for retrieving different kinds of data and building views for and AdapterView.


To clarify the above discussion, we will create a listview that list all the inbox message stored in the device. The listview uses a custom adapter. Custom adapter is used inorder to give the listview a custom layou. Here are the seried of steps that we need to follow:

1. Create the main layout for the project.
This main layout consists of the listview as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    <TextView
        android:id="@+id/list_textview"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Inbox Messages"
        android:textSize="14sp" />
    <ListView
        android:id="@+id/messageList"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp"
        android:layout_marginTop="5dp"
        android:cacheColorHint="@null"
        android:divider="@null"
        android:dividerHeight="6dp"
        android:listSelector="@android:color/transparent" />
</RelativeLayout>

2. Create the layout for the list items.
Layout for the list items are only required if we wish to use a custom adapter, else, we can use the default layout provided by the android. In our listview, we intend to show the sms senders number and the message content. So, we use two text box in our layout.

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_appwid_detail_msg"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >
    <TextView
        android:id="@+id/txt_msgTO"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:lineSpacingExtra="3dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:shadowColor="#FFFFFF"
        android:shadowDy="1"
        android:shadowRadius="0.01"
        android:singleLine="true"
        android:textColor="#000000"
        android:textSize="12sp"
        android:textStyle="bold" />
    <TextView
        android:id="@+id/txt_messageContent"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:lineSpacingExtra="3dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:shadowColor="#FFFFFF"
        android:shadowDy="1"
        android:shadowRadius="0.01"
        android:singleLine="true"
        android:textColor="#000000"
        android:textSize="12sp"
        android:textStyle="bold" />
</LinearLayout>

3. Create a holder class that holds the senders number and the message content.
We name this class as Message. Its content is given below:


public class Message {
    public String messageNumber, messageContent;
}

4. In the main activity, we will do the following things:
a. We will intialize the array and the listview .
b. We will fetch the inbox message from the device. Since, the number of message may count to hundred, we are using threads so that application thread is not overloaded. Thread helps to fetch the inbox message efficiently. The thread is attached to the activity through “weak reference”. Weak references are useful for mappings that should have their entries removed automatically once they are not referenced any more (from outside).
c. We store the inbox message into the array.
d. We initialize our custom adapter and pass the array into that adapter.
e. Then, we set our ListView with that adapter.
The demo class is as follows:


public class MainActivity extends Activity {
    private static final int TYPE_INCOMING_MESSAGE = 1;
    private ListView messageList;
    private MessageListAdapter messageListAdapter;
    private ArrayList<Message> recordsStored;
    private ArrayList<Message> listInboxMessages;
    private ProgressDialog progressDialogInbox;
    private CustomHandler customHandler;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }
    @Override
    public void onResume() {
        super.onResume();
        populateMessageList();
    }
    private void initViews() {
        customHandler = new CustomHandler(this);
        progressDialogInbox = new ProgressDialog(this);
        recordsStored = new ArrayList<Message>();
        messageList = (ListView) findViewById(R.id.messageList);
        populateMessageList();
    }
    public void populateMessageList() {
        fetchInboxMessages();
        messageListAdapter = new MessageListAdapter(this,
                R.layout.message_list_item, recordsStored);
        messageList.setAdapter(messageListAdapter);
    }
    private void showProgressDialog(String message) {
        progressDialogInbox.setMessage(message);
        progressDialogInbox.setIndeterminate(true);
        progressDialogInbox.setCancelable(true);
        progressDialogInbox.show();
    }
    private void fetchInboxMessages() {
        if (listInboxMessages == null) {
            showProgressDialog("Fetching Inbox Messages...");
            startThread();
        } else {
            // messageType = TYPE_INCOMING_MESSAGE;
            recordsStored = listInboxMessages;
            messageListAdapter.setArrayList(recordsStored);
        }
    }
    public class FetchMessageThread extends Thread {
       public int tag = -1;
        public FetchMessageThread(int tag) {
            this.tag = tag;
        }
        @Override
        public void run() {
            recordsStored = fetchInboxSms(TYPE_INCOMING_MESSAGE);
            listInboxMessages = recordsStored;
            customHandler.sendEmptyMessage(0);
        }
    }
    public ArrayList<Message> fetchInboxSms(int type) {
        ArrayList<Message> smsInbox = new ArrayList<Message>();
        Uri uriSms = Uri.parse("content://sms");
            Cursor cursor = this.getContentResolver()
                .query(uriSms,
                        new String[] { "_id", "address", "date", "body",
                                "type", "read" }, "type=" + type, null,
                        "date" + " COLLATE LOCALIZED ASC");
        if (cursor != null) {
            cursor.moveToLast();
            if (cursor.getCount() > 0) {
                do {
                    Message message = new Message();
                    message.messageNumber = cursor.getString(cursor
                            .getColumnIndex("address"));
                    message.messageContent = cursor.getString(cursor
                            .getColumnIndex("body"));
                    smsInbox.add(message);
                } while (cursor.moveToPrevious());
            }
        }  return smsInbox;
    }
    private FetchMessageThread fetchMessageThread;
    private int currentCount = 0;
    public synchronized void startThread() {
        if (fetchMessageThread == null) {
            fetchMessageThread = new FetchMessageThread(currentCount);
            fetchMessageThread.start();
        }
    }
    public synchronized void stopThread() {
        if (fetchMessageThread != null) {
            Log.i("Cancel thread", "stop thread");
            FetchMessageThread moribund = fetchMessageThread;
            currentCount = fetchMessageThread.tag == 0 ? 1 : 0;
            fetchMessageThread = null;
            moribund.interrupt();
        }
    }

    static class CustomHandler extends Handler {
        private final WeakReference<MainActivity> activityHolder;
        CustomHandler(MainActivity inboxListActivity) {
            activityHolder = new WeakReference<MainActivity>(inboxListActivity);
        }
        @Override
        public void handleMessage(android.os.Message msg) {
            MainActivity inboxListActivity = activityHolder.get();
            if (inboxListActivity.fetchMessageThread != null
                    && inboxListActivity.currentCount == inboxListActivity.fetchMessageThread.tag) {
                Log.i("received result", "received result");
                inboxListActivity.fetchMessageThread = null;
               inboxListActivity.messageListAdapter.setArrayList(inboxListActivity.recordsStored);
                inboxListActivity.progressDialogInbox.dismiss();
            }
        }
    }
    private OnCancelListener dialogCancelListener = new OnCancelListener() {

        @Override
        public void onCancel(DialogInterface dialog) {
            stopThread();
        }
    };
}

5. Finally we create our custom adapter
In this class we implement following methods of Android.Widget.Adapter super class, in order to get things working:
getCount() – returns the total number of elements in your data array
getIterm() – returns the data item associated with the specified position in the data set. Not implemented at this point.
getItemId() – returns the row id associated with the specified position in the list. Not implemented at this point.
getView() – returns the row that has to be drawn. This is the method we’re interested in the most. It will be called every time the ListView draws a new row.
Here, we can control what gets drawn in a particular row, by selecting a layout and setting data into it.A list has a transparent background in normal conditions which makes you see the image that you set as the layout background, but when the List is scrolled, it doesn’t remain transparent any more.If you want you background image to be visible while scrolling too, set cacheColorHint of your ListView.
The demo class is as follows:


public class MessageListAdapter extends ArrayAdapter<Message> {
    private Context ctx;
    public ArrayList<Message> messageListArray;
    public static boolean isDialogOpen = true;
    public MessageListAdapter(Context context, int textViewResourceId, ArrayList<Message> messageListArray) {
        super(context, textViewResourceId);
        this.messageListArray = messageListArray;
        this.ctx = context;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Holder holder;
       LinearLayout layout;
        if (convertView == null) {
            holder = new Holder();
           layout = (LinearLayout) View.inflate(ctx,R.layout.message_list_item, null);
            holder.messageTo = (TextView) convertView1.findViewById(R.id.txt_msgTO);
            holder.messageContent = (TextView) convertView1.findViewById(R.id.txt_messageContent);
            layout.setTag(holder);
        } else {
             layout = (LinearLayout) convertView;
            holder = (Holder) layout.getTag();
        }
        Message message = getItem(position);
        holder.messageTo.setText(message.messageNumber +" : ");
        holder.messageContent.setText(message.messageContent);
        return layout;
    }
    @Override
    public int getCount() {
        return messageListArray.size();
    }
    @Override
    public Message getItem(int position) {
        return messageListArray.get(position);
    }
    public void setArrayList(ArrayList<Message> messageList) {
        this.messageListArray = messageList;
        notifyDataSetChanged();
    }
    private class Holder {
        public TextView messageTo, messageContent;
    }
}

The entire project is stored in the github.
Thanks for reading my blog.Hope you enjoyed and Happy coding :-D

References:
http://developer.android.com/reference/android/widget/Adapter.html
http://www.vogella.com/articles/AndroidListView/article.html
http://developer.android.com/guide/topics/ui/layout/listview.html
http://developer.android.com/guide/topics/ui/declaring-layout.html#AdapterViews

, , , , ,

6 Comments

Follow

Get every new post delivered to your Inbox.

Join 69 other followers