Reputation: 1989
As part of my Android app, I am integrating a very simple custom group chat feature; this is just a proof-of-concept feature. I am aware of the fact that this is transient, and that my chats disappear whenever I restart my app. My app uses Fragments and a ViewPager to create tabs within my app.
What I have backing this feature is a LinkedList which holds Message objects (send time, sender name, message). Whenever a chat is sent or received, it is added to the LinkedList. At the start of my MainActivity.java, I declare the list
public static LinkedList<Message> chatList;
In the onCreate method, I initialize the list
chatList = new LinkedList<Message>();
I have a listener thread that runs in the background that listens for incoming messages. I passed the chatList into this thread in its constructor
MyListener myListener = new MyListener(MainActivity.this, chatList);
Thread listenerThread = new Thread(myListener);
listenerThread.start();
Whenever the listener thread receives a message, it sticks it into the LinkedList
chatList.add(new_message_object);
To display the chats, I use a ListView that is backed by a custom ArrayAdapter, in which I have overridden the getView() method. The ArrayAdapter gets the array from the toArray() method of my LinkedList and displays the chats on the screen.
This process almost works. Whenever chats are received, the LinkedList is successfully populated. The problem is getting the ListView to immediately update and display the chats. If I switch to a new fragment/tab within my app, then switch back to my chat tab, then the list is populated; but, I have to do this whenever I want to see any new chats.
This is also true for any chats I enter locally (i.e., within my chat feature). It is still added to the LinkedList, but the ListView does not refresh.
I don't want to use a SwipeRefreshLayout, I would rather the list update itself. I created a method within the custom ArrayAdapter
public void refreshList(){
this.notifyDataSetChanged();
}
I call this whenever a new message object is added to the LinkedList, but it is not refreshing the list.
So, what am I doing incorrectly? Like I said, I would prefer the ListView update itself like a proper chat program does.
Thanks
EDIT:
As requested, here is my custom ArrayAdapter
public class CustomArrayAdapter extends ArrayAdapter {
Context context;
int resource;
Object[] objects;
public CustomArrayAdapter(Context context, int resource, Object[] objects) {
super(context, resource, objects);
this.context = context;
this.resource = resource;
this.objects = objects;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
MessageHolder messageHolder = null;
if(row == null){
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(resource, parent, false);
messageHolder = new MessageHolder();
messageHolder.chat_information = (TextView) row.findViewById(R.id.chat_information);
messageHolder.chat_message = (TextView) row.findViewById(R.id.chat_message);
row.setTag(messageHolder);
} else {
messageHolder = (MessageHolder) row.getTag();
}
Message item = (Message) objects[position];
messageHolder.chat_information.setText(item.getSenderName() + Constants.NEWLINE + item.getSendTime());
messageHolder.chat_message.setText(item.getMessageText());
return row;
}
public void refreshList(){
this.notifyDataSetChanged();
}
private class MessageHolder {
TextView chat_information;
TextView chat_message;
}
}
EDIT 2:
I am adding my Chat.java fragment.
public class Chat extends Fragment {
private View rootView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.chat, container, false);
return rootView;
}
@Override
public void onViewCreated(View rootView, Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
displayChats();
}
@Override
public View getView() {
Button sendChatButton = (Button) rootView.findViewById(R.id.text_send);
sendChatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Date rightNow = new Date();
SimpleDateFormat timeSDF = new SimpleDateFormat(Constants.SIMPLE_TIME);
SimpleDateFormat dateSDF = new SimpleDateFormat(Constants.SIMPLE_DATE);
SharedPreferences myAppPreferences = getContext().getSharedPreferences(Constants.PREFS_NAME, getContext().MODE_PRIVATE);
EditText chatEntryWindow = (EditText)rootView.findViewById(R.id.chat_text_compose);
String message = chatEntryWindow.getText().toString();
String username = myAppPreferences.getString("username", Constants.TABLET_ID);
Message myMessage = new Message(username, message, 0, dateSDF.format(rightNow), timeSDF.format(rightNow));
CustomArrayAdapter caa = new CustomArrayAdapter(getActivity(), R.layout.outgoing_line_of_chat, MainActivity.chatList);
caa.add(myMessage);
caa.notifyDataSetChanged();
new SendChat(getActivity(), message, username).execute();
}
});
return super.getView();
}
public void displayChats(){
ListView list = (ListView) rootView.findViewById(R.id.chat_text_display);
ArrayAdapter adapter = new CustomArrayAdapter(getActivity(), R.layout.outgoing_line_of_chat, MainActivity.chatList);
list.setAdapter(adapter);
}
}
Upvotes: 0
Views: 1967
Reputation: 3885
Change the code like this . This will not make too many changes ,
public class CustomArrayAdapter extends ArrayAdapter {
Context context;
int resource;
List objects ;
Activity activity ;
public CustomArrayAdapter(Context context, int resource, List objects) {
super(context, resource, objects);
this.context = context;
this.resource = resource;
//clone the arraylist.
this.objects = new ArrayList(objects);
activity = (Activity) context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
MessageHolder messageHolder = null;
if (row == null) {
LayoutInflater inflater = LayoutInflater.from(context);
row = inflater.inflate(resource, parent, false);
messageHolder = new MessageHolder();
messageHolder.chat_information = (TextView) row.findViewById(R.id.chat_information);
messageHolder.chat_message = (TextView) row.findViewById(R.id.chat_message);
row.setTag(messageHolder);
} else {
messageHolder = (MessageHolder) row.getTag();
}
Message item = (Message) objects.get(position);
messageHolder.chat_information.setText(item.getSenderName() + Constants.NEWLINE + item.getSendTime());
messageHolder.chat_message.setText(item.getMessageText());
return row;
}
public void setObjects(List newObjects)
{
if (objects != null)
{
objects.clear();
objects.addAll(newObjects);
}
}
public void refreshList() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
}
private class MessageHolder {
TextView chat_information;
TextView chat_message;
}
}
And also one each chat message call setObjects()
method with the linked list and call refreshList()
method
Upvotes: 0
Reputation: 1802
use the following after inserting new data in array adapter:
yourAdapterName.notifyDataSetChanged();
Upvotes: 1
Reputation: 1006664
Your CustomArrayAdapter
does not use your LinkedList
. It uses an Object[]
. I assume, from your question and comments, that when you create your CustomArrayAdapter
, at that point, you call toArray()
on the LinkedList
to get your Object[]
.
At this point, though, the Object[]
is completely decoupled from the LinkedList
. Suppose, at the outset, the LinkedList
has 5 Message
objects. You call toArray()
, and you get an Object[5]
containing those 5 Message
objects. You later call add()
on the LinkedList
, to add a 6th Message
. The Object[5]
is still an Object[5]
and knows nothing about that 6th Message
. The only way the AdapterView
will update is if you replace the CustomArrayAdapter
entirely.
So, get rid of the Object[]
. ArrayAdapter
takes a List
in half of its constructors. So, replace:
public class CustomArrayAdapter extends ArrayAdapter {
Context context;
int resource;
Object[] objects;
public CustomArrayAdapter(Context context, int resource, Object[] objects) {
super(context, resource, objects);
this.context = context;
this.resource = resource;
this.objects = objects;
}
with:
public class CustomArrayAdapter extends ArrayAdapter<Message> {
Context context;
int resource;
public CustomArrayAdapter(Context context, int resource, List<Message> messages) {
super(context, resource, messages);
this.context = context;
this.resource = resource;
}
Then, in getView()
, replace Message item = (Message) objects[position];
with Message item=getItem(position);
.
Finally, when a new Message
arrives, call add()
on the CustomArrayAdapter
, which will both update your List<Message>
and update the attached AdapterView
.
Upvotes: 0