You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中实现安卓WhatsApp风格的聊天气泡尾巴?

安卓实现WhatsApp风格聊天气泡尾巴方案

以下是两种手动创建聊天气泡尾巴的可行方案,无需依赖第三方库:

方法一:使用ShapeDrawable组合实现(简单快捷)

通过layer-list将矩形气泡和旋转的正方形组合成带尾巴的气泡,适合消息高度变化不大的场景。

发送方气泡(带右侧尾巴)

创建drawable/chat_bubble_sent.xml:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 主气泡背景 -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#DCF8C6" /> <!-- WhatsApp发送方绿色 -->
            <corners android:radius="16dp" />
            <padding android:top="8dp" android:bottom="8dp" android:left="12dp" android:right="12dp" />
        </shape>
    </item>
    <!-- 气泡尾巴:旋转45度的正方形 -->
    <item android:right="-10dp" android:top="20dp">
        <rotate android:fromDegrees="45">
            <shape android:shape="rectangle">
                <solid android:color="#DCF8C6" />
                <size android:width="10dp" android:height="10dp" />
            </shape>
        </rotate>
    </item>
</layer-list>

接收方气泡(带左侧尾巴)

创建drawable/chat_bubble_received.xml:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 主气泡背景 -->
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#FFFFFF" /> <!-- 接收方白色 -->
            <corners android:radius="16dp" />
            <padding android:top="8dp" android:bottom="8dp" android:left="12dp" android:right="12dp" />
        </shape>
    </item>
    <!-- 气泡尾巴:旋转45度的正方形 -->
    <item android:left="-10dp" android:top="20dp">
        <rotate android:fromDegrees="45">
            <shape android:shape="rectangle">
                <solid android:color="#FFFFFF" />
                <size android:width="10dp" android:height="10dp" />
            </shape>
        </rotate>
    </item>
</layer-list>

布局中使用

直接给TextView设置背景即可:

<!-- 发送方消息 -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/chat_bubble_sent"
    android:text="这是我发送的消息"
    android:textColor="#000000" />

<!-- 接收方消息 -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/chat_bubble_received"
    android:text="这是对方发送的消息"
    android:textColor="#000000" />

方法二:自定义View绘制(灵活适配)

通过Canvas和Path手动绘制气泡和尾巴,能根据消息高度动态调整尾巴位置,适配复杂场景。

创建自定义ChatBubbleView

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatTextView;

public class ChatBubbleView extends AppCompatTextView {
    private Path bubblePath;
    private RectF bubbleRect;
    private boolean isSentMessage; // 是否为发送方消息
    private final float tailSize = 10f; // 尾巴大小
    private final float cornerRadius = 16f; // 气泡圆角

    public ChatBubbleView(Context context) {
        super(context);
        init();
    }

    public ChatBubbleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        bubblePath = new Path();
        bubbleRect = new RectF();
        updatePadding();
    }

    // 设置是否为发送方消息,更新内边距并重绘
    public void setIsSentMessage(boolean isSent) {
        isSentMessage = isSent;
        updatePadding();
        invalidate();
    }

    private void updatePadding() {
        // 根据消息方向调整内边距,给尾巴留出空间
        int leftPad = isSentMessage ? 12 : 22;
        int rightPad = isSentMessage ? 22 : 12;
        setPadding(leftPad, 8, rightPad, 8);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        buildBubblePath();
    }

    // 构建气泡的Path路径
    private void buildBubblePath() {
        bubblePath.reset();
        float contentWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        float contentHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        bubbleRect.set(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + contentWidth, getPaddingTop() + contentHeight);

        if (isSentMessage) {
            // 绘制发送方气泡:右侧带尾巴
            bubblePath.addRoundRect(bubbleRect, cornerRadius, cornerRadius, Path.Direction.CW);
            float tailX = bubbleRect.right;
            float tailY = bubbleRect.top + contentHeight / 2;
            bubblePath.moveTo(tailX, tailY);
            bubblePath.lineTo(tailX + tailSize, tailY - tailSize);
            bubblePath.lineTo(tailX + tailSize, tailY + tailSize);
            bubblePath.close();
        } else {
            // 绘制接收方气泡:左侧带尾巴
            bubblePath.addRoundRect(bubbleRect, cornerRadius, cornerRadius, Path.Direction.CW);
            float tailX = bubbleRect.left;
            float tailY = bubbleRect.top + contentHeight / 2;
            bubblePath.moveTo(tailX, tailY);
            bubblePath.lineTo(tailX - tailSize, tailY - tailSize);
            bubblePath.lineTo(tailX - tailSize, tailY + tailSize);
            bubblePath.close();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 绘制气泡背景色
        int bgColor = isSentMessage ? 0xFFDCF8C6 : 0xFFFFFFFF;
        canvas.drawColor(bgColor);
        // 裁剪画布,只保留气泡区域
        canvas.clipPath(bubblePath);
        // 绘制文本内容
        super.onDraw(canvas);
    }
}

布局中使用

<!-- 发送方消息 -->
<com.yourpackage.ChatBubbleView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="这是一条发送的长消息,尾巴会自动居中显示"
    android:textColor="#000000" />

<!-- 接收方消息 -->
<com.yourpackage.ChatBubbleView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="这是一条接收的消息"
    android:textColor="#000000" />

使用说明

  • 在代码中调用setIsSentMessage(true/false)切换消息方向
  • 可根据需求修改tailSize、cornerRadius和背景色参数

内容的提问来源于stack exchange,提问作者Kartik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 13:45:10