Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do threaded message view with send and received messages on two sides with recycler view

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!

like image 293
MINH NGUYEN Avatar asked Jun 07 '16 11:06

MINH NGUYEN


1 Answers

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.

like image 105
rgv Avatar answered Nov 14 '22 23:11

rgv