Android - How to make adapters layout stay in position

Viewed 428

I created a simple chat app using firebase but got a little trouble in laying bubble elements for the left and right positions. Left bubble for the other 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 myself try to send a message, Log shows RIGHT which means the position of the balloon on the right. enter image description here

But when I reply with different devices (the other person) then the adapter will move the two bubbles to the left. It seems because it follows the last adapter command. enter image description here

My Log enter image description here

How do I keep my adapter in position every time a message arrives, instead of changing all the 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 Answers
Related