0

When the user clicks an item on the list view, I am changing the background color manually of the selected listView item. I plan on changing more than just the background, (eventually icons and more), therefore I need the listview to redraw itself.

The problem is when I notifyDataChanged nothing happens. Because of the Log's I placed in the functions, when the item is clicked, I can verify that onItemClick is triggered, and also that the notifyDataSetChanged call is made to the adapter.

I would expect that "getView" should be called again for each position, where the data gets linked to the views. But this is not occuring. There is no sign of of anything happening. What am I missing here?

class MyOnItemClickListener implements AdapterView.OnItemClickListener{

    @Override
    public void onItemClick(final AdapterView<?> parent, View view, 
            final int position, long id) {

            Log.i(TAG, "listview on click " + position);

            MyListAdapter adapter = (MyListAdapter) parent.getAdapter();

            adapter.mSelected = position;
            adapter.notifyDataSetChanged();
    }
}


class MyListAdapter extends BaseAdapter{

    public List<String> mTxtContents;

    public int mGroupCount;
    public int mSelected;

    public void setData(List<String> txtContents){

        mTxtContents = txtContents;
        mGroupCount = txtContents.size();
    }

    @Override
    public void registerDataSetObserver(DataSetObserver observer) {
        // TODO Auto-generated method stub

    }

    @Override
    public void notifyDataSetChanged() {

        Log.i(TAG, "notify data set changed");

        // TODO Auto-generated method stub
        super.notifyDataSetChanged();
    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {
        // TODO Auto-generated method stub

    }

    @Override
    public int getCount() {

        Log.i(TAG, "nva panel group count " + mGroupCount);

        return mGroupCount;
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return null;
    }

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

    @Override
    public boolean hasStableIds() {
        // TODO Auto-generated method stub
        return false;
    }

    @SuppressLint("InflateParams")
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        Log.i(TAG, "get view, " + position);

        if(convertView == null){
            LayoutInflater li = LayoutInflater.from(getActivity());
            convertView = li.inflate(R.layout.lvitem_navpanel, parent, false);
        }

        TextView tv = (TextView) convertView.findViewById(R.id.tv_txtData);
        tv.setText(mTxtContents.get(position));

        if(mSelected == position){
            convertView.setBackgroundColor(Color.parseColor("#00FF00"));
        }else{
            convertView.setBackgroundColor(getResources().getColor(android.R.color.transparent));
        }

        return convertView;
    }

    @Override
    public int getItemViewType(int position) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int getViewTypeCount() {
        // TODO Auto-generated method stub
        return 1;
    }

    @Override
    public boolean isEmpty() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean areAllItemsEnabled() {
        // TODO Auto-generated method stub
        return true;
    }

    @Override
    public boolean isEnabled(int position) {
        // TODO Auto-generated method stub
        return true;
    }
}
NameSpace
  • 10,009
  • 3
  • 39
  • 40
  • 1
    You need to finish implementing your MyListAdapter's getItem(int position). Also, don't override registerDataSetObserver and unregisterDataSetObserver unless you need to. ListView uses this observer mechanism to know when you've called notifyDataSetChanged, yet your empty implementation of these methods prevents this from happening. Just let the BaseAdapter take care of that for you. – Michael Krause Oct 23 '14 at 23:12
  • I didn't think your answer would work...but it did :) The part that worked is deleting the override of register/unreigster functions. Deleting the getItem() function didn't affect it, as it is never called when I put a Log there. Eclipse auto filled the unregister/register functions without calling the super function, and I guess that broke the list view. If you want to put that as an answer I will mark it as correct. – NameSpace Oct 23 '14 at 23:29

1 Answers1

1

Try setting your adapter again after you modify it. Like so:

MyListAdapter adapter = (MyListAdapter) parent.getAdapter();
adapter.mSelected = position;
parent.setAdapter(adapter);

The new call to setAdapter will clear the recycler.

edit:

I'm not sure why you're using the pattern you posted above (to me it looks like unnecessary boilerplate and processing, but maybe you have a special need), the following might be of help to you

In your OnClick listener you can do something like

TextView txtView = (TextView)listView.getChildAt(position);
txtView.setBackgroundColor(Color.RED);
txtView.setText("New Text");

This will automatically apply the changes to your list item.

If you require to refresh a whole row you can do

View view = list.getChildAt(position);
list.getAdapter().getView(position, view, list);

Source

Community
  • 1
  • 1
prettyvoid
  • 3,446
  • 6
  • 36
  • 60
  • This works as well, so +1. Setting the listview with the adapter it's currently set to forces getView to be called, and a redraw on all children. I couldn't mark this as the best answer though, because I do not know the costs for doing this. For instance, whether values cached in the listView's children (e.g. viewHolders) are lost, state info, like which groups were expanded in the case of an expandable list view, or the overhead of re-setting the adapter (even if it is the same adapter). – NameSpace Oct 23 '14 at 23:52
  • What other stuff worked for you? Other than resetting the adapter? Good luck however. – prettyvoid Oct 23 '14 at 23:55
  • The answer provided @Michael Krause in the comments worked. The problem was eclipse suggested overriding the "unimplemented methods" by creating stubs for unregister/register data observer, without making a call to the super function. – NameSpace Oct 23 '14 at 23:58
  • So without those overrides a simple notifyDataSetChanged() sufficed to show the new background color? Just wondering for science :) – prettyvoid Oct 24 '14 at 00:00
  • yes, a simple notifyDataSetChanged() works. That's how it is suppose to work :) It notifies the adapterView it's attached to that its child views need to be refreshed. You would think invalidate view would work similarly, but it doesnt... It doesn't cause the child views to requery their data source, just redraw themselves with the data they currently hold (if even that). – NameSpace Oct 24 '14 at 00:04
  • Your method in the edit wouldn't work correctly for a variety of reasons. First, view recycling. It may work on a listview that has no items off the screen, but the moment view recycling is introduced the background color would no longer match the position you assigned it. There's some other reasons too...but I'll just say notifydatachanged is quite convenient than writing methods for every data change (background, icons, text, etc), and then writing variables and methods to track and undo changes from previous changes. – NameSpace Oct 24 '14 at 01:13
  • True what you said. I didn't give recycling much thought when I wrote the edit. – prettyvoid Oct 24 '14 at 09:16