简体   繁体   中英

How to show chats by collecting messages in the custom list view adapter itself?

So, What I want to do is display chats in my activity using a custom list view adapter.

I have a HTTPTask Activity handling the server side interaction and responding with a JSONObject. So, every server side interaction is working fine.

What I want to do is keep updating the messages in the chat by keep checking with the API at a set interval to populate messages in the chat if there are any.

My question is, should this population process done in the adapter or the activity and how?

And, how does viewHolder help in the adapter?

This is my Activity

public class ChatActivity extends Activity {

TextView toUsername;
EditText replyText;
JSONObject resultObject;
StringBuilder reply,from_user_id,c_id;
MessageListViewAdapter myAdapter;
ListView listView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_chat);

    toUsername = (TextView) findViewById(R.id.toUsername);
    replyText = (EditText) findViewById(R.id.replyText);
    reply = new StringBuilder("");
    listView = (ListView) findViewById(R.id.messages);
}

@Override
public void onResume(){
    super.onResume();

    Bundle bundle = getIntent().getExtras();
    if(bundle != null){
        toUsername.setText("" + bundle.get("ToUsername").toString());
        c_id = new StringBuilder(bundle.get("c_id").toString());
        from_user_id = new StringBuilder(bundle.get("FromUserId").toString());
    }

    myAdapter = new MessageListViewAdapter(getBaseContext(),c_id.toString(),from_user_id.toString());
    listView.setAdapter(myAdapter);
}

public void sendTextMsg(View view){
    reply.delete(0,reply.length());
    reply.append(replyText.getText().toString());
    if(!reply.toString().equals("")){
        Log.d("Values: ","c_id: " + c_id.toString() + " FromUserId: " + from_user_id.toString() + "ReplyText: " + reply.toString());

        try{
            resultObject = new HttpTask(getBaseContext()).doInBackground("replyInChat",c_id.toString(),replyText.getText().toString(),from_user_id.toString());
            if(resultObject.get("status").toString().equals("true")) {
                Toast.makeText(getBaseContext(), "Sent.", Toast.LENGTH_SHORT).show();
                replyText.setText("");
            }
            else {
                Toast.makeText(getBaseContext(), "Try Again.", Toast.LENGTH_SHORT).show();
            }
        }
        catch(JSONException e){ }

    }
}
}

My Adapter doesn't seem to work.

public class MessageListViewAdapter extends BaseAdapter implements ListAdapter{

private ArrayList<String> list = new ArrayList<String>();
private Context context;
private StringBuilder conversation_id, user_id;
private static int cr_id;
private JSONArray messages;
private JSONObject resultObject;
private ViewHolder viewHolder;
private View rowView;

public MessageListViewAdapter(Context context, String conversation_id, String user_id) {
    this.context = context;
    this.conversation_id = new StringBuilder(conversation_id.toString());
    this.user_id = new StringBuilder(user_id.toString());
    cr_id=0;
}

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

@Override
public Object getItem(int pos) {
    return list.get(pos);
}

@Override
public long getItemId(int pos) {
    //return list.get(pos).getId();
    //just return 0 if your list items do not have an Id variable.
    return 0;
}

@Override
public boolean isEnabled(int position){
    return false;
}

static class ViewHolder{
    public TextView ItemText;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    rowView = convertView;

    if (rowView == null) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        rowView = inflater.inflate(R.layout.message_list_layout, null);
        //configure view holder
        viewHolder = new ViewHolder();
        viewHolder.ItemText = (TextView) rowView.findViewById(R.id.list_item_text);
        rowView.setTag(viewHolder);
    }
    else {
        //fill data
        viewHolder = (ViewHolder) rowView.getTag();
    }

    try{
        Log.d("cr_id: ",String.valueOf(cr_id).toString());
        //This is where the population should've taken place but didn't.
        resultObject = new HttpTask(context).doInBackground("sendMessages",conversation_id.toString(),String.valueOf(cr_id));
        if(resultObject.get("status").toString().equals("true")) {
            messages = resultObject.getJSONArray("messages");
            Log.d("Messages: ",messages.toString());
            for(int i=0;i<=messages.length();i++){
                list.add(messages.getJSONObject(i).get("reply_text").toString());
            }
        }
    }
    catch(JSONException e){ }

    //Handle TextView and display string from your list
    //final TextView listItemText = (TextView)rowView.findViewById(R.id.list_item_text);
    //listItemText.setText(list.get(position));
    viewHolder.ItemText.setText(list.get(position));

    return rowView;
}
}

If every thing is working fine and you have problem in showing latest chat message in adapter just change your code like this:

try{
        Log.d("cr_id: ",String.valueOf(cr_id).toString());
        //This is where the population should've taken place but didn't.
        resultObject = new HttpTask(context).doInBackground("sendMessages",conversation_id.toString(),String.valueOf(cr_id));
        if(resultObject.get("status").toString().equals("true")) {
            messages = resultObject.getJSONArray("messages");
            Log.d("Messages: ",messages.toString());
            for(int i=0;i<=messages.length();i++){
                list.add(messages.getJSONObject(i).get("reply_text").toString());
                this.notifyDataSetChanged(); // add this line
            }
        }
    }
    catch(JSONException e){ }

Comment below for any further information

Personally i would do the network calls outside of the adapter. With the code currently if the user was to scroll up and down the list the network call would call multiple times which is something im sure you dont want.

What may be a better solution is having a method inside the activity that does the call, then have a timer set up that calls that method say every 2 - 3 minutes to save on the network calls, you could also add a refresh button for the user which gives them the choice of refreshing the data themselves which would just call the same method.

The View Holder design pattern can help speed up a listview and keep it smooth, Think of it this way, when the page first loads, getView will be called a number of times to fill up the list view. In the getView method you instantiate your UI widgets ie textview = (TextView)findviewbyid. Now what the view holder does is keep a reference to these ui elements which means you wont have to keep calling findViewById.

Here is an article that explains it a bit better and go into some examples.

http://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html

So lets say you do the network code in the activity. When you get a response you can simply add the message to the list then notifyDataSetChanged();

So, Finally I got it working with some experimentation. Many thanks to Manikanta and Andy Joyce for their valuable answers. If it weren't for them i wouldn't have gone any further from where I was stuck.

This is what I changed in my custom adapter.

public void add(ArrayList<String> list){
    this.list.clear();
    this.list.addAll(list);
    Log.d("List: ",this.list.toString());
    this.notifyDataSetChanged();
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    rowView = convertView;

    if (rowView == null) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        rowView = inflater.inflate(R.layout.message_list_layout, null);
        //configure view holder
        viewHolder = new ViewHolder();
        viewHolder.ItemText = (TextView) rowView.findViewById(R.id.list_item_text);
        rowView.setTag(viewHolder);
    }

    else {
        //fill data
        viewHolder = (ViewHolder) rowView.getTag();
    }

    viewHolder.ItemText.setText(list.get(position));

    return rowView;
}

This is what I added to my activity

    @Override
public void onResume(){
    super.onResume();

    Bundle bundle = getIntent().getExtras();
    if(bundle != null){
        toUsername.setText("" + bundle.get("ToUsername").toString());
        c_id = new StringBuilder(bundle.get("c_id").toString());
        from_user_id = new StringBuilder(bundle.get("FromUserId").toString());
        //list.add(c_id.toString());
        //list.add(from_user_id.toString());
    }

    myAdapter = new MessageListViewAdapter(getBaseContext(),c_id.toString(),from_user_id.toString());
    listView.setAdapter(myAdapter);

    callAsynchronousTask();
    //myAdapter.add(list);
}
@Override
public void onPause(){
    super.onPause();
    timer.cancel();
}
public void callAsynchronousTask() {
    final Handler handler = new Handler();
    timer = new Timer();
    TimerTask doAsynchronousTask = new TimerTask() {
        @Override
        public void run() {
            handler.post(new Runnable() {
                public void run() {
                    //list.clear();
                    try{
                        resultChatObject = new HttpTask(getBaseContext()).doInBackground("sendMessages",c_id.toString(),String.valueOf(cr_id));
                        if(resultChatObject.get("status").toString().equals("true")) {
                            //list.clear();
                            messages = resultChatObject.getJSONArray("messages");
                            Log.d("Messages: ",messages.toString());
                            for (int i = 0; i <= messages.length(); i++) {
                                list.add(messages.getJSONObject(i).get("reply_text").toString());
                                if (cr_id < Integer.parseInt(messages.getJSONObject(i).get("cr_id").toString()))
                                    cr_id = Integer.parseInt(messages.getJSONObject(i).get("cr_id").toString());
                            }
                        }
                    }
                    catch (JSONException e) { }

                    myAdapter.add(list);
                }
            });
        }
    };
    timer.schedule(doAsynchronousTask, 0, 10000); //execute in every 10000 ms
}

Cheers everyone!!!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM