一、基础实现方案
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悬浮按钮的资料请关注代码网其它相关文章!
发表评论