0

I write a chat application using Firebase.

In ChatActivity, there are a recycler view to display chatting information. The following image is the first loading is correct.

enter image description here

When enter the message send to others, the layout of some messages become different.

enter image description here

THE CODE in MessageAdapter

public MessageAdapter(List<YeMessage> mMessageList){
    this.mMsgList = mMessageList;

}

@Override
public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.item_message, parent, false);

    mContext = parent.getContext();
    ref = FirebaseDatabase.getInstance().getReference();

    return new MessageViewHolder(view);
}

public class MessageViewHolder extends RecyclerView.ViewHolder{
    @BindView(R.id.msg_message)
    TextView messageText;
    @BindView(R.id.msg_imageView)
    ImageView image;
    public MessageViewHolder(View view){
        super(view);
        ButterKnife.bind(this, view);
    }
}

@Override
public void onBindViewHolder(MessageViewHolder holder, int position) {

    mAuth = FirebaseAuth.getInstance();
    current_user_id = mAuth.getCurrentUser().getUid();

    YeMessage c = mMsgList.get(position);
    String from_user = c.getFrom();



    ref.child("Users").child(from_user).addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            RelativeLayout.LayoutParams rp_msg = (RelativeLayout.LayoutParams)holder.messageText.getLayoutParams();

            RelativeLayout.LayoutParams rp_img = (RelativeLayout.LayoutParams)holder.image.getLayoutParams();


            image = dataSnapshot.child("imageUrl").getValue().toString();
            if(from_user.equals(current_user_id)){

                //lp.gravity = Gravity.END;
                //end
                rp_img.addRule(RelativeLayout.ALIGN_PARENT_END);

                //rp_img.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                holder.image.setLayoutParams(rp_img);
                //rp_msg.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.msg_imageView);
                rp_msg.addRule(RelativeLayout.START_OF, R.id.msg_imageView);
                holder.messageText.setLayoutParams(rp_msg);
                Glide.with(mContext)
                        .load(image)
                        .into(holder.image);

            }else {
                //lp.gravity = Gravity.START;
                rp_img.addRule(RelativeLayout.ALIGN_PARENT_START);
                rp_img.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                holder.image.setLayoutParams(rp_img);

                rp_msg.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                rp_msg.addRule(RelativeLayout.END_OF, R.id.msg_imageView);
                holder.messageText.setLayoutParams(rp_msg);

                Glide.with(mContext)
                        .load(image)
                        .into(holder.image);
            }

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });


    YeMessage msg = mMsgList.get(position);
    holder.messageText.setText(msg.getMessage());

}

@Override
public int getItemCount() {
    return mMsgList.size();
}

I think the reason is that I have too little knowledge about the lifecycle of recycler view.

And I find if I want to change gravity in onBindViewHolder(), it can't be successful every time. Because it will be layout correctly when it calls the onCreateViewHolder().

How can to ensure that onCreateViewHolder() is called each time, so that finish to create layout programmatically?

Add item_message.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout    
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/message_single_item"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation = "horizontal"
  android:padding="15dp">

<android.support.v7.widget.AppCompatImageView
    android:id="@+id/msg_imageView"
    android:layout_width="45sp"
    android:layout_height="45sp" />


<TextView
    android:id="@+id/msg_message"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/message_background"
    android:padding="10dp"
    android:textColor="#ffffff"
    />


</RelativeLayout>
zhen She
  • 129
  • 1
  • 9
  • show your xml also – Ankita Sep 15 '17 at 12:28
  • Instead of adding gravity on each item you can use two different layout one with left alignment and another one with right alignment. – Kunu Sep 15 '17 at 12:50
  • @Kunu has a great point. Look at this for more info: https://stackoverflow.com/questions/26245139/how-to-create-recyclerview-with-multiple-view-type – Kuffs Sep 15 '17 at 12:55
  • @Kuffs when use one layout , then modify the layout programmatically according to the situation. feasible? – zhen She Sep 15 '17 at 13:29
  • 1
    Possible but not as efficient or flexible as using separate view types. Learn how to use the `RecyclerView` properly. You won't regret it. – Kuffs Sep 15 '17 at 13:35
  • Okay, I will choose the efficient way. But do you know why the onCreateViewHolder() can't called every time? – zhen She Sep 15 '17 at 13:52

1 Answers1

2

I think you can have two different layouts for incoming and outgoing messages. It will be useful to you in future when you are changing the design.

You can customize your RecyclerView according to your need. You can refer to http://www.codexpedia.com/android/android-recyclerview-with-multiple-different-layouts/ for a detailed answer.

As far as your layout is concerned.

For incoming messages-

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="#000000"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="askjjnads dma dm"
        android:layout_gravity="center"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="20dp"
        />

</LinearLayout>

for out going messages-

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="end"
    android:padding="10dp">



    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="askjjnads dma dm"
        android:layout_gravity="center"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="10dp"
        />

    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="#000000"/>

</LinearLayout>
Aman Verma
  • 3,155
  • 7
  • 30
  • 60
  • Thank you for your suggestion! – zhen She Sep 15 '17 at 15:31
  • I encountered the similar case and recreate different XML for each type is a bad option. I am already having over 20 ViewTypes, it is a huge mess already. – Kirk_hehe Dec 08 '20 at 10:45
  • So How you would suggest? You can figure out the best option for you. For example, if your error Item and no item are similar you can make them one ITEM and show the ui for each one based on some value. My answer was feasible if there are 5 6 types of layout. But If your vies are so complex you might want to add the view dynamically. You can create a different viewHolder and inflate the same XML and show/hide based on some value. How your viewType is over 20? That's a very complex UI. – Aman Verma Dec 08 '20 at 15:19
  • @AmanVerma I am working on custom chat. Chat has images, video and other complex content. I grouped some layouts in one ViewHolder, it helped a little. I tried to use ConstraintSet to dynamically change my Views, but I stack with a problem mentioned above. So now I have 2 to 4 xml assigned to one ViewHolder. It does the job, but maintenance will be a nightmare. – Kirk_hehe Dec 25 '20 at 08:42
  • And How many ViewHolder do you have? Plus 2 to 4 xml are not much given the fact that the UI has so many components in it. You can do one thing. De-assemble any Chat App's Apk and you can see their layouts. I am pretty sure they must be having high number of layout. I had 2 ViewHolders, SEND_ITEM and RECEIVE_ITEM and there were only two layouts(You can have more than 2). So, Suppose I am sending a chat message with video in it, I will check in getItemViewType() that the data with postion contains video(like data.isvideo()), I will return something like SEND_WITH_VIDEO. Then I will use the – Aman Verma Dec 25 '20 at 15:12
  • same ViewHolder in oncreateViewHolder but I will inflate another xml. Then Onbindviewholder gets called and You can show the video content. I mean there are lot of ways. I dont think 2 3 layouts will be of high maintenance. Can you Post a question with all the code and then maybe I will try to optimize or something. And comment the link to the question – Aman Verma Dec 25 '20 at 15:16