14

I have a custom adapter that visualize each row in the list of Orders.

public class OrderRowAdapter extends BaseAdapter implements OnClickListener {
    OrderList items_;
    LayoutInflater inflater_;
    int list_view_resource_id_;
    private final String TAG = "OrderRowAdapter";

    public OrderRowAdapter(Context context, int list_view_resource_id,
            OrderList items) {
        this.list_view_resource_id_ = list_view_resource_id;
        this.items_ = items;
        this.inflater_ = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public Object getItem(int position) {
        return items_.getOrders(position);
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d(TAG, "View updated for item in position = " + position);

        View v = convertView;
        if (v == null) {
            v = inflater_.inflate(list_view_resource_id_, parent);
        }

        Order item = items_.getOrders(position);
        if (item != null) {
            TextView order_info_tv = (TextView) v.findViewById(R.id.order_info);
            TextView order_status_tv = (TextView) v.findViewById(R.id.order_status);

            if (order_info_tv != null) {
                order_info_tv.setText(
                        String.format("For customer: %s\nTotal of %d items", item.getCustomerId(), item.getItemsCount()));
            }
            if (order_status_tv != null) {
                order_status_tv.setText("Status: " + getStatusText(item.getStatus()));
            }
        }
        return v;
    }

    public int getCount() {
        if (items_ == null) {
            Log.d(TAG, "Null so get count returned 0");
            return 0;
        } else {
            Log.d(TAG, "Get count returned " + items_.getOrdersCount());
            return items_.getOrdersCount();
        }
    };

After querying new list of orders from a web service, I want to update the content of the ListView, so I have my Activity does the updating before calling notifyDataSetChanged()

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

    initThreading();
    findViews();
    setUrls();

    // Load the list of order from disk
    try {
        order_list_ = OrderList.parseFrom(new FileInputStream(
                "/sdcard/orderList.bin"));
    } catch (FileNotFoundException e) {
        Log.e(TAG, "Cannot find the file", e);
    } catch (IOException e) {
        Log.e(TAG, "Cannot read the file", e);
    }

    order_row_adapter_ = new OrderRowAdapter(OrderActivity.this,
            R.layout.order_row, order_list_);
    orders_listview_.setAdapter(order_row_adapter_);

    // Request new updates from the server
    updateOrderInformation(-1);
}

public void updateOrders(InputStream new_order_stream) {
    Log.d(TAG, "Updating order UI");
    try {
        order_list_.parseFrom(new_order_stream);
    } catch (IOException e) {
        Log.e(TAG, "IOException" , e);
    }

    runOnUiThread(new Runnable() {
        public void run() {
            guiUpdateOrders();
        }
    });
}

private void guiUpdateOrders() {
    order_row_adapter_.notifyDataSetChanged();
    Log.d(TAG, "Dataset notified that it has changed. GUI update anytime now.");
}

But, the getView() method of OrderRowAdapter is never called. The ListView is never updated.

Dat Chu
  • 10,822
  • 13
  • 58
  • 82

4 Answers4

13

It turns out the problem with my getView() not being called is because it is not visible. My layout xml has the upper TextView with fill_parent for its height. Thus the entire view only has that single TextView visible.

Solution: check the graphical view of the layout in question to make sure the ListView is visible.

alexgophermix
  • 4,189
  • 5
  • 32
  • 59
Dat Chu
  • 10,822
  • 13
  • 58
  • 82
  • IMO this is a very non-obvious issue. Especially since hiding an `AdapterView` to display a `Spinner` is still a common paradigm, I can see this being that pesky wild goose for a lot of people... – alexgophermix Jun 18 '16 at 08:07
4

Make sure BaseAdapter methods

registerDataSetObserver(DataSetObserver observer) 
unregisterDataSetObserver(DataSetObserver observer) 

are not overridden.

MSquare
  • 6,311
  • 4
  • 31
  • 37
1

To change the content of your ListView, you must keep using the same reference to the List. Here you're creating another list and assigning it to the items_ variable (which does not contain the list itself, it's just a place to store a reference to a List), but your View still has a reference to the old list.

Instead of items_ = new_order_list this should work :

items_.clear();
items_.addAll(new_order_list);

EDIT :

To explain it better, try to create a new variable named old_items :

public void setNewOrderList(List<Order> new_order_list)
{
    Log.d(TAG, "New Order List available. Num items = " + new_order_list.size());

    List<Order> old_items = items_; // store the reference to the old list
    items_ = new_order_list;

    Log.d(TAG, "items_.size() = " + items_.size());
    Log.d(TAG, "old_items.size() = " + old_items.size()); // The old list still exists, and it's the one used by your ListView

    notifyDataSetChanged();
}
Dalmas
  • 26,409
  • 9
  • 67
  • 80
  • Even so, shouldn't my getView method be called if getCount keeps returning 5? I tried your suggestion and I get java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableCollection.clear – Dat Chu Dec 06 '11 at 01:36
  • Your `getCount()` method counts the items contained in your new list since you replaced the reference, not the old one which is lost somewhere in the memory. I edited my answer. – Dalmas Dec 06 '11 at 01:49
  • Your exception means that your list is read-only. How do you create it? – Dalmas Dec 06 '11 at 01:57
  • I have a protobuf message OrderList which has multiple Orders in them. My adapter suppose to provide a way to visualize these orders in a list. I was making a mistake by using OrderList.getOrdersList() which returns a read-only copy. I have modified the code to wrap around OrderList directly. – Dat Chu Dec 06 '11 at 02:11
0

If all above answers not working try with invalidateViews()

ListView.invalidateViews() is used to tell the ListView to invalidate all its child item views (redraw them).

Note that there not need to be an equal number of views than items. That's because a ListView recycles its item views and moves them around the screen in a smart way while you scroll.

 listView.invalidateViews()

My sample implementation,

                    lvitems.post(new Runnable() {
                        @Override
                        public void run() {
                            lvitems.invalidateViews(); //invalidate old
                            CustomAdapter customAdapter=new CustomAdapter(context, names, images); // load new data
                            customAdapter.notifyDataSetChanged();// call notifydatasetChanged
                        }
                    });

If any mistake in this answer please correct my mistakes.

Ranjithkumar
  • 16,071
  • 12
  • 120
  • 159