3

I am using a ListView to show some data that comes from a JSON. The JSON has 3 objects. I am using a Custom Adapter for the list view.

Here is the layout of list view

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/doc_signing"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <ListView android:id="@+id/android:list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:dividerHeight="1.5dp" />
    <TextView android:id="@+id/android:empty"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="22sp"
        android:text="@string/no_docs_to_sign"/>
</LinearLayout>

Here is the layout of the list view row

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/doc_list_row"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/doc_name"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:textSize="22sp" >
    </TextView>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView
            android:id="@+id/doc_id"
            android:layout_weight="0.50"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingBottom="2dp"
            android:paddingLeft="5dp"
            android:paddingRight="3dp"
            android:textSize="16sp" >
        </TextView>
        <TextView
            android:id="@+id/doc_date"
            android:layout_weight="0.50"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingBottom="2dp"
            android:paddingLeft="3dp"
            android:paddingRight="3dp"
            android:textSize="16sp" >
        </TextView>
    </LinearLayout>
</LinearLayout>

Code to read the JSON and set the adapter is here

ArrayList<DocInfo> docInfo = new ArrayList<DocInfo>();
try {
    JSONArray jArray = new JSONArray(readData());
    for (int i = 0; i < jArray.length(); i++) {
        JSONObject jObject = jArray.getJSONObject(i);
        Log.i("Object" + i, jObject.toString());
        docInfo.add(new DocInfo(Long.valueOf(jObject.getString("doc_id")), jObject.getString("doc_name"), jObject.getString("doc_file_name"), jObject.getString("doc_date")));
    }
} catch (Exception e) {
}
CustomAdapter adapter = new CustomAdapter(this, R.layout.doc_list, docInfo);
setListAdapter(adapter);

The custom adapter

public class CustomAdapter extends ArrayAdapter<DocInfo> {
    private List<DocInfo> entries;
    private Activity activity;

    public CustomAdapter(Activity a, int textViewResourceId, ArrayList<DocInfo> entries) {
        super(a, textViewResourceId, entries);
        this.entries = entries;
        this.activity = a;
    }

    public static class ViewHolder{
        public TextView tv_doc_name;
        public TextView tv_doc_id;
        public TextView tv_doc_date;
    }

    @Override
    public int getCount(){
          return entries!=null ? entries.size() : 0;
    }

    @Override
    public DocInfo getItem(int index) {
        return entries.get(index);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            LayoutInflater vi = LayoutInflater.from(activity);
            convertView = vi.inflate(R.layout.doc_list, (ViewGroup) activity.findViewById(R.id.doc_list_row));
            holder = new ViewHolder();
            holder.tv_doc_name = (TextView) convertView.findViewById(R.id.doc_name);
            holder.tv_doc_id = (TextView) convertView.findViewById(R.id.doc_id);
            holder.tv_doc_date = (TextView) convertView.findViewById(R.id.doc_date);
            convertView.setTag(holder);
        }
        else
            holder=(ViewHolder) convertView.getTag();

        final DocInfo custom = entries.get(position);
        if (custom != null) {
            holder.tv_doc_name.setText(custom.get_doc_name());
            holder.tv_doc_id.setText(String.valueOf(custom.get_doc_id()));
            holder.tv_doc_date.setText(custom.get_doc_date());
        }
        Log.i("Custom" + position, custom.get_doc_name() + String.valueOf(custom.get_doc_id()) + custom.get_doc_date());
        return convertView;
    }
}

The JSON being used for testing

[
    {
        "doc_name": "Home Loan Agreement",
        "doc_file_name": "home_loan_agreement.pdf",
        "doc_id": "6781",
        "doc_date": "11-Mar-2017"
    },
    {
        "doc_name": "Personal Loan Agreement",
        "doc_file_name": "personal_loan_agreement.pdf",
        "doc_id": "2517",
        "doc_date": "19-Mar-2017"
    },
    {
        "doc_name": "Insurance Policy Proposal",
        "doc_file_name": "policy_proposal.pdf",
        "doc_id": "1291",
        "doc_date": "24-Mar-2017"
    }
]

Finally, the output showing only the last object of the JSON in the listview at the bottom of the page screen shot

When debugging, I see that the JSON has been read correctly and all 3 objects are present in the array list. The GetView method of the custom adapter is called for the 3 objects and the view is being formed and returned correctly. Tried different things for a long time but couldn't figure out where the first two items are why they are not showing up on the screen.

Any help will be greatly appreciated.

Sriman
  • 788
  • 9
  • 25

3 Answers3

2

You're inflating your list item layout incorrectly.

convertView = vi.inflate(R.layout.doc_list, (ViewGroup) activity.findViewById(R.id.doc_list_row));

In that particular inflate() overload, the second argument is the ViewGroup that the inflated View is added to after inflation. The first time your getView() method runs, activity.findViewById(R.id.doc_list_row) will return null, since a doc_list_row View doesn't exist in the hierarchy yet, so the inflated View will just get added to the ListView like normal, after the return. The next time getView() runs, however, there is a doc_list_row View, from the first list item, so the second item's View, and any ones inflated after that, get added inside the first item, stacking below its original child Views in its vertical LinearLayout.

Furthermore, the inflate() method returns the root View of whatever results from the inflation and (optional) addition. If the ViewGroup argument is null, there is nothing to add the inflated layout to, and it returns the root View of the just inflated layout. If the ViewGroup argument is not null, then that ViewGroup is returned as the root, since it is now the top parent. After your first list item is created, the getView() method is returning the same first item View repeatedly, and the ListView has problems reconciling the layout with its own internal logic, which is why the one item you do see isn't aligned to the top.

The correct way to inflate a list item is to pass the ViewGroup parent parameter as the second argument in the inflate() call, and false as a third argument, to indicate that the inflated layout should not be added to the parent there, since it will eventually be added to the ListView after the return.

convertView = vi.inflate(R.layout.doc_list, parent, false);

You may also want to make the layout_height of the root LinearLayout in the doc_list layout wrap_content, if just to be on the safe side. ListView should be smart enough to override that and wrap it instead, but it's best not to introduce anything potentially problematic.

Mike M.
  • 38,532
  • 8
  • 99
  • 95
  • Thanks a lot Mike. You are correct. However, I am surprised. I have the exact same code running well for several years in another app where I use a Custom Adapter implementing a filterable to show results for an AutoCompleteTextView. – Sriman Mar 26 '17 at 11:09
  • Yeah, I can see how that might still work in that case. `AutoCompleteTextView` uses a `DropDownListView`, which has slightly different behavior. All of the items after the first are still there in this `ListView`; they're just hidden below the `TextView`s inside the first, pushed out of its bounds, because the `ListView` is wrapping to the first's initial height. If the `DropDownListView` isn't forcing the wrap, you'd see what is actually 1 big item, each one after the first added to the bottom in the first vertical `LinearLayout`. (Just realized I misspoke in my post; meant below, not atop.) – Mike M. Mar 26 '17 at 11:41
  • Makes sense. Thanks. – Sriman Mar 26 '17 at 17:52
0

Replace by this code:

 <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/doc_signing"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical">
        <ListView android:id="@+id/android:list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:dividerHeight="1.5dp" />
        <TextView android:id="@+id/android:empty"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="22sp"
            android:text="@string/no_docs_to_sign"/>
    </LinearLayout>

You have kept listview height as fill_parent. This may occur problem to you. Change height to wrap_content

Rabindra Acharya
  • 1,868
  • 2
  • 18
  • 32
  • Thanks. I have tried this already. As such, having wrap_content on listview can cause performance issues. – Sriman Mar 26 '17 at 11:03
0

There is problem with your list view row item xml instead of parent layout height fill_parent make them wrap_content like this

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:id="@+id/doc_list_row"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/doc_name"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:textSize="22sp" >
    </TextView>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        <TextView
            android:id="@+id/doc_id"
            android:layout_weight="0.50"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingBottom="2dp"
            android:paddingLeft="5dp"
            android:paddingRight="3dp"
            android:textSize="16sp" >
        </TextView>
        <TextView
            android:id="@+id/doc_date"
            android:layout_weight="0.50"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingBottom="2dp"
            android:paddingLeft="3dp"
            android:paddingRight="3dp"
            android:textSize="16sp" >
        </TextView>
    </LinearLayout>
</LinearLayout>
shahid17june
  • 1,441
  • 1
  • 10
  • 15