Android - How to arrange adapters in position

I created a simple chat app using firebase

but had a little difficulty setting bubble elements for positions left

and right

. Left bubble for another person, right bubble for me.

This is my Adapter :

public class MessageAdapterCustom extends RecyclerView.Adapter<MessageAdapterCustom.MessageViewHolder> {

        private List<Messages> mMessageList;
        private Context context;
        private String mBubblePosition;

        public MessageAdapterCustom(Context context, List<Messages> mMessageList, String mBubblePosition) {
            this.context = context;
            this.mMessageList = mMessageList;
            this.mBubblePosition = mBubblePosition;
        }

        @Override
        public MessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

            View v = LayoutInflater.from( parent.getContext() )
                    .inflate( R.layout.message_single_layout_custom, parent, false );

            return new MessageViewHolder( v );
        }

        public class MessageViewHolder extends RecyclerView.ViewHolder {

            public TextView messageText;
            public CircleImageView profileImage;

            public MessageViewHolder(View view) {
                super( view );

                if(mBubblePosition.equals( "kiri" )) {

                    RelativeLayout right = (RelativeLayout) view.findViewById( R.id.sendingMessageLayout );
                    right.setVisibility( View.GONE );

                    messageText = (TextView) view.findViewById( R.id.message_text_layout_kiri );
                    profileImage = (CircleImageView) view.findViewById( R.id.message_profile_layout_kiri );

                } else {

                    LinearLayout left = (LinearLayout) view.findViewById(R.id.recievemessageLayout);
                    left.setVisibility(View.GONE);

                    messageText = (TextView) view.findViewById( R.id.message_text_layout_kanan );
                    profileImage = (CircleImageView) view.findViewById( R.id.message_profile_layout_kanan );

                }

            }
        }

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

            final Messages msg = mMessageList.get( position );
            holder.messageText.setText( msg.getMessage() );

            Picasso.with( context )
                    .load( msg.getProfile_pic() )
                    .networkPolicy( NetworkPolicy.OFFLINE )
                    .into( holder.profileImage );

            /*Picasso.with( context )
                    .load( msg.getProfile_pic() )
                    .networkPolicy( NetworkPolicy.OFFLINE )
                    .placeholder( R.drawable.no_profile )
                    .into( holder.profileImage, new Callback() {
                        @Override
                        public void onSuccess() {

                        }

                        @Override
                        public void onError() {
                            Picasso.with( context )
                                    .load( msg.getProfile_pic() )
                                    .networkPolicy( NetworkPolicy.OFFLINE )
                                    .into( holder.profileImage );
                        }
                    } );*/


        }

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

      

My ChatActivity:

public class ChatsActivity extends BaseActivity {

        private Toolbar mToolbar;

        private String mChatUser, mChatUserProfileImage;

        private DatabaseReference mRootRef, mMessageDatabase;
        private FirebaseAuth mAuth;

        private TextView mTitleView, mLastSeenView;
        private CircleImageView mProfileImage, mChatProfilePic;

        private String online, image, mCurrentUserID;

        private ImageButton mChatAddBtn, mChatSendBtn;
        private EditText mChatMessageText;

        private RecyclerView mMessagesList;

        private final List<Messages> messagesList = new ArrayList<>();

        private LinearLayoutManager mLinearLayout;

        private MessageAdapter mAdapter;

        private MessageAdapterCustom mAdapterCustom;

        String URL_PROFILE;

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

            mRootRef = FirebaseDatabase.getInstance().getReference();
        /*mRootRef.keepSynced( true );*/

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

            mChatUser = getIntent().getStringExtra( "user_id" );
            mChatUserProfileImage = getIntent().getStringExtra( "thumb_image" );

        /*getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE );*/

            mToolbar = (Toolbar) findViewById( R.id.toolbar );
            setSupportActionBar( mToolbar );

            mChatAddBtn = (ImageButton) findViewById( R.id.chat_add_btn );
            mChatSendBtn = (ImageButton) findViewById( R.id.chat_send_btn );
            mChatMessageText = (EditText) findViewById( R.id.chat_message );

            mChatMessageText.requestFocus();

            mMessagesList = (RecyclerView) findViewById( R.id.chat_messages_list );

            mLinearLayout = new LinearLayoutManager( this );

            mMessagesList.setHasFixedSize( true );
            mMessagesList.setLayoutManager( mLinearLayout );

            ActionBar actionBar = getSupportActionBar();
            actionBar.setDisplayHomeAsUpEnabled( true );
            actionBar.setDisplayShowCustomEnabled( true );

            final String userName = getIntent().getStringExtra( "user_name" );
            final String thumbProfilePic = getIntent().getStringExtra( "thumb_image" );

            chatUserPic( mChatUser );

            LayoutInflater layoutInflater = (LayoutInflater) this.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
            View action_bar_view = layoutInflater.inflate( R.layout.chat_custom_bar, null );

            actionBar.setCustomView( action_bar_view );

            mTitleView = (TextView) findViewById( R.id.custom_bar_title );
            mLastSeenView = (TextView) findViewById( R.id.custom_bar_seen );
            mProfileImage = (CircleImageView) findViewById( R.id.custom_bar_image );
            mChatProfilePic = (CircleImageView) findViewById( R.id.message_profile_layout );

            mTitleView.setText( userName );

            mRootRef.child( "Users" ).child( mCurrentUserID ).child( "online" ).setValue( "Online" );

            loadMessages();

            mRootRef.child( "Users" ).child( mChatUser ).addValueEventListener( new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    mTitleView.setText( userName );

                    online = dataSnapshot.child( "online" ).getValue().toString();
                    image = dataSnapshot.child( "image" ).getValue().toString();

                    if (online.equals( "Online" )) {
                        mLastSeenView.setText( online );
                    } else {

                        GetTimeAgo getTimeAgo = new GetTimeAgo();
                        long lastTime = Long.parseLong( online );
                        String lastSeenTime = getTimeAgo.timeAgo( lastTime, getApplicationContext() );
                        mLastSeenView.setText( lastSeenTime );
                    }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            } );

            mChatMessageText.setOnClickListener( new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(mAdapterCustom != null) {
                        mMessagesList.scrollToPosition( mAdapterCustom.getItemCount() - 1 );
                    }
                }
            } );

            mChatMessageText.setOnFocusChangeListener( new View.OnFocusChangeListener() {
                @Override
                public void onFocusChange(View view, boolean hasFocus) {
                    if (hasFocus) {
                        if(mAdapterCustom != null) {
                            mMessagesList.scrollToPosition( mAdapterCustom.getItemCount() - 1 );
                        }
                    } else {
                        if(mAdapterCustom != null) {
                            mMessagesList.scrollToPosition( mAdapterCustom.getItemCount() - 1 );
                        }
                    }
                }
            } );


            mRootRef.child( "Chat" ).child( mCurrentUserID ).addValueEventListener( new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    if (!dataSnapshot.hasChild( mChatUser )) {

                        Map chatAddMap = new HashMap();
                        chatAddMap.put( "seen", false );
                        chatAddMap.put( "timestamp", ServerValue.TIMESTAMP );

                        Map chatUserMap = new HashMap();
                        chatUserMap.put( "Chat" + "/" + mCurrentUserID + "/" + mChatUser, chatAddMap );
                        chatUserMap.put( "Chat" + "/" + mChatUser + "/" + mCurrentUserID, chatAddMap );

                        mRootRef.updateChildren( chatUserMap, new DatabaseReference.CompletionListener() {
                            @Override
                            public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                                if (databaseError != null) {
                                    Log.e( "CHAT_LOG", databaseError.getMessage().toString() );
                                } else {
                                    mMessagesList.scrollToPosition( mAdapterCustom.getItemCount() - 1 );
                                }
                            }
                        } );

                    }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            } );

            mChatSendBtn.setOnClickListener( new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    final String message = mChatMessageText.getText().toString();
                    if (TextUtils.isEmpty( message )) {
                        onError( "Message cannot be empty!" );
                    } else {
                        DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
                        Query query = reference.child( "Users" ).child( mCurrentUserID );
                        query.addListenerForSingleValueEvent( new ValueEventListener() {
                            @Override
                            public void onDataChange(DataSnapshot dataSnapshot) {

                                if (dataSnapshot.exists()) {
                                    final String URL_PROFILE = dataSnapshot.child( "thumb_image" ).getValue().toString();
                                    sendMessage( message, URL_PROFILE );
                                }
                            }

                            @Override
                            public void onCancelled(DatabaseError databaseError) {

                            }
                        } );

                    }
                }
            } );

        }

        private void chatUserPic(String id) {
            DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
            Query query = reference.child( "Users" ).child( id );
            query.addListenerForSingleValueEvent( new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {

                    if (dataSnapshot.exists()) {
                        final String URL_PROFILE = dataSnapshot.child( "thumb_image" ).getValue().toString();
                        Picasso.with( getApplication() )
                                .load( URL_PROFILE )
                                .networkPolicy( NetworkPolicy.OFFLINE )
                                .placeholder( R.drawable.no_profile )
                                .into( mProfileImage, new Callback() {
                                    @Override
                                    public void onSuccess() {

                                    }

                                    @Override
                                    public void onError() {
                                        Picasso.with( getApplication() )
                                                .load( URL_PROFILE )
                                                .placeholder( R.drawable.no_profile )
                                                .into( mProfileImage );
                                    }
                                } );
                    } else {
                        String URL_PROFILE = "no_profile";
                        Log.i( "PAUL", URL_PROFILE );
                    }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            } );
        }


        private void loadMessages() {

            mRootRef.child( "Messages" ).child( mCurrentUserID ).child( mChatUser ).addChildEventListener( new ChildEventListener() {
                @Override
                public void onChildAdded(DataSnapshot dataSnapshot, String s) {

                    Messages message = dataSnapshot.getValue( Messages.class );

                    String currentUserID = message.current_user_id.toString();

                /*mAdapter = new MessageAdapter( getApplicationContext(), messagesList );*/

                    if (currentUserID.equals( mCurrentUserID )) {
                        mAdapterCustom = new MessageAdapterCustom( getApplicationContext(), messagesList, "kanan" );
                        mMessagesList.setAdapter( mAdapterCustom );
                        messagesList.add( message );
                        mAdapterCustom.notifyDataSetChanged();
                        Log.i( "CHAT_ACTIVITY", "RIGHT" );
                    } else {
                        mAdapterCustom = new MessageAdapterCustom( getApplicationContext(), messagesList, "kiri" );
                        mMessagesList.setAdapter( mAdapterCustom );
                        messagesList.add( message );
                        mAdapterCustom.notifyDataSetChanged();
                        Log.i( "CHAT_ACTIVITY", "LEFT" );
                    }

                /*mMessagesList.scrollToPosition( mAdapterCustom.getItemCount() - 1 );*/
                }

                @Override
                public void onChildChanged(DataSnapshot dataSnapshot, String s) {

                }

                @Override
                public void onChildRemoved(DataSnapshot dataSnapshot) {

                }

                @Override
                public void onChildMoved(DataSnapshot dataSnapshot, String s) {

                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            } );
        }

        private void sendMessage(String message, String profile_pic) {

            String current_user_ref = "Messages" + "/" + mCurrentUserID + "/" + mChatUser;
            String chat_user_ref = "Messages" + "/" + mChatUser + "/" + mCurrentUserID;

            DatabaseReference user_message_push = mRootRef.child( "Messages" )
                    .child( mCurrentUserID ).child( mChatUser ).push();


            String push_id = user_message_push.getKey();

            Map messageMap = new HashMap();
            messageMap.put( "message", message );
            messageMap.put( "seen", false );
            messageMap.put( "type", "text" );
            messageMap.put( "time", ServerValue.TIMESTAMP );
            messageMap.put( "profile_pic", profile_pic );
            messageMap.put( "current_user_id", mCurrentUserID );


            Map messageUserMap = new HashMap();
            messageUserMap.put( current_user_ref + "/" + push_id, messageMap );
            messageUserMap.put( chat_user_ref + "/" + push_id, messageMap );

            mRootRef.updateChildren( messageUserMap, new DatabaseReference.CompletionListener() {
                @Override
                public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                    if (databaseError != null) {
                        mChatMessageText.setText( "" );
                        Log.e( "SEND_MESSAGE_CHAT", databaseError.getMessage().toString() );
                    } else {
                        mChatMessageText.setText( "" );
                        mMessagesList.scrollToPosition( mAdapterCustom.getItemCount() - 1 );
                    }
                }
            } );

        }

        @Override
        protected void onStart() {
            super.onStart();
            mRootRef.child( "Users" ).child( mCurrentUserID ).child( "online" ).setValue( "Online" );
            mMessagesList.getLayoutManager().scrollToPosition( mLinearLayout.findLastVisibleItemPosition() + 1000 );
        }

}  

      

You see the line:

if (currentUserID.equals( mCurrentUserID )) {
   mAdapterCustom = new MessageAdapterCustom( getApplicationContext(), messagesList, "kanan" );
   mMessagesList.setAdapter( mAdapterCustom );
   messagesList.add( message );
   mAdapterCustom.notifyDataSetChanged();
   Log.i( "CHAT_ACTIVITY", "RIGHT" );
} else {
   mAdapterCustom = new MessageAdapterCustom( getApplicationContext(), messagesList, "kiri" );
   mMessagesList.setAdapter( mAdapterCustom );
   messagesList.add( message );
   mAdapterCustom.notifyDataSetChanged();
   Log.i( "CHAT_ACTIVITY", "LEFT" );
}

      

When I try to use two devices, first if I try to send a message myself, the log shows right

what the position of the ball on the right means . enter image description here

But when I respond with different devices (by a different person), the adapter will move two bubbles to the left . It looks like it follows the last command from the adapter. enter image description here

My magazine enter image description here

How do I keep my adapter in position every time I message arrives

change the whole structure of my view elements as shown.

- Update - My Layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/stackoverflow"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp">

    <LinearLayout
        android:id="@+id/recievemessageLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/message_profile_layout_kiri"
            android:layout_width="55dp"
            android:layout_height="55dp"
            android:src="@drawable/no_profile" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/message_text_layout_kiri"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:layout_marginTop="5dp"
                android:background="@drawable/message_text_background"
                android:paddingBottom="5dp"
                android:paddingLeft="10dp"
                android:paddingRight="10dp"
                android:paddingTop="5dp"
                android:text="@string/message_here"
                android:textColor="@android:color/white" />

            <ImageView
                android:id="@+id/message_buble_kiri"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_marginStart="10dp"
                android:layout_marginTop="-10dp"
                android:src="@drawable/buble_left" />

        </LinearLayout>

    </LinearLayout>

    <RelativeLayout
        android:id="@+id/sendingMessageLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/recievemessageLayout"
        android:layout_marginTop="5dp"
        android:orientation="horizontal">

        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/message_profile_layout_kanan"
            android:layout_width="55dp"
            android:layout_height="55dp"
            android:layout_alignParentEnd="true"
            android:src="@drawable/no_profile" />

        <TextView
            android:id="@+id/message_text_layout_kanan"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginEnd="10dp"
            android:layout_marginTop="5dp"
            android:layout_toStartOf="@+id/message_profile_layout_kanan"
            android:background="@drawable/message_text_background"
            android:paddingBottom="5dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:paddingTop="5dp"
            android:text="@string/message_here"
            android:textColor="@android:color/white" />

        <ImageView
            android:id="@+id/message_buble_kanan"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_alignEnd="@+id/message_text_layout_kanan"
            android:layout_below="@+id/message_text_layout_kanan"
            android:layout_marginTop="-10dp"
            android:src="@drawable/buble_right" />


    </RelativeLayout>

</RelativeLayout>

      

+3


source to share


3 answers


You are currently creating two different adapters with all messages pointing in the same direction. Instead, you must create one adapter to decide how to align each message one at a time.



+3


source


RecyclerView with multiple view types?



public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        class ViewHolder0 extends RecyclerView.ViewHolder {
            ...
            public ViewHolder0(View itemView){
            ...
            }
        }

        class ViewHolder2 extends RecyclerView.ViewHolder {
            ...
            public ViewHolder2(View itemView){
            ...
        }

        @Override
        public int getItemViewType(int position) {
            // Just as an example, return 0 or 2 depending on position
            // Note that unlike in ListView adapters, types don't have to be contiguous
            return position % 2 * 2;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
             switch (viewType) {
                 case 0: return new ViewHolder0(...);
                 case 2: return new ViewHolder2(...);
                 ...
             }
        }

        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
            switch (holder.getItemViewType()) {
                case 0:
                    ViewHolder0 viewHolder0 = (ViewHolder0)holder;
                    ...
                    break;

                case 2:
                    ViewHolder2 viewHolder2 = (ViewHolder2)holder;
                    ...
                    break;
            }
        }
    }

      

0


source


The items in your message list will basically consist of two types: sent and received. Could you make your data model so that they define their types, and in onCreateViewHolder()

you you have a different viewer for each type. In onBindViewHolder()

you need to bind your views again by element type.

I modified Naveen's example a bit:

interface MsgType {
        int TYPE_SENT = 1;
        int TYPE_RECEIVED = 2;
    }

    class Msg implements MsgType
    {
        String text ;
        int type;

        public Msg(String text, int type) {
            // set 
        }

        public String getText() {
            return text;
        }

        public int getType() {
            return type;
        }
    }

    public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        List<Msg> msgs;

        class SentMsgVH extends RecyclerView.ViewHolder {
            ...
            public SentMsgVH(View itemView){
            ...
            }
        }

        class ReceivedMsgVh extends RecyclerView.ViewHolder {
            ...
            public ReceivedMsgVh(View itemView){
            ...
        }

        @Override
        public int getItemViewType(int position) {
            return msgs.get(position).getType();
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v;
             switch (viewType) {
                 case TYPE_SENT: 
                    v = ... ; // inflate view
                    return new SentMsgVH(v);
                 case TYPE_RECEIVED:
                    v = ...; // inflate view
                    return new ReceivedMsgVh(v);
             }
        }

        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
            switch (getItemViewType(position)) {
                case TYPE_SENT:
                    ViewHolder0 viewHolder0 = (SentMsgVH)holder;
                    ...
                    break;

                case TYPE_RECEIVED:
                    ViewHolder2 viewHolder2 = (ReceivedMsgVh)holder;
                    ...
                    break;
            }
        }
    }

      

In your respective viewers, you have to inflate the correct layout to suit your needs.

0


source







All Articles