I'm making a chat app with Firebase, basically the app is running well. I'm working on the UI, it have a problem that the message recipient or from sender is one side of the RecyclerView, I want it both side (right for sender and left for recipient) I have 2 XML file for message recipient and sender already so how can I use the item_message_input.xml for sender and item_message.xml for recipient (sorry I don't know how to describe my problem clearly).
Here is my code:
MainActivity.java
public class MainActivity extends AppCompatActivity
implements GoogleApiClient.OnConnectionFailedListener {
public static class MessageViewHolder extends RecyclerView.ViewHolder {
public TextView messageTextView;
public TextView messengerTextView;
public CircleImageView messengerImageView;
public MessageViewHolder(View v) {
super(v);
messageTextView = (TextView) itemView.findViewById(R.id.messageTextView);
messengerTextView = (TextView) itemView.findViewById(R.id.messengerTextView);
messengerImageView = (CircleImageView) itemView.findViewById(R.id.messengerImageView);
}
}
private static final String TAG = "MainActivity";
public static final String MESSAGES_CHILD = "messages";
private static final int REQUEST_INVITE = 1;
public static final int DEFAULT_MSG_LENGTH_LIMIT = 150;
public static final String ANONYMOUS = "anonymous";
private static final String MESSAGE_SENT_EVENT = "message_sent";
private String mUsername;
private String mPhotoUrl;
private SharedPreferences mSharedPreferences;
private GoogleApiClient mGoogleApiClient;
private Button mSendButton;
private RecyclerView mMessageRecyclerView;
private LinearLayoutManager mLinearLayoutManager;
private ProgressBar mProgressBar;
private EditText mMessageEditText;
// Firebase instance variables
private FirebaseAuth mFirebaseAuth;
private FirebaseUser mFirebaseUser;
private DatabaseReference mFirebaseDatabaseReference;
private FirebaseRecyclerAdapter<FriendlyMessage, MessageViewHolder> mFirebaseAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Set default username is anonymous.
mUsername = ANONYMOUS;
// Initialize FirebaseAuth
mFirebaseAuth = FirebaseAuth.getInstance();
mFirebaseUser = mFirebaseAuth.getCurrentUser();
if (mFirebaseUser == null) {
// Not signed in, launch the Sign In activity
startActivity(new Intent(this, SignInActivity.class));
finish();
return;
} else {
mUsername = mFirebaseUser.getDisplayName();
if (mFirebaseUser.getPhotoUrl() != null) {
mPhotoUrl = mFirebaseUser.getPhotoUrl().toString();
}
}
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API)
.build();
// Initialize ProgressBar and RecyclerView.
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mMessageRecyclerView = (RecyclerView) findViewById(R.id.messageRecyclerView);
mLinearLayoutManager = new LinearLayoutManager(this);
mLinearLayoutManager.setStackFromEnd(true);
mMessageRecyclerView.setLayoutManager(mLinearLayoutManager);
// New child entries
mFirebaseDatabaseReference = FirebaseDatabase.getInstance().getReference();
mFirebaseAdapter = new FirebaseRecyclerAdapter<FriendlyMessage,
MessageViewHolder>(
FriendlyMessage.class,
R.layout.item_message,
MessageViewHolder.class,
mFirebaseDatabaseReference.child(MESSAGES_CHILD)) {
@Override
protected void populateViewHolder(MessageViewHolder viewHolder,
FriendlyMessage friendlyMessage, int position) {
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
viewHolder.messengerTextView.setText(friendlyMessage.getName());
viewHolder.messageTextView.setText(friendlyMessage.getText());
if (friendlyMessage.getPhotoUrl() == null) {
viewHolder.messengerImageView
.setImageDrawable(ContextCompat
.getDrawable(MainActivity.this,
R.drawable.ic_account_circle_black_48dp));
} else {
Glide.with(MainActivity.this)
.load(friendlyMessage.getPhotoUrl())
.into(viewHolder.messengerImageView);
}
}
};
mFirebaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
int friendlyMessageCount = mFirebaseAdapter.getItemCount();
int lastVisiblePosition =
mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
// If the recycler view is initially being loaded or the
// user is at the bottom of the list, scroll to the bottom
// of the list to show the newly added message.
if (lastVisiblePosition == -1 ||
(positionStart >= (friendlyMessageCount - 1) &&
lastVisiblePosition == (positionStart - 1))) {
mMessageRecyclerView.scrollToPosition(positionStart);
}
}
});
mMessageRecyclerView.setLayoutManager(mLinearLayoutManager);
mMessageRecyclerView.setAdapter(mFirebaseAdapter);
mMessageEditText = (EditText) findViewById(R.id.messageEditText);
mMessageEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mSharedPreferences
.getInt(CodelabPreferences.FRIENDLY_MSG_LENGTH, DEFAULT_MSG_LENGTH_LIMIT))});
mMessageEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence.toString().trim().length() > 0) {
mSendButton.setEnabled(true);
} else {
mSendButton.setEnabled(false);
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
mSendButton = (Button) findViewById(R.id.sendButton);
mSendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
FriendlyMessage friendlyMessage = new
FriendlyMessage(mMessageEditText.getText().toString(), mUsername, mPhotoUrl);
mFirebaseDatabaseReference.child(MESSAGES_CHILD).push().setValue(friendlyMessage);
mMessageEditText.setText("");
}
});
}
@Override
public void onStart() {
super.onStart();
// Check if user is signed in.
// TODO: Add code to check if user is signed in.
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.sign_out_menu:
mFirebaseAuth.signOut();
Auth.GoogleSignInApi.signOut(mGoogleApiClient);
mUsername = ANONYMOUS;
setResult(RESULT_OK);
finish();
startActivity(new Intent(this, SignInActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
// An unresolvable error has occurred and Google APIs (including Sign-In) will not
// be available.
Log.d(TAG, "onConnectionFailed:" + connectionResult);
Toast.makeText(this, "Google Play Services error.", Toast.LENGTH_SHORT).show();
}
item_message.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/messengerImageView"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="top" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/msg_bubble_incoming">
<TextView
android:id="@+id/messageTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="8dp"
android:textColor="#ffffff"
android:textSize="16sp" />
<TextView
android:id="@+id/messengerTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/messageTextView"
android:gravity="center_vertical"
android:paddingBottom="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAllCaps="true"
android:textColor="#a2ffffff"
android:textSize="10sp"
android:textStyle="bold" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
item_message_input.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/msg_bubble_input"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/messengerImageView2"
android:layout_toStartOf="@+id/messengerImageView2">
<TextView
android:id="@+id/messageTextView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="8dp"
android:textColor="#ffffff"
android:textSize="16sp" />
<TextView
android:id="@+id/messengerTextView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/messageTextView2"
android:gravity="center_vertical"
android:paddingBottom="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAllCaps="true"
android:textColor="#a2ffffff"
android:textSize="10sp"
android:textStyle="bold" />
</RelativeLayout>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/messengerImageView2"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
</LinearLayout>
So, is it possible to make message appear on both side RecyclerView in this situation? Please help me :( Thank you so much!
In recycler view you will need two view holders. One is SenderViewHolder and the other Recipient View Holder.
In the recyclerView's dataset. every element(message) should say if it is a sent or received message and according to the type, you can inflate the and populate the appropriate viewHolder.
In the xml of sender, set android:gravity = "left"
and in xml of recipient, set android:gravity = "right"
In short, you will have one recycler view, which lists send and received messages. And sent and received have their own viewHolders. So you will need two xmls for the two viewholders.
In the xml of the view holders, set android:gravity = "left"
for one and android:gravity = "left"
for the other. The gravity is set in the first tag.<LinearLayout>
in your case.
Do the following in your recyclerView adapter:
ArrayList<Message> dataSet = new ArrayList<>(); // declare this as class level property.
First, you will have to ovverride the getItemViewType()
in recyclerview adapter.
@Override
public int getItemViewType(int position) {
if(dataset.get(position).getType == "sent") {
return 1;
} else {
return 2;
}
}
then override onCreateViewHolder()
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType = 1) {
View view = LayoutInflater.from(context).inflate(R.layout.sent_view_holder_layout, parent, false);
return new SentViewHolder(view);
} else {
LayoutInflater.from(context).inflate(R.layout.received_view_holder_layout, parent, false);
return new ReceivedViewHolder(view);
}
}
Then override onBindViewHolder()
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(dataSet.get(position).getType== "sent") {
populateSentViewHolder(holder, position);
} else {
populateReceivedViewHolder(holder, position);
}
}
private void populateSentViewHolder(RecyclerView.ViewHolder holder, int position) {
Message message = dataSet.get(position);
((SentViewHolder)holder).senderNameTextView.setText(message.getSender());
((SentViewHolder)holder).messageTextView.setText(message.getMessage());
}
private void populateReceivedViewHolder(RecyclerView.ViewHolder holder, int position) {
Message message = dataSet.get(position);
((ReceivedViewHolder)holder).senderNameTextView.setText(message.getSender());
((ReceivedViewHolder)holder).messageTextView.setText(message.getMessage());
}
In the populate methods, populate the widgets with data.
If you understand how recyclerViews work, this answer should be enough.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With