Files
zhini_im/开发文档/微信气泡弹窗/微信气泡弹框.txt
rw0067680 c01808ac21 first commit
Change-Id: Ib7c2ab10a2562044fcaf9879388a6cbc1db6ac61
2025-12-23 10:00:49 +08:00

513 lines
15 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 微信气泡弹框实现文档
## 📋 功能概述
本功能实现了类似微信的气泡弹框效果,当用户点击首页右上角的加号按钮时,会弹出一个深色半透明的气泡弹框,包含三个菜单项:发起群聊、添加朋友、扫一扫。
## 🎨 视觉效果特点
### 1. **气泡样式**
- **背景色**: 深色半透明 `#CC000000`
- **圆角**: 8dp圆角矩形
- **宽度**: 屏幕宽度的40%
- **右边缘距离**: 与屏幕右边缘保持10dp距离
### 2. **小三角箭头**
- **位置**: 右上角,指向加号按钮
- **尺寸**: 16x16dp正三角形
- **位置偏移**: 距离右边56dp
- **颜色**: 与气泡背景色一致
### 3. **菜单项样式**
- **文字颜色**: 白色 `#FFFFFF`
- **图标颜色**: 白色
- **内边距**: 32dp左右24dp上下
- **分割线**: 白色半透明 `#33FFFFFF`
## 🔧 技术实现
### 1. **核心文件结构**
```
uikit/src/main/java/cn/wildfire/chat/kit/dialog/
├── WeChatBubbleDialog.java # 主弹框类
├── WeChatBubbleView.java # 自定义气泡View带箭头
└── MainHomePressDialog.java # 原始底部弹框(已替换)
uikit/src/main/res/drawable/
├── bg_wechat_bubble.xml # 气泡背景样式
└── bg_menu_item_selector.xml # 菜单项点击效果
app/src/main/java/com/xunpaisoft/social/im/main/
└── MainActivity.java # 主Activity包含点击事件
```
### 2. **WeChatBubbleDialog.java - 主弹框类**
```java
package cn.wildfire.chat.kit.dialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import cn.wildfire.chat.kit.R;
/**
* 微信风格的气泡弹框
*/
public class WeChatBubbleDialog {
private Context context;
private PopupWindow popupWindow;
private View anchorView;
private OnMenuItemClickListener listener;
public interface OnMenuItemClickListener {
void onGroupChatClick();
void onAddFriendClick();
void onScanClick();
}
public WeChatBubbleDialog(Context context, View anchorView) {
this.context = context;
this.anchorView = anchorView;
initPopupWindow();
}
private void initPopupWindow() {
// 创建气泡布局
View bubbleView = createBubbleView();
// 计算气泡宽度 - 设置为屏幕宽度的40%,符合微信效果
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
int bubbleWidth = (int) (screenWidth * 0.4);
// 创建 PopupWindow设置固定宽度
popupWindow = new PopupWindow(bubbleView,
bubbleWidth,
ViewGroup.LayoutParams.WRAP_CONTENT,
true);
// 设置背景
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
// 设置外部可点击
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
}
private View createBubbleView() {
// 创建主容器 - 使用自定义的带箭头气泡View
WeChatBubbleView container = new WeChatBubbleView(context);
container.setOrientation(LinearLayout.VERTICAL);
// 创建菜单项
String[] menuItems = {"发起群聊", "添加朋友", "扫一扫"};
int[] menuIcons = {R.drawable.ic_group_chat_white, R.drawable.ic_add_friend_white, R.drawable.ic_scan_white};
for (int i = 0; i < menuItems.length; i++) {
View menuItem = createMenuItem(menuItems[i], menuIcons[i], i);
container.addView(menuItem);
// 添加分割线(除了最后一个)- 使用白色半透明分割线
if (i < menuItems.length - 1) {
View divider = new View(context);
divider.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 1));
divider.setBackgroundColor(Color.parseColor("#33FFFFFF"));
container.addView(divider);
}
}
return container;
}
private View createMenuItem(String title, int iconRes, int position) {
// 创建菜单项布局
LinearLayout itemLayout = new LinearLayout(context);
itemLayout.setOrientation(LinearLayout.HORIZONTAL);
itemLayout.setPadding(32, 24, 32, 24);
itemLayout.setGravity(Gravity.CENTER_VERTICAL);
// 设置点击效果
itemLayout.setBackgroundResource(R.drawable.bg_menu_item_selector);
// 创建图标 - 使用白色图标
TextView iconView = new TextView(context);
iconView.setTextSize(20);
iconView.setTextColor(Color.WHITE);
iconView.setCompoundDrawablesWithIntrinsicBounds(iconRes, 0, 0, 0);
iconView.setCompoundDrawablePadding(16);
// 创建标题 - 使用白色
TextView titleView = new TextView(context);
titleView.setText(title);
titleView.setTextSize(16);
titleView.setTextColor(Color.WHITE);
titleView.setPadding(16, 0, 0, 0);
// 添加到布局
itemLayout.addView(iconView);
itemLayout.addView(titleView);
// 设置点击事件
itemLayout.setOnClickListener(v -> {
if (listener != null) {
switch (position) {
case 0:
listener.onGroupChatClick();
break;
case 1:
listener.onAddFriendClick();
break;
case 2:
listener.onScanClick();
break;
}
}
dismiss();
});
return itemLayout;
}
public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
this.listener = listener;
}
public void show() {
if (popupWindow != null && !popupWindow.isShowing()) {
// 计算显示位置
int[] location = new int[2];
anchorView.getLocationOnScreen(location);
// 获取屏幕宽度
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
// 计算气泡宽度 - 设置为屏幕宽度的40%,符合微信效果
int bubbleWidth = (int) (screenWidth * 0.4);
// 计算X坐标让气泡右边缘与屏幕右边缘保持10dp距离
int marginRight = (int) (10 * context.getResources().getDisplayMetrics().density);
int x = screenWidth - bubbleWidth - marginRight;
// 确保不超出屏幕左边界
if (x < 20) {
x = 20;
}
// 显示在加号按钮下方
popupWindow.showAtLocation(anchorView, Gravity.NO_GRAVITY,
x, location[1] + anchorView.getHeight() + 8);
}
}
public void dismiss() {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
}
}
public boolean isShowing() {
return popupWindow != null && popupWindow.isShowing();
}
}
```
### 3. **WeChatBubbleView.java - 自定义气泡View**
```java
package cn.wildfire.chat.kit.dialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/**
* 微信风格的气泡View带小三角箭头
*/
public class WeChatBubbleView extends LinearLayout {
private Paint bubblePaint;
private Path arrowPath;
private RectF bubbleRect;
public WeChatBubbleView(Context context) {
super(context);
init();
}
public WeChatBubbleView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public WeChatBubbleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 设置背景透明,我们自己绘制
setBackgroundColor(Color.TRANSPARENT);
// 初始化画笔
bubblePaint = new Paint();
bubblePaint.setColor(Color.parseColor("#CC000000"));
bubblePaint.setStyle(Paint.Style.FILL);
bubblePaint.setAntiAlias(true);
// 初始化路径
arrowPath = new Path();
bubbleRect = new RectF();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 设置气泡矩形区域(留出箭头空间)
bubbleRect.set(0, 16, w, h);
// 创建箭头路径
createArrowPath(w, h);
}
private void createArrowPath(int width, int height) {
arrowPath.reset();
// 箭头位置(右上角,指向加号按钮)
int arrowX = width - 56; // 距离右边56dp
int arrowY = 0;
int arrowSize = 16;
// 创建三角形箭头
arrowPath.moveTo(arrowX, arrowY);
arrowPath.lineTo(arrowX - arrowSize/2, arrowY + arrowSize);
arrowPath.lineTo(arrowX + arrowSize/2, arrowY + arrowSize);
arrowPath.close();
}
@Override
protected void onDraw(Canvas canvas) {
// 绘制主气泡(圆角矩形)
canvas.drawRoundRect(bubbleRect, 16, 16, bubblePaint);
// 绘制箭头
canvas.drawPath(arrowPath, bubblePaint);
super.onDraw(canvas);
}
}
```
### 4. **MainActivity.java - 点击事件处理**
```java
private void rightAddFrid() {
setMenuVisible(cn.wildfire.chat.kit.R.mipmap.ic_add, new View.OnClickListener() {
@Override
public void onClick(View view) {
showWeChatStylePopupMenu(view);
}
});
}
/**
* 显示微信风格的气泡弹框
*/
private void showWeChatStylePopupMenu(View anchorView) {
// 使用自定义气泡弹框
WeChatBubbleDialog bubbleDialog = new WeChatBubbleDialog(this, anchorView);
bubbleDialog.setOnMenuItemClickListener(new WeChatBubbleDialog.OnMenuItemClickListener() {
@Override
public void onGroupChatClick() {
createConversation();
}
@Override
public void onAddFriendClick() {
searchUser();
}
@Override
public void onScanClick() {
requestCameraPermissionAndScan();
}
});
bubbleDialog.show();
}
/**
* 请求相机权限并启动扫码
*/
private void requestCameraPermissionAndScan() {
XXPermissions.with(mContext)
.permission(PermissionLists.getCameraPermission())
.interceptor(new PermissionInterceptor())
.description(new PermissionDescription())
.request(new OnPermissionCallback() {
@Override
public void onResult(@NonNull List<IPermission> grantedList, @NonNull List<IPermission> deniedList) {
boolean allGranted = deniedList.isEmpty();
if (!allGranted) {
showToast("请申请相机权限");
return;
}
// 权限获取成功,启动扫码
startActivityForResult(new Intent(MainActivity.this, ScanQRCodeActivity.class), REQUEST_CODE_SCAN_QR_CODE);
}
});
}
```
### 5. **样式资源文件**
#### bg_wechat_bubble.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 深色半透明背景,类似微信 -->
<solid android:color="#CC000000" />
<!-- 圆角 -->
<corners android:radius="8dp" />
</shape>
```
#### bg_menu_item_selector.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下状态 - 深色背景下的高亮效果 -->
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#33FFFFFF" />
</shape>
</item>
<!-- 正常状态 -->
<item>
<shape android:shape="rectangle">
<solid android:color="#00000000" />
</shape>
</item>
</selector>
```
## 🎯 关键参数配置
### 1. **尺寸参数**
- **气泡宽度**: 屏幕宽度的40% (`screenWidth * 0.4`)
- **右边缘距离**: 10dp (`10 * density`)
- **左边缘最小距离**: 20dp
- **箭头尺寸**: 16x16dp
- **箭头位置**: 距离右边56dp
### 2. **颜色参数**
- **背景色**: `#CC000000` (深色半透明)
- **文字颜色**: `#FFFFFF` (白色)
- **分割线颜色**: `#33FFFFFF` (白色半透明)
- **点击高亮**: `#33FFFFFF` (白色半透明)
### 3. **间距参数**
- **菜单项内边距**: 32dp左右24dp上下
- **图标文字间距**: 16dp
- **气泡圆角**: 8dp
- **箭头圆角**: 16dp
## 📱 使用方法
### 1. **基本使用**
```java
// 创建气泡弹框
WeChatBubbleDialog bubbleDialog = new WeChatBubbleDialog(context, anchorView);
// 设置点击监听器
bubbleDialog.setOnMenuItemClickListener(new WeChatBubbleDialog.OnMenuItemClickListener() {
@Override
public void onGroupChatClick() {
// 处理发起群聊
}
@Override
public void onAddFriendClick() {
// 处理添加朋友
}
@Override
public void onScanClick() {
// 处理扫一扫
}
});
// 显示弹框
bubbleDialog.show();
```
### 2. **生命周期管理**
```java
// 关闭弹框
bubbleDialog.dismiss();
// 检查是否显示
if (bubbleDialog.isShowing()) {
// 弹框正在显示
}
```
## 🔧 自定义配置
### 1. **调整宽度比例**
```java
// 在 initPopupWindow() 和 show() 方法中修改
int bubbleWidth = (int) (screenWidth * 0.4); // 0.4 = 40%
// 可以调整为 0.3 (30%) 或 0.5 (50%)
```
### 2. **调整右边缘距离**
```java
// 在 show() 方法中修改
int marginRight = (int) (10 * context.getResources().getDisplayMetrics().density);
// 可以调整为 5dp 或 15dp
```
### 3. **调整箭头位置**
```java
// 在 WeChatBubbleView.java 的 createArrowPath() 方法中修改
int arrowX = width - 56; // 距离右边56dp
// 可以调整为 40dp 或 70dp
```
## 🎉 最终效果
实现的气泡弹框具有以下特点:
- ✅ **完全符合微信设计**: 深色半透明背景,白色文字
- ✅ **精确定位**: 右对齐10dp边距箭头指向加号
- ✅ **流畅交互**: 点击反馈,外部点击关闭
- ✅ **响应式设计**: 宽度自适应屏幕尺寸
- ✅ **易于维护**: 模块化设计,参数可配置
---
**文档版本**: v1.0
**创建时间**: 2024年10月26日
**状态**: 已实现
**维护者**: 开发团队