一、基础实现方案
1. 使用 windowmanager 实现全局悬浮窗
这是最灵活的实现方式,可以在任何界面显示悬浮按钮:
public class floatingbuttonservice extends service { private windowmanager windowmanager; private view floatingbutton; @override public ibinder onbind(intent intent) { return null; } @override public void oncreate() { super.oncreate(); // 创建悬浮按钮视图 floatingbutton = layoutinflater.from(this).inflate(r.layout.floating_button, null); // 设置按钮点击事件 floatingbutton.findviewbyid(r.id.float_button).setonclicklistener(v -> { // 处理点击事件 toast.maketext(this, "悬浮按钮被点击", toast.length_short).show(); }); // 设置窗口参数 windowmanager.layoutparams params = new windowmanager.layoutparams( windowmanager.layoutparams.wrap_content, windowmanager.layoutparams.wrap_content, build.version.sdk_int >= build.version_codes.o ? windowmanager.layoutparams.type_application_overlay : windowmanager.layoutparams.type_phone, windowmanager.layoutparams.flag_not_focusable, pixelformat.translucent); // 设置初始位置 params.gravity = gravity.top | gravity.start; params.x = 0; params.y = 100; // 获取windowmanager并添加视图 windowmanager = (windowmanager) getsystemservice(window_service); windowmanager.addview(floatingbutton, params); // 添加拖拽功能 adddragfeature(); } private void adddragfeature() { floatingbutton.setontouchlistener(new view.ontouchlistener() { private int initialx; private int initialy; private float initialtouchx; private float initialtouchy; @override public boolean ontouch(view v, motionevent event) { switch (event.getaction()) { case motionevent.action_down: initialx = params.x; initialy = params.y; initialtouchx = event.getrawx(); initialtouchy = event.getrawy(); return true; case motionevent.action_move: params.x = initialx + (int) (event.getrawx() - initialtouchx); params.y = initialy + (int) (event.getrawy() - initialtouchy); windowmanager.updateviewlayout(floatingbutton, params); return true; } return false; } }); } @override public void ondestroy() { super.ondestroy(); if (floatingbutton != null) { windowmanager.removeview(floatingbutton); } } }
2. 布局文件 (res/layout/floating_button.xml)
<?xml version="1.0" encoding="utf-8"?> <framelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <imagebutton android:id="@+id/float_button" android:layout_width="56dp" android:layout_height="56dp" android:background="@drawable/circle_background" android:src="@drawable/ic_float_button" android:elevation="8dp" android:layout_margin="16dp" /> </framelayout>
3. 圆形背景 (res/drawable/circle_background.xml)
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@color/colorprimary" /> </shape>
4. 启动服务
// 在需要显示悬浮按钮的地方启动服务 startservice(new intent(context, floatingbuttonservice.class)); // 停止服务 stopservice(new intent(context, floatingbuttonservice.class));
二、权限处理
1. androidmanifest.xml 中添加权限
<uses-permission android:name="android.permission.system_alert_window" />
2. 检查并请求权限
// 检查悬浮窗权限 if (build.version.sdk_int >= build.version_codes.m) { if (!settings.candrawoverlays(this)) { intent intent = new intent(settings.action_manage_overlay_permission, uri.parse("package:" + getpackagename())); startactivityforresult(intent, overlay_permission_req); } else { // 已经有权限,启动服务 startservice(new intent(this, floatingbuttonservice.class)); } } else { // 6.0以下直接启动 startservice(new intent(this, floatingbuttonservice.class)); }
三、高级功能扩展
1. 添加动画效果
// 在按钮点击时添加动画 floatingbutton.setonclicklistener(v -> { // 缩放动画 objectanimator scalex = objectanimator.offloat(v, "scalex", 1f, 0.8f, 1f); objectanimator scaley = objectanimator.offloat(v, "scaley", 1f, 0.8f, 1f); animatorset animatorset = new animatorset(); animatorset.playtogether(scalex, scaley); animatorset.setduration(200); animatorset.start(); // 执行点击操作 performbuttonaction(); });
2. 自动吸附边缘
private void autoattachtoedge() { int screenwidth = getresources().getdisplaymetrics().widthpixels; int buttonwidth = floatingbutton.getwidth(); if (params.x < screenwidth / 2 - buttonwidth / 2) { // 吸附到左边 params.x = 0; } else { // 吸附到右边 params.x = screenwidth - buttonwidth; } windowmanager.updateviewlayout(floatingbutton, params); }
3. 显示/隐藏动画
public void hidebutton() { floatingbutton.animate() .translationy(floatingbutton.getheight()) .setduration(300) .start(); } public void showbutton() { floatingbutton.animate() .translationy(0) .setduration(300) .start(); }
四、优化建议
性能优化:
- 使用轻量级的视图层级
- 避免频繁调用
updateviewlayout
- 使用硬件加速
内存管理:
- 在不需要时及时移除悬浮窗
- 在低内存时自动隐藏
用户体验:
- 添加适当的触摸反馈
- 考虑屏幕旋转时的位置调整
- 提供设置选项让用户自定义位置和行为
兼容性处理:
- 处理不同 android 版本的权限差异
- 适配各种屏幕尺寸和密度
- 考虑全面屏和刘海屏的适配
五、替代方案
1. 使用 coordinatorlayout + floatingactionbutton
如果只需要在应用内显示悬浮按钮,可以使用 material design 组件:
<androidx.coordinatorlayout.widget.coordinatorlayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 其他内容 --> <com.google.android.material.floatingactionbutton.floatingactionbutton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="16dp" android:src="@drawable/ic_add" /> </androidx.coordinatorlayout.widget.coordinatorlayout>
2. 使用第三方库
一些流行的悬浮按钮库:
六、常见问题解决
权限问题:
- 确保已正确请求
system_alert_window
权限 - 在 android 6.0+ 上需要动态请求权限
- 某些厂商 rom 可能需要额外的白名单设置
- 确保已正确请求
位置不正确:
- 检查
windowmanager.layoutparams
的 gravity 设置 - 考虑状态栏和导航栏的高度
- 在屏幕旋转时更新位置
- 检查
点击穿透:
- 设置
flag_not_focusable
可能导致点击事件穿透 - 可以通过在
ontouch
中返回true
来拦截事件
- 设置
内存泄漏:
- 确保在服务销毁时移除视图
- 避免在视图中持有 activity 引用
以上就是android studio实现自定义全局悬浮按钮的示例代码的详细内容,更多关于android studio悬浮按钮的资料请关注代码网其它相关文章!
发表评论