如何在Android Studio中设计WhatsApp风格聊天气泡?我需开发聊天应用
Hey there! I’ve built a couple of chat apps with WhatsApp-style bubbles before, so I can walk you through exactly how to implement this in Android Studio. Let’s break it down into simple, actionable steps.
1. Define Bubble Shape Drawables
First, we need two custom shape drawables: one for messages you send, and one for messages you receive. These will replicate WhatsApp’s distinct rounded corners and color scheme.
Create res/drawable/chat_bubble_sent.xml (for your outgoing messages):
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!-- WhatsApp's signature green for outgoing messages --> <solid android:color="#25D366" /> <!-- Rounded corners: smaller on left, larger on right --> <corners android:topLeftRadius="16dp" android:topRightRadius="2dp" android:bottomLeftRadius="16dp" android:bottomRightRadius="16dp" /> <!-- Padding between text and bubble edge --> <padding android:top="8dp" android:bottom="8dp" android:left="12dp" android:right="12dp" /> </shape>
Create res/drawable/chat_bubble_received.xml (for incoming messages):
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!-- White background for incoming messages --> <solid android:color="#FFFFFF" /> <!-- Rounded corners: smaller on right, larger on left --> <corners android:topLeftRadius="16dp" android:topRightRadius="16dp" android:bottomLeftRadius="2dp" android:bottomRightRadius="16dp" /> <!-- Matching padding --> <padding android:top="8dp" android:bottom="8dp" android:left="12dp" android:right="12dp" /> <!-- Light gray border to match WhatsApp's style --> <stroke android:width="1dp" android:color="#E5E5EA" /> </shape>
2. Build the Chat Item Layout
Next, create a layout for individual chat messages (we’ll use this with RecyclerView). Save it as res/layout/item_chat_message.xml:
<?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" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="8dp"> <!-- Outgoing message (right-aligned) --> <TextView android:id="@+id/tv_sent_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxWidth="280dp" <!-- Prevent overly wide bubbles --> android:textColor="#FFFFFF" android:textSize="14sp" android:background="@drawable/chat_bubble_sent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" android:visibility="gone" /> <!-- Incoming message (left-aligned) --> <TextView android:id="@+id/tv_received_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxWidth="280dp" android:textColor="#000000" android:textSize="14sp" android:background="@drawable/chat_bubble_received" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" android:visibility="gone" /> <!-- Timestamp for outgoing messages --> <TextView android:id="@+id/tv_sent_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:textColor="#999999" android:layout_marginTop="4dp" app:layout_constraintTop_toBottomOf="@+id/tv_sent_message" app:layout_constraintEnd_toEndOf="@+id/tv_sent_message" android:visibility="gone" /> <!-- Timestamp for incoming messages --> <TextView android:id="@+id/tv_received_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:textColor="#999999" android:layout_marginTop="4dp" app:layout_constraintTop_toBottomOf="@+id/tv_received_message" app:layout_constraintStart_toStartOf="@+id/tv_received_message" android:visibility="gone" /> </androidx.constraintlayout.widget.ConstraintLayout>
3. Create the RecyclerView Adapter
We’ll use a RecyclerView to display the chat messages. First, define a data class to hold message details:
data class ChatMessage( val content: String, val isSentByMe: Boolean, val timestamp: String )
Then build the adapter to toggle between outgoing and incoming message views:
class ChatAdapter(private val messages: List<ChatMessage>) : RecyclerView.Adapter<ChatAdapter.ChatViewHolder>() { inner class ChatViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val sentMessage: TextView = itemView.findViewById(R.id.tv_sent_message) val receivedMessage: TextView = itemView.findViewById(R.id.tv_received_message) val sentTime: TextView = itemView.findViewById(R.id.tv_sent_time) val receivedTime: TextView = itemView.findViewById(R.id.tv_received_time) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_chat_message, parent, false) return ChatViewHolder(view) } override fun onBindViewHolder(holder: ChatViewHolder, position: Int) { val message = messages[position] if (message.isSentByMe) { // Show outgoing message, hide incoming holder.sentMessage.visibility = View.VISIBLE holder.receivedMessage.visibility = View.GONE holder.sentTime.visibility = View.VISIBLE holder.receivedTime.visibility = View.GONE holder.sentMessage.text = message.content holder.sentTime.text = message.timestamp } else { // Show incoming message, hide outgoing holder.receivedMessage.visibility = View.VISIBLE holder.sentMessage.visibility = View.GONE holder.receivedTime.visibility = View.VISIBLE holder.sentTime.visibility = View.GONE holder.receivedMessage.text = message.content holder.receivedTime.text = message.timestamp } } override fun getItemCount() = messages.size }
4. Set Up the Chat Activity
Finally, wire everything together in your chat activity:
class ChatActivity : AppCompatActivity() { private lateinit var recyclerView: RecyclerView private lateinit var adapter: ChatAdapter private val messageList = mutableListOf<ChatMessage>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_chat) recyclerView = findViewById(R.id.rv_chat) // Layout manager with stackFromEnd to show latest messages at bottom val layoutManager = LinearLayoutManager(this).apply { stackFromEnd = true } recyclerView.layoutManager = layoutManager // Add test messages messageList.apply { add(ChatMessage("Hey! How's it going?", false, "10:30 AM")) add(ChatMessage("Great! Just building WhatsApp-style bubbles 😊", true, "10:31 AM")) add(ChatMessage("Nice! Can you show me how?", false, "10:32 AM")) add(ChatMessage("Already did—check the code above!", true, "10:33 AM")) } adapter = ChatAdapter(messageList) recyclerView.adapter = adapter } }
Quick Optimization Tips
- Add long-click listeners to bubbles for copy/share functionality
- Extend the item layout to support images or media messages
- Adjust the
maxWidthof the message TextViews to match your design preferences - Use
DiffUtilin the adapter for smoother updates when new messages arrive
内容的提问来源于stack exchange,提问作者Sanjay




