Monday, 5 November 2012

Android Custom ListView Example

Today I am going to  custom my own ListView using the data getting from XML. I will use XmlPullParser to parse XML.
I am going to use BBC News RSS data ( http://feeds.bbci.co.uk/news/rss.xmlas my ListView content.


ListView is a view group that displays a list of scrollable items. The list items are automatically inserted to the list using an Adapter that pulls content from a source such as an array or database query and converts each item result into a view that's placed into the list.


1.ListView Layout

Main Layout is very simple, just  a listview.
activity_main.xml


    
    




2. Row Layout

Create a new XML layout file to design the single row layout. In this Layout we can design ourown layout for each row.
list_item.xml



    
    

    
    


NewsItem.java
package com.example.listview;

public class NewsItem {
//News title
public String mTitle;
//News description
public String mDescription;
}

Secondly, Create an Adapter. Use this adapter to bind the data to the layout.

ListViewAdapter.java
package com.example.listview;

public class NewsItem {
 //News title
 public String mTitle;
 //News description
 public String mDescription;
}

Secondly, Create an Adapter. Use this adapter to bind the data to the layout.

ListViewAdapter.java
package com.example.listview;

import java.util.ArrayList;

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;

public class ListViewAdapter extends BaseAdapter {
 private LayoutInflater mInflater;
 private ArrayList mNewsList;
 public ListViewAdapter(Context c) {
  mInflater = (LayoutInflater) c
    .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
 }

 @Override
 public int getCount() {
  if(mNewsList != null){
   return mNewsList.size();
  }
  return 0;
 }

 @Override
 public Object getItem(int position) {  
  return position;
 }

 @Override
 public long getItemId(int position) {
  return position;
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  if(convertView == null) {
   convertView = mInflater.inflate(R.layout.list_item, null);
  }
  //title
  TextView title = (TextView) convertView.findViewById(R.id.textview_title);
  //description
  TextView desc = (TextView) convertView.findViewById(R.id.textview_description);
  
  //news item
  NewsItem item = mNewsList.get(position);
  //set values
  title.setText(item.mTitle);
  desc.setText(item.mDescription);
  
  return convertView;
 }
 
 public void setData(ArrayList list){
  mNewsList = list;
 }

4. MainActivity Class

In MainActivity class, I use XmlPullParser to parse the RSS and use an ArrayList to store the result, then notify the the Adapter to refresh the ListView.
MainActivity.java
package com.example.listview;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;


import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.view.Menu;
import android.widget.ListView;

public class MainActivity extends Activity {
//BBC RSS Site URL
private static final String RSS_SITE = "http://feeds.bbci.co.uk/news/rss.xml";
//RSS TAG Name
private static final String TAG_ITEM = "item";
private static final String TAG_TITLE = "title";
private static final String TAG_DESC = "description";
private ListViewAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.listview);
mAdapter = new ListViewAdapter(this);
//set adapter
listView.setAdapter(mAdapter);
//use AsyncTask to parse the RSS data
ParseTask task = new ParseTask(this);
task.execute(RSS_SITE);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}

private class ParseTask extends
AsyncTask<String, Void, ArrayList<NewsItem>> {
private ProgressDialog dialog;

public ParseTask(Context c) {
dialog = new ProgressDialog(c);
}

@Override
protected void onPreExecute() {
dialog.setMessage("Loading data...");
dialog.show();
}

@Override
protected ArrayList<NewsItem> doInBackground(String... params) {
String strUrl = params[0];
HttpURLConnection httpConnection = null;
InputStream is = null;
try {
URL url = new URL(strUrl);
httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setRequestMethod("GET");
httpConnection.setConnectTimeout(10000);
httpConnection.setReadTimeout(10000);
httpConnection.connect();
int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
is = httpConnection.getInputStream();
return parseNews(is);
}

} catch (Exception e) {
// TODO
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (httpConnection != null) {
httpConnection.disconnect();
httpConnection = null;
}
}
return null;
}

@Override
protected void onPostExecute(ArrayList<NewsItem> result) {
//set the result
mAdapter.setData(result);
//notify to refresh
mAdapter.notifyDataSetChanged();

// Close the progress dialog
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}

private ArrayList<NewsItem> parseNews(InputStream in) throws XmlPullParserException, IOException {
ArrayList<NewsItem> newsList = new ArrayList<NewsItem>();
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser pullParser = factory.newPullParser();
pullParser.setInput(in, "UTF-8");
int eventType = pullParser.getEventType();
NewsItem item = null;
while (eventType != XmlPullParser.END_DOCUMENT) {
String tagName;

if (eventType == XmlPullParser.START_TAG) {
tagName = pullParser.getName();
if (tagName.equals(TAG_ITEM)) {
item = new NewsItem();
} else if (tagName.equals(TAG_TITLE)) {
if (item != null) {
item.mTitle = pullParser.nextText();

}

} else if (tagName.equals(TAG_DESC)) {
if (item != null) {
item.mDescription = pullParser.nextText();
}
}
} else if (eventType == XmlPullParser.END_TAG) {
tagName = pullParser.getName();
if (tagName.equals(TAG_ITEM)) {
newsList.add(item);
item = null;

}
}
eventType = pullParser.next();
}
return newsList;
}
}

The result of running is as below.