Hi I am creating a chat application based on Firebase and Parse. In this I referred official Firebase chat example. The chat is working perfect but the incoming and sending chat both display one by one. Like this, for example
author: sender
message: hi
author: receiver
message: hi how r u?
I just need the chat to look like whats app and other chat in left and right alignment.
In firebase chat they are using two adapter one is FirebaseListAdapter (generic, which extends BaseAdapter) and other is ChatListAdapter (Which extends FirebaseCahtListAdapter). I don't know where I need to modify the code. Please help me to fix this.
FirebaseListAdapter
public abstract class FirebaseListAdapter<T> extends BaseAdapter {
private Query mRef;
private Class<T> mModelClass;
private int mLayout;
private LayoutInflater mInflater, recieveInflater;
private List<T> mModels;
private Map<String, T> mModelKeys;
private ChildEventListener mListener;
/**
* @param mRef The Firebase location to watch for data changes. Can also be a slice of a location, using some
* combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code>,
* @param mModelClass Firebase will marshall the data at a location into an instance of a class that you provide
* @param mLayout This is the mLayout used to represent a single list item. You will be responsible for populating an
* instance of the corresponding view with the data from an instance of mModelClass.
* @param activity The activity containing the ListView
*/
public FirebaseListAdapter(Query mRef, Class<T> mModelClass, int mLayout, Activity activity) {
this.mRef = mRef;
this.mModelClass = mModelClass;
this.mLayout = mLayout;
mInflater = activity.getLayoutInflater();
mModels = new ArrayList<T>();
mModelKeys = new HashMap<String, T>();
// Look for all child events. We will then map them to our own internal ArrayList, which backs ListView
mListener = this.mRef.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
T model = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
mModelKeys.put(dataSnapshot.getKey(), model);
// Insert into the correct location, based on previousChildName
if (previousChildName == null) {
mModels.add(0, model);
} else {
T previousModel = mModelKeys.get(previousChildName);
int previousIndex = mModels.indexOf(previousModel);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(model);
} else {
mModels.add(nextIndex, model);
}
}
notifyDataSetChanged();
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// One of the mModels changed. Replace it in our list and name mapping
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
T newModel = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
int index = mModels.indexOf(oldModel);
mModels.set(index, newModel);
mModelKeys.put(modelName, newModel);
notifyDataSetChanged();
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// A model was removed from the list. Remove it from our list and the name mapping
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
mModels.remove(oldModel);
mModelKeys.remove(modelName);
notifyDataSetChanged();
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
// A model changed position in the list. Update our list accordingly
String modelName = dataSnapshot.getKey();
T oldModel = mModelKeys.get(modelName);
T newModel = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
int index = mModels.indexOf(oldModel);
mModels.remove(index);
if (previousChildName == null) {
mModels.add(0, newModel);
} else {
T previousModel = mModelKeys.get(previousChildName);
int previousIndex = mModels.indexOf(previousModel);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(newModel);
} else {
mModels.add(nextIndex, newModel);
}
}
notifyDataSetChanged();
}
@Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("FirebaseListAdapter", "Listen was cancelled, no more updates will occur");
}
});
}
public void cleanup() {
// We're being destroyed, let go of our mListener and forget about all of the mModels
mRef.removeEventListener(mListener);
mModels.clear();
mModelKeys.clear();
}
@Override
public int getCount() {
return mModels.size();
}
@Override
public Object getItem(int i) {
return mModels.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = mInflater.inflate(mLayout, viewGroup, false);
}
T model = mModels.get(i);
// Call out to subclass to marshall this model into the provided view
populateView(view, model);
return view;
}
/**
* Each time the data at the given Firebase location changes, this method will be called for each item that needs
* to be displayed. The arguments correspond to the mLayout and mModelClass given to the constructor of this class.
* <p/>
* Your implementation should populate the view using the data contained in the model.
*
* @param v The view to populate
* @param model The object containing the data used to populate the view
*/
protected abstract void populateView(View v, T model);
}
ChatListAdapter
public class ChatListAdapter extends FirebaseListAdapter<Chat> {
// The mUsername for this client. We use this to indicate which messages originated from this user
private String mUsername;
Activity activity;
LayoutInflater inflater;
public ChatListAdapter(Query ref, Activity activity, int layout, String mUsername) {
super(ref, Chat.class, layout, activity);
this.mUsername = mUsername;
this.activity = activity;
this.inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* Bind an instance of the <code>Chat</code> class to our view. This method is called by <code>FirebaseListAdapter</code>
* when there is a data change, and we are given an instance of a View that corresponds to the layout that we passed
* to the constructor, as well as a single <code>Chat</code> instance that represents the current data to bind.
*
* @param view A view instance corresponding to the layout we passed to the constructor.
* @param chat An instance representing the current state of a chat message
*/
@Override
protected void populateView(View view, Chat chat) {
String author = chat.getAuthor();
TextView authorText = (TextView) view.findViewById(R.id.author);
TextView messageText = (TextView) view.findViewById(R.id.message);
authorText.setText(author + ": ");
// If the message was sent by this user, design it differently
if (author != null && author.equals(mUsername)) {
authorText.setTextColor(view.getResources().getColor(R.color.lblFromName));
authorText.setGravity(Gravity.RIGHT | Gravity.END);
messageText.setText(chat.getMessage());
messageText.setGravity(Gravity.RIGHT);
messageText.setBackground(view.getResources().getDrawable(R.drawable.bg_msg_you));
} else {
authorText.setTextColor(view.getResources().getColor(R.color.lblFromName));
authorText.setGravity(Gravity.LEFT | Gravity.START);
messageText.setText(chat.getMessage());
messageText.setGravity(Gravity.LEFT);
messageText.setBackground(view.getResources().getDrawable(R.drawable.bg_msg_from));
}
}
}
Layout File Used In CahtListAdapter
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:text="Name"
android:padding="5dp"
android:textStyle="italic"
android:textSize="12dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Content"
android:paddingBottom="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:textSize="16dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"/>
</LinearLayout>
Finally I found a solution for this, I just added a LayoutGravity in coding, and from there I set Gravity.
Inside ChatListAdapter
@Override
protected void populateView(View view, Chat chat) {
String author = chat.getAuthor();
TextView authorText = (TextView) view.findViewById(R.id.author);
TextView messageText = (TextView) view.findViewById(R.id.message);
authorText.setText(author + ": ");
// If the message was sent by this user, design it differently
if (author != null && author.equals(mUsername)) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_PARENT);
params.weight = 1.0f;
params.gravity = Gravity.RIGHT;
authorText.setTextColor(view.getResources().getColor(R.color.lblFromName));
authorText.setLayoutParams(params);
messageText.setText(chat.getMessage());
messageText.setBackground(view.getResources().getDrawable(R.drawable.bg_msg_you));
messageText.setLayoutParams(params);
} else {
authorText.setTextColor(view.getResources().getColor(R.color.lblFromName));
authorText.setGravity(Gravity.LEFT | Gravity.START);
messageText.setText(chat.getMessage());
messageText.setGravity(Gravity.LEFT);
messageText.setBackground(view.getResources().getDrawable(R.drawable.bg_msg_from));
}
}