I’ve an activity in my Android App named as ChatActivity. Here is the xml layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:windowSoftInputMode="adjustResize"
tools:context=".ui.home.ui.chat.ChatActivity">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="?attr/homeAsUpIndicator"
app:navigationIconTint="@color/black"
app:title="@string/chat"
app:titleCentered="true" >
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/ivProfile"
android:layout_width="@dimen/_30sdp"
android:layout_height="@dimen/_30sdp"
android:src="@drawable/ic_dummy_user"
android:layout_marginHorizontal="@dimen/_10sdp"
android:layout_gravity="end"
/>
</com.google.android.material.appbar.MaterialToolbar>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvMessages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/toolbar"
android:paddingBottom="@dimen/_95sdp"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintVertical_bias="1.0"
tools:listitem="@layout/item_message_sent" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tiMessage"
style="@style/textInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxHeight="@dimen/_200sdp"
android:layout_marginLeft="@dimen/_5sdp"
android:layout_marginRight="@dimen/_35sdp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
app:errorEnabled="false"
android:layout_marginVertical="@dimen/_5sdp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
>
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="@dimen/_200sdp"
android:autofillHints="name"
android:hint="@string/message"
android:inputType="textMultiLine" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/ivSend"
android:layout_width="@dimen/_25sdp"
android:layout_height="@dimen/_25sdp"
android:src="@drawable/ic_send_message"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/_12sdp"
app:layout_constraintLeft_toRightOf="@id/tiMessage"
app:tint="@color/blue" />
</androidx.constraintlayout.widget.ConstraintLayout>
When I open the textInput field, the layout does not adapt to it, it shows only keyboard to open but when i type in keyboard it does gets typed in text input layout, but while typiing the user cannot see the text input layout with out closing the keyboard
+
The recyclerview start scroll from top to bottom but i want t to be like a chat, bottom to top. And on new message added it does scroll to bottom but on the last iitem of the exsiting list not on the new item.
I’ve tried using android:windowSoftInputMode="adjustResize"
for keyboard and
private fun scrollToBottom() {
binding.rvMessages.scrollToPosition(adapter.itemCount -1)
}
for recylerview.
Any suggestions for optimizing the code / for betterment is welcome. My activity code is:
@AndroidEntryPoint
class ChatActivity : AppCompatActivity() {
private lateinit var binding: ActivityChatBinding
private lateinit var database: DatabaseReference
private lateinit var adapter: ChatAdapter
private var currentUserId: Int = 0
private var otherUserId: Int = 0
private lateinit var conversationId: String
private var convoId = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityChatBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
binding.toolbar.setNavigationOnClickListener { onBackPressedDispatcher.onBackPressed() }
val userDetails = intent.getParcelableExtra<ConnectionDetailDTO>("USER_DETAILS")
val currentUser = Paper.book().read<User>(Constant.k_user)
currentUserId = currentUser?.id!!
otherUserId = userDetails?.id!!
binding.toolbar.title = userDetails.name
Glide.with(this).load(userDetails.profileUrl).placeholder(R.drawable.ic_dummy_user).into(binding.ivProfile)
binding.ivProfile.setOnClickListener{showProfile()}
adapter = ChatAdapter(currentUserId)
binding.rvMessages.layoutManager = LinearLayoutManager(this)
binding.rvMessages.adapter = adapter
scrollToBottom()
val database = FirebaseDatabase.getInstance()
val reference = database.getReference("Conversations")
reference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val messages = mutableListOf<Message>()
for (conversationSnapshot in dataSnapshot.children) {
val conversationIdParts = conversationSnapshot.key?.split("_")
if (conversationIdParts?.size == 3) {
val firstUserId = conversationIdParts[1]
val secondUserId = conversationIdParts[2]
convoId = "conversation_${firstUserId}_${secondUserId}"
if (firstUserId == currentUserId.toString() && secondUserId == otherUserId.toString()
|| firstUserId == otherUserId.toString() && secondUserId == currentUserId.toString()) {
val conversationMessages = mutableListOf<Message>()
for (messageSnapshot in conversationSnapshot.child("messages").children) {
val content = messageSnapshot.child("content").value.toString()
val date = messageSnapshot.child("date").value.toString()
val messageId = messageSnapshot?.child("id")?.value.toString()
val isRead = messageSnapshot.child("isRead").value as? Boolean ?: false
val name = messageSnapshot.child("name").value.toString()
val senderEmail = messageSnapshot.child("senderEmail").value.toString()
val type = messageSnapshot.child("type").value.toString()
val message = Message(content, date, messageId, isRead, name, senderEmail, type)
conversationMessages.add(message)
Log.d(TAG, "onDataChange: $message")
}
messages.addAll(conversationMessages)
}
}
}
Log.d(TAG, "onDataChange: $messages")
adapter.updateMessages(messages)
}
override fun onCancelled(databaseError: DatabaseError) {
println("Error: ${databaseError.message}")
}
})
binding.ivSend.setOnClickListener {
val messageText = binding.tiMessage.editText?.text.toString().trim()
if (messageText.isNotEmpty()) {
val conversationId1 = "conversation_${currentUserId}_${otherUserId}"
val conversationId2 = "conversation_${otherUserId}_${currentUserId}"
reference.child(conversationId1).get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val data = task.result?.value
val conversationId = if (data != null) {
conversationId1
} else {
conversationId2
}
val messageReference =
reference.child(conversationId).child("messages").push()
val message = Message(
content = messageText,
date = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss",
Locale.getDefault()
).format(Date()),
id = messageReference.key!!,
isRead = false,
name = currentUser?.name ?: "",
senderEmail = currentUserId.toString(),
type = "text"
)
messageReference.setValue(message)
binding.tiMessage.editText?.setText("")
scrollToBottom()
}
}
addUser(currentUserId.toString())
}
}
}
private fun scrollToBottom() {
binding.rvMessages.scrollToPosition(adapter.itemCount -1)
}
}
I tried solutions for recycler view from StackOverFLow & Chat GPT
Sahar Batool is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.