Android Lazy Loading images and text in listview from http xml data

September 20, 2012
By

In this article, we will create an Android application which will download XML data from a remote http server. Then it will parse the downloaded xml data and display it to a listview. The application will download the images find in the xml data to a temporary cache directory and will be displayed in the listview as they are downloaded.

This application is an alternative to the application discussed in the article “Android Lazy Loading images and text in listview from http json data“, where images and text are displayed from json data.

This application is developed in Eclipse ( 4.2.0 ) with ADT plugin ( 20.0.3 ) and Android SDK ( R20.0.3 ).


1. Create a new Android application project namely “ListViewWithXMLFromURL”

Create new Android application project

Figure 1 : Create new Android application project


2. Design application launcher icon

Design application launcher icon

Figure 2 : Design application launcher icon


3. Create a blank activity to define the class MainActivity

Create a blank activity

Figure 3 : Create a blank activity


4. Enter MainActivity details

Enter MainActivity Details

Figure 4 : Enter MainActivity Details


5. Delete Android Support library from this project, if exists

By default Eclipse ( 4.2.0) adds Android Support Library to  Android application project. For this application, we don’t need to use this support library. So the library file libs/android-support-v4.jar may be removed manually via ProjectExplorer by simply right click on the file and then clicking the menu item “delete”.


6. Create a folder namely “drawable” under the folder “res”


7. Download the given below image to the folder res/drawable


8. Update the file res/values/strings.xml

<resources>
    <string name="app_name">ListViewWithXMLFromURL</string>
    <string name="hello_world">Hello world!</string>
    <string name="menu_settings">Settings</string>
    <string name="title_activity_main">ListView with XML From URL</string>
    <string name="str_iv_flag">Flag</string>
</resources>

9. Create a custom layout for the listview in res/layout/lv_layout.xml


<?xml version="1.0" encoding="utf-8"?>
<pre><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/tv_country"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:textSize="20dp"
        android:textStyle="bold" />

    <ImageView
        android:id="@+id/iv_flag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_country"
        android:layout_centerVertical="true"
        android:padding="5dp"
        android:contentDescription="@string/str_iv_flag" />

    <TextView
        android:id="@+id/tv_country_details"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_flag"
        android:layout_below="@id/tv_country"  />

</RelativeLayout>

10. Update the layout activity_main in the file res/layout/activity_main.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" >

    <ListView
        android:id="@+id/lv_countries"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:context=".MainActivity" />
</RelativeLayout>


11. Create an XML parser class in the file src/in/wptrafficanalyzer/listviewwithxmlfromurl/CountryXMLParser.java


package in.wptrafficanalyzer.listviewwithxmlfromurl;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

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

import android.util.Xml;

public class CountryXmlParser {

    private static final String ns = null;

        /** This is the only function need to be called from outside the class */
        public List<HashMap<String, Object>> parse(Reader reader)
            throws XmlPullParserException, IOException{
            try{
                XmlPullParser parser = Xml.newPullParser();
                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
                parser.setInput(reader);
                parser.nextTag();
                return readCountries(parser);
            }finally{}
        }

        /** This method read each country in the xml data and add it to List */
        private List<HashMap<String, Object>> readCountries(XmlPullParser parser)
            throws XmlPullParserException,IOException{

            List<HashMap<String, Object>> list = new ArrayList<HashMap<String,Object>>();

            parser.require(XmlPullParser.START_TAG, ns, "countries");

            while(parser.next() != XmlPullParser.END_TAG){
                if(parser.getEventType() != XmlPullParser.START_TAG){
                    continue;
            }

            String name = parser.getName();
            if(name.equals("country")){
                list.add(readCountry(parser));
            }else{
                skip(parser);
            }
        }
        return list;
    }

    /** This method read a country and returns its corresponding HashMap construct */
    private HashMap<String, Object> readCountry(XmlPullParser parser)
        throws XmlPullParserException, IOException{

        parser.require(XmlPullParser.START_TAG, ns, "country");

        String countryName = parser.getAttributeValue(ns, "name");
        String flag = parser.getAttributeValue(ns, "flag");
        String language="";
        String capital="";
        String currencyCode="";
        String currency="";

        while(parser.next() != XmlPullParser.END_TAG){
            if(parser.getEventType() != XmlPullParser.START_TAG){
                continue;
            }

            String name = parser.getName();

            if(name.equals("language")){
                language = readLanguage(parser);
            }else if(name.equals("capital")){
                capital = parser.getAttributeValue(ns, "city");
                readCapital(parser);
            }else if(name.equals("currency")){
                currencyCode = parser.getAttributeValue(ns, "code");
                currency = readCurrency(parser);
            }else{
                skip(parser);
            }
        }

        String details =     "Language : " + language + "\n" +
        "Capital : " + capital + "\n" +
        "Currency : " + currency + "(" + currencyCode + ")";

        HashMap<String, Object> hm = new HashMap<String, Object>();
        hm.put("country", countryName);
        hm.put("flag", R.drawable.blank);
        hm.put("flag_path", flag);
        hm.put("details",details);

        return hm;
    }

    /** Process language tag in the xml data */
    private String readLanguage(XmlPullParser parser)
        throws IOException, XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, "language");
        String language = readText(parser);
        return language;
    }

    /** Process Capital tag in the xml data */
    private void readCapital(XmlPullParser parser)
        throws IOException, XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, "capital");
        parser.nextTag();
    }

    /** Process Currency tag in the xml data */
    private String readCurrency(XmlPullParser parser)
        throws IOException, XmlPullParserException {
        parser.require(XmlPullParser.START_TAG, ns, "currency");
        String currency = readText(parser);
        return currency;
    }

    /** Getting Text from an element */
    private String readText(XmlPullParser parser)
        throws IOException, XmlPullParserException{
        String result = "";
        if(parser.next()==XmlPullParser.TEXT){
            result = parser.getText();
            parser.nextTag();
        }
        return result;
    }

    private void skip(XmlPullParser parser)
        throws XmlPullParserException, IOException {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            throw new IllegalStateException();
        }
        int depth = 1;
        while (depth != 0) {
            switch (parser.next()) {
                case XmlPullParser.END_TAG:
                    depth--;
                    break;
                case XmlPullParser.START_TAG:
                    depth++;
                    break;
            }
        }
    }
}


12. Update the class MainActivity in the file src/in/wptrafficanalyzer/listviewwithxmlfromurl/MainActivity.java


package in.wptrafficanalyzer.listviewwithxmlfromurl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.ListView;
import android.widget.SimpleAdapter;

public class MainActivity extends Activity {

    ListView mListView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // URL to the XML data
        String strUrl = "http://wptrafficanalyzer.in/p/demo1/countries_xml.php/countries/";

        // Creating a new non-ui thread task to download xml data
        DownloadTask downloadTask = new DownloadTask();

        // Starting the download process
        downloadTask.execute(strUrl);

        // Getting a reference to ListView of activity_main
        mListView = (ListView) findViewById(R.id.lv_countries);

    }

    /** A method to download xml data from url */
    private String downloadUrl(String strUrl) throws IOException{
        String data = "";
        InputStream iStream = null;
        try{
            URL url = new URL(strUrl);

            // Creating an http connection to communicate with url
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

            // Connecting to url
            urlConnection.connect();

            // Reading data from url
            iStream = urlConnection.getInputStream();

            BufferedReader br = new BufferedReader(new InputStreamReader(iStream));

            StringBuffer sb  = new StringBuffer();

            String line = "";
            while( ( line = br.readLine())  != null){
                sb.append(line);
            }

            data = sb.toString();

            br.close();

        }catch(Exception e){
            Log.d("Exception while downloading url", e.toString());
        }finally{
            iStream.close();
        }
        return data;
    }

    /** AsyncTask to download xml data */
    private class DownloadTask extends AsyncTask<String, Integer, String>{
        String data = null;
        @Override
        protected String doInBackground(String... url) {
            try{
                data = downloadUrl(url[0]);
            }catch(Exception e){
                Log.d("Background Task",e.toString());
            }
            return data;
        }

        @Override
        protected void onPostExecute(String result) {

            // The parsing of xml data is done in a non-ui thread
            ListViewLoaderTask listViewLoaderTask = new ListViewLoaderTask();

            // Start parsing xml data
            listViewLoaderTask.execute(result);
        }
    }

    /** AsyncTask to parse xml data and load ListView */
    private class ListViewLoaderTask extends AsyncTask<String, Void, SimpleAdapter>{

        StringReader reader;

        // Doing the parsing of xml data in a non-ui thread
        @Override
        protected SimpleAdapter doInBackground(String... strXml) {
            try{
                reader = new StringReader(strXml[0]);
            }catch(Exception e){
                Log.d("XML Exception1",e.toString());
            }

            // Instantiating xml parser class
            CountryXmlParser countryXmlParser = new CountryXmlParser();

            // A list object to store the parsed countries list
            List<HashMap<String, Object>> countries = null;

            try{
                // Getting the parsed data as a List construct
                countries = countryXmlParser.parse(reader);
            }catch(Exception e){
                Log.d("Exception",e.toString());
            }

            // Keys used in Hashmap
            String[] from = { "country","flag","details"};

            // Ids of views in listview_layout
            int[] to = { R.id.tv_country,R.id.iv_flag,R.id.tv_country_details};

            // Instantiating an adapter to store each items
            // R.layout.listview_layout defines the layout of each item
            SimpleAdapter adapter = new SimpleAdapter(getBaseContext(), countries, R.layout.lv_layout, from, to);

            return adapter;
        }

        /** Invoked by the Android when "doInBackground" is executed */
        @Override
        protected void onPostExecute(SimpleAdapter adapter) {

            // Setting adapter for the listview
            mListView.setAdapter(adapter);

            for(int i=0;i<adapter.getCount();i++){
                HashMap<String, Object> hm = (HashMap<String, Object>) adapter.getItem(i);
                String imgUrl = (String) hm.get("flag_path");
                ImageLoaderTask imageLoaderTask = new ImageLoaderTask();

                HashMap<String, Object> hmDownload = new HashMap<String, Object>();
                hm.put("flag_path",imgUrl);
                hm.put("position", i);

                // Starting ImageLoaderTask to download and populate image in the listview
                imageLoaderTask.execute(hm);
            }
        }
    }

    /** AsyncTask to download and load an image in ListView */
    private class ImageLoaderTask extends AsyncTask<HashMap<String, Object>, Void, HashMap<String, Object>>{

        @Override
        protected HashMap<String, Object> doInBackground(HashMap<String, Object>... hm) {

            InputStream iStream=null;
            String imgUrl = (String) hm[0].get("flag_path");
            int position = (Integer) hm[0].get("position");

            URL url;
            try {
                url = new URL(imgUrl);

                // Creating an http connection to communicate with url
                HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

                // Connecting to url
                urlConnection.connect();

                // Reading data from url
                iStream = urlConnection.getInputStream();

                // Getting Caching directory
                File cacheDirectory = getBaseContext().getCacheDir();

                // Temporary file to store the downloaded image
                File tmpFile = new File(cacheDirectory.getPath() + "/wpta_"+position+".png");

                // The FileOutputStream to the temporary file
                FileOutputStream fOutStream = new FileOutputStream(tmpFile);

                // Creating a bitmap from the downloaded inputstream
                Bitmap b = BitmapFactory.decodeStream(iStream);

                // Writing the bitmap to the temporary file as png file
                b.compress(Bitmap.CompressFormat.PNG,100, fOutStream);

                // Flush the FileOutputStream
                fOutStream.flush();

                //Close the FileOutputStream
                fOutStream.close();

                // Create a hashmap object to store image path and its position in the listview
                HashMap<String, Object> hmBitmap = new HashMap<String, Object>();

                // Storing the path to the temporary image file
                hmBitmap.put("flag",tmpFile.getPath());

                // Storing the position of the image in the listview
                hmBitmap.put("position",position);

                // Returning the HashMap object containing the image path and position
                return hmBitmap;

            }catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(HashMap<String, Object> result) {
            // Getting the path to the downloaded image
            String path = (String) result.get("flag");

            // Getting the position of the downloaded image
            int position = (Integer) result.get("position");

            // Getting adapter of the listview
            SimpleAdapter adapter = (SimpleAdapter ) mListView.getAdapter();

            // Getting the hashmap object at the specified position of the listview
            HashMap<String, Object> hm = (HashMap<String, Object>) adapter.getItem(position);

            // Overwriting the existing path in the adapter
            hm.put("flag",path);

            // Noticing listview about the dataset changes
            adapter.notifyDataSetChanged();
        }
    }

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


13. Update AndroidManifest.xml to provide Internet access permission for this application


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="in.wptrafficanalyzer.listviewwithxmlfromurl"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="4"
        android:targetSdkVersion="15" />

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

14. Screenshot of the application

Lazy Loading Images and Text in ListView

Figure 5 : Lazy Loading Images and Text in ListView


15. Download


16. Reference

http://developer.android.com/guide/index.html


How to hire me?

I am George Mathew, working as software architect and Android app developer at wptrafficanalyzer.in

You can hire me on hourly basis or on project basis for Android applications development.

For hiring me, please mail your requirements to info@wptrafficanalyzer.in.

My other blogs
store4js.blogspot.com


Android Knowledge Quiz

Ready to test your knowledge in Android? Take this quiz :



Tags: , , , , , , , , ,

5 Responses to Android Lazy Loading images and text in listview from http xml data

  1. Amit on September 24, 2012 at 2:21 pm

    Can we show selected listview item values in another intent with selected image and texts not in dialog but on proper screen, if yes so how, post these changes also….Thanks in advance

  2. charitha amarasinghe on February 1, 2013 at 1:13 pm

    That is a very good tutorial . thanks a lot.

  3. charitha amarasinghe on February 1, 2013 at 2:16 pm

    can you show your parsed xml file…it will more helpful to your students..

  4. swarup gosavi on January 25, 2014 at 5:00 pm

    Nice blog,Sir I want display images from xml file with its original name,how can achive?

  5. Pratik on March 19, 2015 at 9:41 pm

    Sir , your tutorial is very useful but I am facing an issue , the app force closes if no internet connection or slow connections. Plz help , waiting eagerly for reply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Be friend at g+

Subscribe for Lastest Updates

FBFPowered by ®Google Feedburner