Only in some phones, ListView not refresh after notifyDataSetChanged()
Asked Answered
H

8

17

It is weirded because it happens only in some phones those resolution are FHD.

When UI is shown, everything seems ok. When I click items and call notifyDataSetChanged(), item won't refresh it's look. I need to click on the ListView anywhere again, the item will refresh layout to the correct look.

If the listview changes size (ex: search function will redesign whole layout), everything becomes OK.

Here is the ListView code:

public final class MyListView extends ListView implements AdapterView.OnItemClickListener
{

    ArrayList<SELECT_ITEM> selectList;
    ArrayList<ID_ITEM> idList;
    ShowItemAdapter showAdapter;

    public MyListView(Context context) 
    {
        selectList = new ArrayList<SELECT_ITEM>();
        idList = new ArrayList<ID_ITEM>();

        readIdList(mIdList);
        showAdapter = new ShowItemAdapter(context, idList, selectList);
        setAdapter(showAdapter);        

        ...
    }

    @Override
    protected void onFinishInflate() {
        setOnItemClickListener(this);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long ID) {
        boolean itemIsSelected = true;
        int size = selectList.size();

        // remove if click item in selectList
        for(int i=0 ; i<size ; i++) {
            int selectID = selectList.get(i).id;
            if (idList.get(position).id == selectID) {  
                itemIsSelected = false;
                selectList.remove(i);
                showAdapter.notifyDataSetChanged();
                break;
            }
        }

        if (itemIsSelected) {
            SELECT_ITEM item = new SELECT_ITEM();
            item.id = idList.get(position);
            selectList.add(item);
            // Here
            showAdapter.notifyDataSetChanged();
        }
    ...
    }

    ....
}

And here is Adapter code,

    public final class ShowItemAdapter extends BaseAdapter{

    public ArrayList<ID_ITEM>   mIdList;
    public ArrayList<SELECT_ITEM>   mSelectList;

    public ShowItemAdapter(Context context,
        ArrayList<ID_ITEM> idList,
        ArrayList<SELECT_ITEM> selectList)
    {
        mIdList = idList;
        mSelectList = selectList;
    }

    @Override
    public int getCount() {
        int ret = mIdList.size();
        return ret;
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

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

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

        ...

        for(int i=0 ; i<size ; i++)
        {
            // is selected
            if (mIdList.get(position).id == 
                selectList.get(i).id)
            {
                mIsSelected = true;
                break;
            }
        }

        if (mIsSelected)
        {
            textView.setBackgroundColor(Color.red);
        }
        else
        {
            textView.setBackgroundColor(Color.white);
        }
    }
}

Anybody give me a hand?

I found those phones work correctly will cause the same issue when debugging pause at getView(). I think .. it is like "Views has been updated so it won't refresh views." But views update during debugging make the ui not refresh actually, then it works in the wrong way.

I guess this is about refreshing views.

Huntlee answered 4/6, 2015 at 2:54 Comment(13)
can you share code/snippet, how and where do you call notifyDataSetChanged(), ?Dayle
I have added code and simplified it. Actually, it works fine on some phones. So that's why I donno where is the problem.Huntlee
I suspect that selectList from onClick() is the same object that is referenced in the adapter as mSelectList, right? It is a bad practice in general, but please show us the whole relevant code (e.g. how itemIsSelected assigned) in order to answer your question.Coyote
SelectList and IdList are never been set as null or assigned with another list. I have searched in StackOverflow about those might occur errors. Thanks for your help.Huntlee
please post onInit code as well.. is there any other clicklistner you are using in this listview..Whereto
Just post your whole activity code...Tonitonia
This may not help, but you should call super.ononFinishInflate() from onFinishInflate(). The documentation states: "Even if the subclass overrides onFinishInflate, they should always be sure to call the super method, so that we get called."Retrogression
Sorry but my source code is too large and with some reason I cannot post them all. I have added super.onFinishInflate(). Thank you all.Huntlee
You could post relevant code here. If too much code since everything seems so relevant, post them at Github. You may have to explain in more details of what is working and not. And we don't have much time to meet the bounty deadline.Manuscript
Looking at the code and thought about it for some time, I doubt this issue appears only in some phones and working fine with others. I can believe the issue is intermittent.Manuscript
How is this going so far? If you made code changes, pls post the updated code so we can help you.Manuscript
is that all your getview? because i doubt, and your problem has to be with the views not the data backing it up, so you need to major on posting the getview, also why do you loop to find your selected item in your list? you already have positionGlucosuria
Currently this post has been upvoted 17 times, obviously attracting much attention. I am surprise that a problem so specific to a developer's code design can attract many people. Am I the only one who is surprised?Manuscript
M
1

I see a possible code conflict and a timing issue between getView() of the adapter and the onItemClick().

Allow me to explain. In getView(), there is code

if (mIdList.get(position).id == selectList.get(i).id)

Note: This checks the value of selectList, which is not even declared in this Adapter class. I assume you meant mSelectList. But let's move on...

In onItemClick(), there is code

if (idList.get(position).id == selectID) {  
   ...
   selectList.remove(i);
   ...

Note: This code removes an item from selectList, while in getView(), it is checking for the same object. We cannot know which code will run first. But I have an idea... getView() is a virtual callback method where the BaseAdapter may trigger the method when it requires a row or item to display. So when you remove an item from a Listview, the Adapter may not request a refresh, no matter what you do in code. And the adapter ShowItemAdapter is responsible for all row/views refresh.

Suggestion: Place the same code (for remove/add item) in getView() to avoid these kind of code conflicts.

Let us know and good luck with this odd anomaly.
Tommy Kwee

Manuscript answered 9/6, 2015 at 6:27 Comment(2)
I tried, and it became more strange. When clicking all the items, these views won't refresh. Until clicking on the gap between the items or on the Listview blank space, the whole view will refresh.Huntlee
I and some of us need to see the most updated code, pls post them here or on Github. As for clicking the gap, I am guessing this is a layout xml issue, found in getView(). Besides that, is it refreshing correctly now, after clicking on the gaps?Manuscript
S
0

You need to call super methods in in constructor and onFinishInflate functions of MyListView class. That might have caused the issue.

public MyListView(Context context) 
 {
    super(context);
    selectList = new ArrayList<SELECT_ITEM>();
    ..
    ..
}

and

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        setOnItemClickListener(this);
    }
Steck answered 8/6, 2015 at 13:53 Comment(1)
I added it but no effect. Thank you anyway.Huntlee
I
0

It's the bug on Android 4.0.3, notifyDataSetChanged doesn't work from onItemClickListener.

So the solution would be something like this:

  1. Set onItemClickListener separately to each view in adapter
  2. Dispatch it to where ever you'd like to handle it
  3. Do what ever you want and call notifyDataSetChanged from there
Ingredient answered 8/6, 2015 at 14:30 Comment(1)
I run my code on Android 4.1.2, and in fact I have already done that with your solutions. Thank you anyway.Huntlee
M
0

Normally I update data on my Adapter, and I don't get this kind of problem, I believe it's more solid. Since you are updating data (selectList) in the Listview, the refresh/update is a bit indirect, I think. I would suggest at first to try.

Code samples after code below:

selectList.remove(i);
showAdapter.notifyDataSetChanged();

ADD:

  1. invalidate() OR
  2. invalidateViews()

Note: I notice your solution of calling onInit() which I have no idea of what it is. It seems there is a timing issue involved, meaning you should not have to run the code on a new/separate thread.

Manuscript answered 9/6, 2015 at 0:2 Comment(0)
P
0

Below could help. I had similar issue in 4.4.2 and this helped:

//clear the adapter    
showAdapter.clear();
//add all the data in your list to adapter again
showAdapter.addAll(selectList);

You do not need to call notifyDataSetChanged as it will do it in addAll. Hope that helps.

Philander answered 12/6, 2015 at 11:26 Comment(0)
T
0

You should update your ListView Synchronously.

 public final class ShowItemAdapter extends BaseAdapter{

public ArrayList<ID_ITEM>   mIdList;
public ArrayList<SELECT_ITEM>   mSelectList;

public ShowItemAdapter(Context context,
    ArrayList<ID_ITEM> idList,
    ArrayList<SELECT_ITEM> selectList)
{
    mIdList = idList;
    mSelectList = selectList;
}

 public synchronized void refreshData(ArrayList<SELECT_ITEM> items) {
    this.mSelectList = items;
    notifyDataSetChanged();
}

Then call the refreshData() method whenever you make changes to you list

 selectList.remove(i);
 refreshData(selectList);

or

 selectList.add(i);
 refreshData(selectList);

This will definitely refresh user list view with the updated List of items

Thalamus answered 12/6, 2015 at 20:22 Comment(1)
Your answer is interesting to me. But I could not recommend this technique to everyone else, mainly because, with this code, contents of mSelectList can be changed from either the Adapter or in the caller MyListView class. That's why I suggested to the author timyau to make a minor redesign of code.Manuscript
W
0

Rather than using showAdapter.notifyDataSetChanged(); just update your data source I mean selectList and reinitialize your adapter and attach it to the ListView.

replace this

selectList.remove(i);
showAdapter.notifyDataSetChanged();

and

selectList.add(item);
showAdapter.notifyDataSetChanged();

with this respectively

selectList.remove(i);
// or selectList.add(item);
  showAdapter = new ShowItemAdapter(context, idList, selectList);
        setAdapter(showAdapter);     
Weintraub answered 15/6, 2015 at 7:31 Comment(0)
H
-1

I found a solution although it still works with some issues.
I call below at Activity.onCreate(), and only in onInit() MyListView will be initialled :

    postDelayed(new Runnable()
    {
        public void run()
        {
            onInit(icicle); // initial ui
        }
    }, 100);

It makes the view shows slower but works fine.
If delayed about 100 milli-seconds, it might cause those work fine phones become bad ones. Delayed about 350 milli-seconds makes both phones work well. Is there better answers?

Huntlee answered 5/6, 2015 at 8:31 Comment(1)
I know it is bad so I need suggestions to correct this problem. But I think it shouldn't be wrong on only some phones, it doesn't make sense!Huntlee

© 2022 - 2024 — McMap. All rights reserved.