androidlistviewconvertview

My convertView in getView does not seem to be implemented properly, can you tell me why?


My listView seems to load correctly but on testing I think there's something wrong with it.

I'm looking at this tutorial : http://android.amberfog.com/?p=296#more-296

I've added the line:

System.out.println("getView " + position + " " + convertView);

Going by the tutorial, in logcat I'm supposed to see convertView values as I scroll down my listView, like :

System.out(947): getView 20 android.widget.LinearLayout@437430f8

But in my logcat all my convertViews are null. I don't even scroll the listView and my 122 rows are immediately inflated - judging by logcat. All the values are of the form:

System.out: getView number is :21convertView is : null

Am I doing something wrong?

Here's my code:

public class SelectPhoneContactAdapter extends BaseAdapter {

    //define a list made out of SelectPhoneContacts and call it theContactsList
    public List<SelectPhoneContact> theContactsList;
    //define an array list made out of SelectContacts and call it arraylist
    private ArrayList<SelectPhoneContact> arraylist;
    Context _c;

    //define a ViewHolder to hold our name and number info, instead of constantly querying
    // findviewbyid. Makes the ListView run smoother
    // ViewHolder viewHolder;

    public SelectPhoneContactAdapter(final List<SelectPhoneContact> selectPhoneContacts, Context context) {
        theContactsList = selectPhoneContacts;
        _c = context;
        this.arraylist = new ArrayList<SelectPhoneContact>();
        this.arraylist.addAll(theContactsList);

    }


    @Override
    public int getCount() {
        System.out.println("the amount in arraylist :" + theContactsList.size());
        return arraylist.size();
    }

    @Override
    public Object getItem(int i) {
        return theContactsList.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)

    static class ViewHolder {
        //        In each cell in the listview show the items you want to have
//        Having a ViewHolder caches our ids, instead of having to call and load each one again and again
        TextView title, phone;
        CheckBox check;
    }

    @Override
    public View getView(int i, View convertView, ViewGroup viewGroup) {
        System.out.println("getView number is :" + i + "convertView is : " + convertView);

        ViewHolder viewHolder = null;

        if (convertView == null) {

            //if there is nothing there (if it's null) inflate the view with the layout
            LayoutInflater li = (LayoutInflater) _c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = li.inflate(R.layout.phone_inflate_listview, null);

            viewHolder = new ViewHolder();

            //      So, for example, title is cast to the name id, in phone_inflate_listview,
//        phone is cast to the id called no etc
            viewHolder.title = (TextView) convertView.findViewById(R.id.name);
            viewHolder.phone = (TextView) convertView.findViewById(R.id.no);

            viewHolder.check = (CheckBox) convertView.findViewById(R.id.checkBoxContact);
            viewHolder.check.setVisibility(View.GONE);

            //remember the state of the checkbox
            viewHolder.check.setOnCheckedChangeListener((NewContact) _c);

            convertView.setTag(viewHolder);

        } else {

            viewHolder = (ViewHolder) convertView.getTag();

        }
//        store the holder with the view
            final SelectPhoneContact data = (SelectPhoneContact) theContactsList.get(i);
            //in the listview for contacts, set the name
            viewHolder.title.setText(data.getName());
            //in the listview for contacts, set the number
            viewHolder.phone.setText(data.getPhone());

            viewHolder.check.setChecked(data.isSelected());
            viewHolder.check.setTag(data);

        return convertView;

    }
}

In my activity I use a function that measures the height of the listview before it loads on screen, perhaps this is part of the problem. Not sure it can be resolved or not, but if it can, that would be great.

From my NewContact.java:

 @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);

            adapter = new SelectPhoneContactAdapter(selectPhoneContacts, NewContact.this);

            adapter.notifyDataSetChanged();
            listView.setAdapter(adapter);

            justifyListViewHeightBasedOnChildren(listView);

        }

justifyListViewHeightBasedOnChildren function :

//this is the function we call to measure the height of the listview
//we need this because there are problems with a listview within a scrollview
public static void justifyListViewHeightBasedOnChildren (ListView listView) {

    ListAdapter adapter = listView.getAdapter();

    if (adapter == null) {
        return;
    }
    ViewGroup vg = listView;
    int totalHeight = 0;
    for (int i = 0; i < adapter.getCount(); i++) {
        View listItem = adapter.getView(i, null, vg);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();
    }

    ViewGroup.LayoutParams par = listView.getLayoutParams();
    par.height = totalHeight + (listView.getDividerHeight() * (adapter.getCount() - 1));
    listView.setLayoutParams(par);
    listView.requestLayout();

    System.out.println("the getcount is " + adapter.getCount());
    System.out.println("the height is " + par.height);
}

Solution

  • When ListView calls the getView() method, whether or not the convertView parameter has a non-null value depends on whether or not the ListView is trying to "recycle" a view. Recycling happens when a view that was previously on screen got scrolled (far enough) off-screen that the ListView is confident it won't be needed for a while and can be re-used ("recycled") to display the values for a "new" view that is about to scroll on-screen.

    The idea here is that all of the children of the ListView have the same structure; they have the same Views in the same places. It's only the contents of those views that are changing. So rather than inflating yet another view, an old one that isn't in use any more can be recycled.

    Regardless of how it happens, if your ListView is large enough that it can hold all of its children at once, then recycling is impossible. You have essentially created a glorified LinearLayout. Whether or not that is a problem is really up to you and your requirements. But when this happens, the whole "point" of ListView (view recycling) won't happen.

    All of that is to say: seeing null for all 122 values of convertView in all 122 calls to getView() means that you are using ListView differently than expected. If your app still runs perfectly, there's no strict need to change it. But know that you're not working within the expected boundaries of Google's framework.