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
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.
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 View
s 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.