1

Before I begin, I checked all the other answers in Stackoverflow.com. Sorry for bringing up another same old notifyDataSetChanged()-doesn't-work stuff.

If I find an answer, I will answer to my own question.

Hi, I am having difficulty in using ListViewAdapter which extends BaseAdapter as you see below. There are a lot of talks on this issue in Stackoverflow.com or many other blogs. Solution seems to converge to saying

1) Don't forget to set setListAdapter. 2) Use notifyDataSetChanged() when new data is added. (Some say use notifyDataSetInvalidate ... )

The tutorial I have been studying is

http://adilsoomro.blogspot.kr/2012/12/android-listview-with-speech-bubble.html

.

The problem is sometimes it works very well but it sometimes it doesn't.

There is no pattern.

Since the messages are transmitted from other system, I thought it was due to network problem. But all the messages were seamlessly coming into my app.

The reason I have not used ArrayList instance on ListViewActivity is that from certain point of system operations occur a case where ArrayList instance within ListViewAdapter fails to sync with the arrayList on ListViewActivity. And I've confirmed that there starts to exist a moment where some messages are put into list A(within ListViewActivty) and other messages into list B(within ListViewAdapter but I am not sure if this is really the case). That's why I removed the arraylist within ListViewActivty.

Update is done within onPostExecute part of AsyncTask, not using runUiThread (There was no difference bewteen using runUiThread and not).

And I've found when list view is not updated, getView() was not called either, even though message list in adapter had added data and getCount() returns exact number of refreshed data size.

I doubt the operation inside onPostExecute is once in a while blocked by some unknown process.

When I give UI event( for example, entering text ) on my app, it gives a late refresh of the added data on screen which should have been done when addNewMessage() was called().

=======================================================================

1. AsycTask in which addNewMessage() is called ( a part of ListViewActivity.java )

private class RefreshListView extends AsyncTask<Argument, String, Argument>
{
@Override
protected Argument doInBackground(Argument... _args) 
{ 
     Argument result = new Argument();

     result.setMessageString( _args[0].getMessageString() );
     result.setIsMine( _args[0].getIsMine() );

     return result;
}

      @Override
public void onProgressUpdate(String... v) 
{ 

}

@Override
protected void onPostExecute(Argument _arg ) 
{
    addNewMessage(new Message( _arg.getMessageString(), _arg.getIsMine() ) );  
}
} 

=======================================================================

2. A point where notifyDataSetChanged() is called. ( a part of ListViewActivity.java )

public void addNewMessage( final Message _message )
{
    adapter.add( _message ); 
    adapter.notifyDataSetChanged();
    getListView().setSelection( adapter.observeMessageList().size() - 1 );
}

=======================================================================

3. ListViewActivity setting ListViewAdapter as an observer

public void onCreate( Bundle savedInstanceState ) 
{
    adapter = new ListViewAdapter( this );
    this.setListAdapter( adapter );
}

=======================================================================

4. ListViewAdapter.java which has actual arrayList that stores data item

import java.util.ArrayList;
import java.util.Iterator;

import com.test.R;

import android.content.Context;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

// http://stackoverflow.com/questions/3669325/notifydatasetchanged-example
public class ListViewAdapter extends BaseAdapter
{
private Context mContext;

public static ArrayList<com.test.Message> mMessages = new ArrayList<com.test.Message>();

public ListViewAdapter(Context context ) 
{
     super();

     this.mContext = context;
}

public void clearMessages()
{
    ListViewAdapter.mMessages.clear();
}

public void add( Message  _message )
{
    ListViewAdapter.mMessages.add( _message );
}

public ArrayList<com.test.Message> observeMessageList()
{
     return ListViewAdapter.mMessages;
}

@Override
public int getCount() 
{ 
    return mMessages.size();
} 


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


@Override
public View getView( int position, View convertView, ViewGroup parent ) 
{
    com.test.Message message = (com.test.Message) this.getItem(position);

    ViewHolder holder; 

    if ( convertView == null )
    {
        holder = new ViewHolder();

        convertView = LayoutInflater.from(mContext).inflate(R.layout.sms_row, parent, false);

        holder.message = (TextView) convertView.findViewById(R.id.message_text);

        convertView.setTag(holder);
    }
    else
    {
        holder = (ViewHolder) convertView.getTag();
    }

    holder.message.setText( message.getMessage() );

    LayoutParams lp = (LayoutParams) holder.message.getLayoutParams();

    // check if it is a status message then remove background, and change text color.
    if ( message.isStatusMessage() )
    {
        holder.message.setBackgroundDrawable(null);

        lp.gravity = Gravity.LEFT;

        // holder.message.setTextColor( R.color.textFieldColor );
    }
    else
    {       
        // Check whether message is mine to show green background and align to right
        if ( message.isMine() )
        {
            holder.message.setBackgroundResource(R.drawable.speech_bubble_green);
            lp.gravity = Gravity.RIGHT;
        }
        // If not mine then it is from sender to show orange background and align to left
        else
        {
            holder.message.setBackgroundResource(R.drawable.speech_bubble_orange);
            lp.gravity = Gravity.LEFT;
        }

        holder.message.setLayoutParams(lp);

        // holder.message.setTextColor( R.color.textColor );    
    }

    return convertView;
}

public static class ViewHolder
{
    TextView message;
}

@Override
// http://stackoverflow.com/questions/6711592/what-is-the-intent-of-the-methods-getitem-and-getitemid-in-the-android-class-bas
public long getItemId(int position) 
{
    // Unimplemented, because we aren't using Sqlite.

    // http://stackoverflow.com/questions/24223393/android-listview-custom-adapter-getview-never-called-up
    return 0;
    // return position;
}

}

Calia Kim
  • 65
  • 14

1 Answers1

0

This is not an authenic way to deal with this problem.

But currently I have made some workaround by adding the method below. This is called within some separate thread per 1 second. So irrespective of whether or not actually new data is added, it just do notifyDataSetChanged() and if really there are some items added but failed to be seen on the UI as described above, it will be refreshed upon user screen.

public void refresh()
{
     this.runOnUiThread
     ( 
        new Runnable() 
        { 
            @Override 
            public void run() 
            { 
                  adapter.notifyDataSetChanged();

                  getListView().setSelection( adapter.observeMessageList().size() - 1 );
            } 
     });  
}

If I find some fundamental reason why it has been the case, I will elaborate on this issue.

Calia Kim
  • 65
  • 14