My previous article Customized ListView with Image and Text gives you a good overview of customizing a list view which contains a thumbnail image and few text fields. All the list data will be downloaded by making a network calls. The main challenge in that tutorial is downloading the images asynchronously and caching them. Because of lack of good tools I used a third party library (it is a good library though) to download the listview data and caching the images.

Today I am going to explain the same, but this time with a different approach by using an another library called volley. By using volley you can see decent improvement in listview performance without much effort.

Android Custom ListView with Image and Text using Volley

Final Output

The list view we are going to build contains list of movies information where each list row defines single movie. The row layout is customized that contains movie poster as thumbnail image on the left and the movie title, rating, genre and release date on the right. Below is the final output.

android custom listview with image and text using volley

1. Example JSON

I am going to use this example json to load the list data. This json contains array of objects where the each object contains information like movie name, rating, genre and release date.


        "title": "Dawn of the Planet of the Apes",
        "image": "",
        "rating": 8.3,
        "releaseYear": 2014,
        "genre": ["Action", "Drama", "Sci-Fi"]

2. Downloading Volley Library (volley.jar)

If you are coming across volley for the first time, I would suggest you go through my previous article Android working with Volley Library which gives you enough knowledge about volley and other things like building the volley library to generate volley.jar. It is up to you that you want to generate volley.jar on your own or just download the the volley.jar

3. Planning The Layout Design

To achieve this customized layout I choose the RelativeLayout as parent element. All the child elements are aligned using relative properties like align below, align right, align parent bottom etc.,

android custom list view design

Now let’s start by creating a new project.

4. Creating New Project

1. In Eclipse create a new project by navigating to File ⇒ New ⇒ Android Application Project and fill required details. I gave my project package name as

2. Once the project is created paste the volley.jar in libs folder.

3. I am creating required packages first just to keep the project organised. This step is optional but it is recommended. Create four packages named adapter, app, model and util. So after creating packages my project contains following.

4. Open colors.xml under res ⇒ values and add below colors. If you don’t see colors.xml file, create a new file and add these color values.

<?xml version="1.0" encoding="utf-8"?>

    <color name="genre">#666666</color>
    <color name="year">#888888</color>
    <color name="list_divider">#d9d9d9</color>
    <color name="list_row_start_color">#ffffff</color>
    <color name="list_row_end_color">#ffffff</color>
    <color name="list_row_hover_start_color">#ebeef0</color>
    <color name="list_row_hover_end_color">#ebeef0</color>


5. Also add below dimensions in dimens.xml file located under res ⇒ values.

    <dimen name="title">17dp</dimen>
    <dimen name="rating">15dip</dimen>
    <dimen name="genre">13dip</dimen>
    <dimen name="year">12dip</dimen>


6. Before start writing java code, I would like to complete the UI part first. Create list_row_bg.xml, list_row_bg_hover.xml and list_row_selector.xml with below respective codes under res ⇒ drawable folder. If you don’t see drawable folder under res, create a new folder and name it as drawable.

list_row_bg.xml – Default style for list view row.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android=""
      android:angle="270" />

list_row_bg_hover.xml – Style for list view row when row is selected

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android=""
    android:shape="rectangle" >

        android:startColor="@color/list_row_hover_start_color" />


list_row_selector.xml – Selector file to toggle the row states.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="">

    <item android:drawable="@drawable/list_row_bg" android:state_pressed="false" android:state_selected="false"/>
    <item android:drawable="@drawable/list_row_bg_hover" android:state_pressed="true"/>
    <item android:drawable="@drawable/list_row_bg_hover" android:state_pressed="false" android:state_selected="true"/>


7. Now open your layout file of main activity(in my case activity_main.xml) and add a ListView element.

<RelativeLayout xmlns:android=""
    tools:context=".MainActivity" >

        android:listSelector="@drawable/list_row_selector" />


8. We need to create another layout file for list view row. This is the main design component in this project as we define actual custom list design here. I am naming this file as list_row.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android=""
    android:padding="8dp" >

    <!-- Thumbnail Image -->
        android:layout_marginRight="8dp" />

    <!-- Movie Title -->
        android:textStyle="bold" />

    <!-- Rating -->
        android:textSize="@dimen/rating" />
    <!-- Genre -->
        android:textSize="@dimen/genre" />

    <!-- Release Year -->
        android:textSize="@dimen/year" />


Here we completed the design part. Let’s move to java part.

9. Create under util package. This class takes care of caching images on disk.

package info.androidhive.customlistviewvolley.util;



public class LruBitmapCache extends LruCache<String, Bitmap> implements
		ImageCache {
	public static int getDefaultLruCacheSize() {
		final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
		final int cacheSize = maxMemory / 8;

		return cacheSize;

	public LruBitmapCache() {

	public LruBitmapCache(int sizeInKiloBytes) {

	protected int sizeOf(String key, Bitmap value) {
		return value.getRowBytes() * value.getHeight() / 1024;

	public Bitmap getBitmap(String url) {
		return get(url);

	public void putBitmap(String url, Bitmap bitmap) {
		put(url, bitmap);

10. Create under app package. This class is a singleton class which initializes core objects of volley library.


import info.androidhive.customlistviewvolley.util.LruBitmapCache;
import android.text.TextUtils;


public class AppController extends Application {

	public static final String TAG = AppController.class.getSimpleName();

	private RequestQueue mRequestQueue;
	private ImageLoader mImageLoader;

	private static AppController mInstance;

	public void onCreate() {
		mInstance = this;

	public static synchronized AppController getInstance() {
		return mInstance;

	public RequestQueue getRequestQueue() {
		if (mRequestQueue == null) {
			mRequestQueue = Volley.newRequestQueue(getApplicationContext());

		return mRequestQueue;

	public ImageLoader getImageLoader() {
		if (mImageLoader == null) {
			mImageLoader = new ImageLoader(this.mRequestQueue,
					new LruBitmapCache());
		return this.mImageLoader;

	public <T> void addToRequestQueue(Request<T> req, String tag) {
		// set the default tag if tag is empty
		req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);

	public <T> void addToRequestQueue(Request<T> req) {

	public void cancelPendingRequests(Object tag) {
		if (mRequestQueue != null) {

11. Now add the class in AndroidManifest.xml to your <application> tag using name property to execute this class on application start. Also add INTERNET permission as we are going to make network calls.

        android:name="" ../>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android=""
    android:versionName="1.0" >

        android:targetSdkVersion="18" />

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

        android:theme="@style/AppTheme" >
            android:label="@string/app_name" >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />


12. Now create under model package. This model class will be used to provide movie objects data to list view after parsing the json.

package info.androidhive.customlistviewvolley.model;

import java.util.ArrayList;

public class Movie {
	private String title, thumbnailUrl;
	private int year;
	private double rating;
	private ArrayList<String> genre;

	public Movie() {

	public Movie(String name, String thumbnailUrl, int year, double rating,
			ArrayList<String> genre) {
		this.title = name;
		this.thumbnailUrl = thumbnailUrl;
		this.year = year;
		this.rating = rating;
		this.genre = genre;

	public String getTitle() {
		return title;

	public void setTitle(String name) {
		this.title = name;

	public String getThumbnailUrl() {
		return thumbnailUrl;

	public void setThumbnailUrl(String thumbnailUrl) {
		this.thumbnailUrl = thumbnailUrl;

	public int getYear() {
		return year;

	public void setYear(int year) {
		this.year = year;

	public double getRating() {
		return rating;

	public void setRating(double rating) {
		this.rating = rating;

	public ArrayList<String> getGenre() {
		return genre;

	public void setGenre(ArrayList<String> genre) {
		this.genre = genre;


13. Create a class named with below code. This is a custom list adapter class which provides data to list view. In other words it renders the layout_row.xml in list by pre-filling appropriate information.

package info.androidhive.customlistviewvolley.adater;

import info.androidhive.customlistviewvolley.R;
import info.androidhive.customlistviewvolley.model.Movie;

import java.util.List;

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 CustomListAdapter extends BaseAdapter {
	private Activity activity;
	private LayoutInflater inflater;
	private List<Movie> movieItems;
	ImageLoader imageLoader = AppController.getInstance().getImageLoader();

	public CustomListAdapter(Activity activity, List<Movie> movieItems) {
		this.activity = activity;
		this.movieItems = movieItems;

	public int getCount() {
		return movieItems.size();

	public Object getItem(int location) {
		return movieItems.get(location);

	public long getItemId(int position) {
		return position;

	public View getView(int position, View convertView, ViewGroup parent) {

		if (inflater == null)
			inflater = (LayoutInflater) activity
		if (convertView == null)
			convertView = inflater.inflate(R.layout.list_row, null);

		if (imageLoader == null)
			imageLoader = AppController.getInstance().getImageLoader();
		NetworkImageView thumbNail = (NetworkImageView) convertView
		TextView title = (TextView) convertView.findViewById(;
		TextView rating = (TextView) convertView.findViewById(;
		TextView genre = (TextView) convertView.findViewById(;
		TextView year = (TextView) convertView.findViewById(;

		// getting movie data for the row
		Movie m = movieItems.get(position);

		// thumbnail image
		thumbNail.setImageUrl(m.getThumbnailUrl(), imageLoader);
		// title
		// rating
		rating.setText("Rating: " + String.valueOf(m.getRating()));
		// genre
		String genreStr = "";
		for (String str : m.getGenre()) {
			genreStr += str + ", ";
		genreStr = genreStr.length() > 0 ? genreStr.substring(0,
				genreStr.length() - 2) : genreStr;
		// release year

		return convertView;


14. Now open your main activity class ( and do the below changes. Here I created volley’s JsonArrayRequest to get the json from url. Upon parsing the json, I stored all the json data into an ArrayList as Movie objects. Finally I called notifyDataSetChanged() on CustomListAdapter instance to render the list view with updated information.

package info.androidhive.customlistviewvolley;

import info.androidhive.customlistviewvolley.adater.CustomListAdapter;
import info.androidhive.customlistviewvolley.model.Movie;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.ListView;


public class MainActivity extends Activity {
	// Log tag
	private static final String TAG = MainActivity.class.getSimpleName();

	// Movies json url
	private static final String url = "";
	private ProgressDialog pDialog;
	private List<Movie> movieList = new ArrayList<Movie>();
	private ListView listView;
	private CustomListAdapter adapter;

	protected void onCreate(Bundle savedInstanceState) {

		listView = (ListView) findViewById(;
		adapter = new CustomListAdapter(this, movieList);

		pDialog = new ProgressDialog(this);
		// Showing progress dialog before making http request

		// changing action bar color
				new ColorDrawable(Color.parseColor("#1b1b1b")));

		// Creating volley request obj
		JsonArrayRequest movieReq = new JsonArrayRequest(url,
				new Response.Listener<JSONArray>() {
					public void onResponse(JSONArray response) {
						Log.d(TAG, response.toString());

						// Parsing json
						for (int i = 0; i < response.length(); i++) {
							try {

								JSONObject obj = response.getJSONObject(i);
								Movie movie = new Movie();
								movie.setRating(((Number) obj.get("rating"))

								// Genre is json array
								JSONArray genreArry = obj.getJSONArray("genre");
								ArrayList<String> genre = new ArrayList<String>();
								for (int j = 0; j < genreArry.length(); j++) {
									genre.add((String) genreArry.get(j));

								// adding movie to movies array

							} catch (JSONException e) {


						// notifying list adapter about data changes
						// so that it renders the list view with updated data
				}, new Response.ErrorListener() {
					public void onErrorResponse(VolleyError error) {
						VolleyLog.d(TAG, "Error: " + error.getMessage());


		// Adding request to request queue

	public void onDestroy() {

	private void hidePDialog() {
		if (pDialog != null) {
			pDialog = null;

	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(, menu);
		return true;


Now run the project and test it once. Please do comment below if you ran into any issue.

Ravi is hardcore Android programmer and Android programming has been his passion since he compiled his first hello-world program. Solving real problems of Android developers through tutorials has always been interesting part for him.
  • Aneudy Amparo

    It worked! I’m trying to solve a little problem with the image only!

    Thank you so much!

    • Cheers!

      • M Mohsin

        Your Tutorial is so much fine
        how to put this code and activity and method name:
        updateimageurl = obj.getString(“image”).replace(“http”, “https”);

        • Paste your json parsing code.

          • M Mohsin

            Thanks, Lastly Its work.

          • Great. Have you found out the problem?

          • M Mohsin

            Yeah. Dear

            Lots of thanks

  • Mohammed Anis

    Hello sir, I am PHP developer. For the first time i am developing android app. I saw your article and developed the app almost. But i am facing a problem in null pointer exception sometimes when i click listview.. suggest me what can i do? Check below screenshot

    • It seems you are not checking for null response from http call, but accessing the response.

      • Mohammed Anis

        thanks sir its working

  • dharmesh rajput

    i have succeded making a app (working very nice)with many fragments in which one fragment is having a custom listview loading images from network and caching them in memory ,without any external library but the problem is the bitmap is taking to much of memory around 70-100 mb ,i have released memory at approriate cycles of fragments and activity, everything is fine, my memory allocation tracker tells that “onbitmapcreate” method is taking 80 % of apps memory,because of this 4% users have reported outofmemory crashreport ,so can you suggest any method for bitmap memory optimization ??or using the above volley library will clear the memory issue?? help ? thank you very much for your tutorials it really helped a lot till now

    • Why are not using Image Caching libraries like Glide, Picasso or Fresco?

      • dharmesh rajput

        good Aternoon ! Sir today ii tested the app with glide library but the memory allocation is almost the same.

        • In the library you can mention the max cache size somewhere.

          • dharmesh rajput

            Thanks Sir,for all your Help, i will test the library again !!!Keep Posting ! Helping Android community a lot

  • divya chandrika

    where do we have to place volley.jar file

  • divya chandrika

    i have done everything you have mentioned above, there were no errors. but still why is the app getting stopped without even starting? can you please help me solving it

  • Edgar Tinajero

    Awesome Ravi! This isn’t the first guide from you that helps me, there have been other using Material Design but this one I just loved It!

    I have just finished your guide and it’s working really great!, some minor problems with the new HTTPS from your blog but nothing that a little gist can’t handle haha.

    Thanks a lot!! Your blog is one of the best ones!

    • Yeah updating all the articles to https is a big task for me. I modify them one day.

      Thanks for your appreciation.
      Happy Coding 😀

  • Keleskop

    I downloaded code and run ‘app’ to install it. I run app but it shows empty slots. There aren’t films and other things.
    I run it on android studio.

    • Hi
      Could you try the changing the url to https in the code?


      to (note: this is https)

      • Keleskop

        Yes, it worked but it doesnt show images. I guess it is because of image url. If I change url to image: “https…”, it will work. I have a question, how can I change url in this code. I want to add “s” after “http”.


        • I have to modify the image url in the json and update on the server.

          A quick fix can be replace the http to https in image url before passing it to Glide library.

          • Ankur Jain

            there is only one place where u mentioned http in Mainactivity, Where else to change http to https? I m not getting images. Where is Glide lib? pls guide

          • Ankur Jain

            worked, by replacing string being passed to image url like this String updateimageurl = obj.getString(“image”).replace(“http”, “https”);

          • Thanks Ankur.

  • Sivakumar

    Hi ravi, i got this error,
    Unexpected response code 301

  • Sivakumar

    Hi ravi,

    The application is not opened.?

    • In the code could you try to change the json url to https?

  • binod lamichhane

    Hello Ravi Sir can you please suggest a tutorial that show how we can implement detailView in the above tutorial when listitem is clicked??? Please Sir this is urgent i couldnt find good tutorial. if possible mail me in.

  • Neo Toshiya

    Hi Ravi,
    In the first step you said to create 4 packages. I did create a package but it didn’t show up in my project structure.
    What I did is I right-clicked on the app directory, and click new Package.
    When I tried to create new Package under the same name, it says “adapter with the same name already exist”.
    Where does the package exist or what did I do wrong.


    • Hi

      You need to toggle the project view from Android to Project. Also it hides the empty packages. Please explore in this area.

  • virendra gour

    Hi Ravi,
    I am beginner in Android development .Your tutorial is very useful for reference.
    Thank you so much 🙂

  • irfan fadhilah

    Hi Ravi,
    Can you give me source code php and database?
    Thank you, sir

  • Vu Dinh Phong

    Hi Ravi, How can I have the list view to auto refresh and get the data again every 10 seconds? Thank you so much. Do you have any source?

    • Chris S

      Add the following as the last thing in your onCreate:

      //Runs volley request every 10 seconds
      Runnable runnable = new Runnable() {
      public void run() {

      if (movieList != null) {



      handler.postDelayed(this, 10000);


      handler.postDelayed(runnable, 10000);

      This works, but every time it refreshes the list blinks. I can’t figure out how to prevent this. Anyone have any ideas?

  • shraddha

    I have replaced the image url then also the images are not displaying.
    what to do?
    Please help me

    • shashi patil

      did u get it?

  • Pradeep Kumar

    i pasted the code in android studio but it is showing several errors i rectified some errors.still there are errors.can you please help me to rectify those errors.R.”menu”.this is in red color last line in main activity java class.

  • kumar

    hi ravi
    this is my image pickup. How to clickon this open full image on new window
    thumb_image = (NetworkImageView) findViewById(;

  • Satria Adytia

    hi ravi,
    how to make it can acces data from database?

  • Van Roosevelt

    what about if its from json Object? help me please.

  • Ahmed Abd El-hak

    what IDE you work on , this doesn’t open with android studio !!

    • It’s older Eclipse project. Try adding one by one file (by reading the article) in android studio.

  • Ahmed Abd El-hak
    • Seems the value in a variable is null at line #50 in MainActivity. Could you post the code of MainActivity?