Housefly
Housefly

Reputation: 4422

Dynamically increasing the number of elements in a listview

I want to increase the number of items my list view will display dynamically when the list view is scrolled to the end. In my case my listview will display 10 items initially. then when we scroll to the last item it must start displaying 10 more items and so on. how can i do that??

Here is my custom array adapter

    package com.android.listview;

import java.util.ArrayList;

import com.android.listview.R;
import com.android.listview.Product;

import android.widget.ArrayAdapter;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.listview.ListViewActivity;

public class CustomArrayAdapter extends ArrayAdapter<Product> implements
        OnScrollListener {

    private final Context context;
    private final ArrayList<Product> values;

    static class ViewHolder {
        public TextView text;
        public ImageView image;
    }

    public CustomArrayAdapter(Context arg0, int arg1, int arg2,
            ArrayList<Product> arg3) {
        super(arg0, arg1, arg2, arg3);
        this.context = arg0;
        this.values = arg3;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        int product_id = 0;
        View rowView = inflater.inflate(R.layout.feed_items, parent, false);
        try {

            ImageView wic_logo = (ImageView) rowView.findViewById(R.id.logo);
            TextView label = (TextView) rowView.findViewById(R.id.label);

            Product p = values.get(position);
            product_id = p.productId;
            String date = new java.text.SimpleDateFormat("dd/MM/yy")
                    .format(new java.util.Date(p.timeStamp));
            label.setText(p.productName + "\n" + p.reportedPrice + "   MRP: "
                    + p.mrp + "\n" + "Discount: " + p.discount + "%   "
                    + p.area + "  " + p.city + "\n" + "Shared by " + p.userName
                    + " " + "on" + " " + date);

            wic_logo.setImageResource(R.drawable.wic_logo_small);
            Log.d("date", "" + date);

            Log.d("Custom Array Adapter", "at" + position);
        } catch (Exception e) {
            Log.d("Custom Array Adapter", "catch");
        }

        return rowView;
    }

    public void onScroll(AbsListView arg0, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        Log.d("entered onScroll", " " + firstVisibleItem + visibleItemCount
                + totalItemCount);
        if (((firstVisibleItem + visibleItemCount) >= totalItemCount - 1)) {
            Log.d("entered if", " " + firstVisibleItem + visibleItemCount
                    + totalItemCount);
            // if we're at the bottom of the listview, load more data
            addData(totalItemCount, values.get(totalItemCount).productId);
        }
    }

    private void addData(int totalItemCount, int productId) {

        Toast.makeText(getContext(), "last item", Toast.LENGTH_SHORT).show();
    }

    public void onScrollStateChanged(AbsListView arg0, int arg1) {

    }
}

and here is my activity

package com.android.listview;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.android.listview.CustomArrayAdapter;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Button;
import android.widget.ListView;

public class ListViewActivity extends Activity {

    static Product[] feed_products_list;
    private JSONArray JArray;
    private InputStream is;
    private StringBuilder sb;
    private String result;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button feed_button = (Button) findViewById(R.id.feedButton_feed);
        feed_button.setBackgroundResource(R.color.grey);
        ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        try {
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = new HttpPost("http://10.0.2.2/wic2/mobile/feed");
            httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            HttpResponse response = httpclient.execute(httppost);
            HttpEntity entity = response.getEntity();
            is = entity.getContent();

            try {
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(is, "iso-8859-1"), 8);
                sb = new StringBuilder();
                sb.append(reader.readLine() + "\n");
                String line = "0";
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
                is.close();
                result = sb.toString();
                Log.d("result", result);
            } catch (Exception e) {
                Log.e("error", "Error in http connection" + e.toString());
            }

            Log.e("response", "response is:" + response.toString());
        } catch (Exception e) {
            Log.e("error", "Error in http connection" + e.toString());
        }

        ListView tv = (ListView) findViewById(R.id.feedListView);
        try {
            Log.d("JArray", "entered try");
            JArray = new JSONArray(result);
            int length = JArray.length();
            feed_products_list = new Product[length+1];
            Log.d("JArray", "try last line");
        } catch (JSONException e) {
            Log.d("JArray", "in catch");
            e.printStackTrace();
        }

        JSONObject jsonObj;

        for (int i = 0; i < JArray.length(); i++) {
            try {
                Log.d("jsonObj", "entered try");
                jsonObj = JArray.getJSONObject(i);
            } catch (Exception e) {
                Log.d("jsonObj", "in catch");
                continue;
            }

            try {
                Log.d("feed_products_list", "entered try");
                feed_products_list[i] = new Product(jsonObj.getInt("id"),
                        jsonObj.getString("image"),
                        jsonObj.getString("product_name"),
                        jsonObj.getString("reported_price_formated"),
                        jsonObj.getString("store_area"),
                        jsonObj.getString("store_city"),
                        jsonObj.getString("mrp"),
                        jsonObj.getString("user_name"),
                        jsonObj.getLong("reported_timestamp"),
                        jsonObj.getInt("discount"));
                Log.d("feed_products_list", feed_products_list[i].productName);
            } catch (JSONException e) {
                Log.d("feed_products_list in catch",
                        feed_products_list[i].productName);
                e.printStackTrace();
            }
        }

        tv.setAdapter(new CustomArrayAdapter(this, R.layout.feed_items,
                R.id.label, feed_products_list));


    }
}

and one more problem is that i have to initialize my array feed_products_list[length] with the number of items in the JArray. so i cannot change array size every time i scroll to the last item and re-populate the whole list again

Upvotes: 3

Views: 9351

Answers (6)

Reinier
Reinier

Reputation: 3876

First, there's a few problems with your code. In your Activity, change your variable values to an ArrayList<Product> since you cannot manipulate the length of an array after creation. Next, rename JArray to jArray; if the first letter is a capital that should indicate you're dealing with a class instead of a variable.

Also, you're not using the ViewHolder pattern, which greatly improves performance. Check out this link on vogella.com about ListView; more specifically, check out chapters 3.1 and 3.2.

In CustomArrayAdapter, implement OnScrollListener and add the required methods. Then, in onScroll(...), determine if you want to add data (e.g. user reached bottom of list). Finally, load the new items, add them to your dataset with values.add(product) or values.addAll(products) where products is a List of Products, and call notifyDataSetChanged from within your CustomArrayAdapter after every call to values.add(...) or values.addAll(...).

For example, you could change the following in your code:

In your Activity:

  • Remove the lines

    int length = JArray.length();
    feed_products_list = new Product[length+1];
    
  • Remove the 'static' from your feed_products_list and change it to an ArrayList:

    private ArrayList<Product> feed_products_list = new ArrayList<Product>();
    
  • Instead of feed_products_list[i] = new Product(jsonObj.getInt("id"), .... yadiya ..., call feed_products_list.add(new Product(jsonObj.getInt("id"), ... yadiya ...`.

In your Adapter:

  • Make the class implement OnScrollListener, and set the onScroll()-method to something like the following:

    public void onScroll(AbsListView view, int firstVisibleItem,
                            int visibleItemCount, final int totalItemCount) {
        if(((firstVisibleItem + visibleItemCount) >= totalItemCount)) {
    
            // if we're at the bottom of the listview, load more data
            addData(totalItemCount);
        }
    }
    
  • Create a method addData(long offset):

    private void addData(long offset) {
    
        // download your data, pass the offset so you know where to start
        // and when you convert your json into `Product`s
        Product product = new Product(JSONObject.getInt("id"), ... yadiya ...);
        this.add(product);
        this.notifyDataSetChanged();
    
        // make sure to call notifyDataSetChanged() on your UI-Thread.
    }
    

Upvotes: 3

Bhavin
Bhavin

Reputation: 6010

I think you want something like Lazy loading.

It is Called as Lazy Loading. i am sure you haven't Tried searching like that. And i have given you example of GridView. This is other link of Listview Other Link

Upvotes: 0

Konstantin Pribluda
Konstantin Pribluda

Reputation: 12367

You do not need to do this. Android listviews can easily handle thousands entries - provided that you write your adapter carefully, and reuse views. So in your getView() method you have to check whether converView is supplied and of correct , and then just populate it with new values. (Andriod is so smart, that it offers to recycle list entries which are n ot visible right now) - this will give you immediate performance boost

Do it like this:

View rowView = convertView;
if(rowView == null) {
   // do what you are doing now to inflate view
}
// do what you are doing now to set up view

return rowView;

Increasing size of list would make sense if you do not like to load all the entries from whatever storage you immediately - in this case you have to register scroll listener and react to it by loading missing entries, and then signaling that list data changed. And you do not have to use array as backing storage - list will be perfect match for this purpose

Upvotes: 0

confucius
confucius

Reputation: 13327

use ArrayList instead of array like this

public class CustomArrayAdapter extends ArrayAdapter<Object> {

    private final Context context;
    private final ArrayList<Product> values;

and in your getView()

            Product p = values.get(position);

you could provide a method inside your adapter to add element to the ArrayList and notifyDataSetChange() it looks like this

public void addElement(Product p){
values.add(p);
notifiyDataSetChange();
}

use the OnScrollListener to load more data example :

listView.setOnScrollListener(new OnScrollListener() { 
    @Override 
    public void onScroll(AbsListView view, int firstVisibleItem,  
        int visibleItemCount, int totalItemCount) { 

        if (totalItemCount - visibleItemCount <= firstVisibeItem + 5) { 
            //load more content 

            .....
            adpater.addElement(p);

        } 


     } 
    });

Upvotes: 0

Mayank
Mayank

Reputation: 8852

you should attach a scroll listener to your listview

ListView lv = (ListView)findViewById(R.id.list_view); 
lv.setOnScrollListener(new OnScrollListener() { 
    @Override 
    public void onScroll(AbsListView view, int firstVisibleItem,  
        int visibleItemCount, int totalItemCount) { 
        //Check if the last view is visible 
        if (view.getLastVisiblePosition()+1 == 10) { 
            //load more content 
        } 
    } 
});

Update

so initially in your feed_products_list you'll have 10 items, when user scrolls down, add 10 more items to the list and reset the CustomArrayAdapter to listView. Keep doing that untill you have all the items in the list.

Upvotes: 1

UVM
UVM

Reputation: 9914

For dynamically increasing the size, it is better to use ArrayList.For scrolling you can use @Mayank idea.

Upvotes: 2

Related Questions