android 添加随意拖动的桌面悬浮窗口
程序员文章站
2023-12-15 11:41:04
用过新版本android 360手机助手都人都对 360中只在桌面显示一个小小悬浮窗口羡慕不已吧? 其实实现这种功能,主要有两步: 1.判断当前显示的是为桌面。这个内容我在...
用过新版本android 360手机助手都人都对 360中只在桌面显示一个小小悬浮窗口羡慕不已吧?
其实实现这种功能,主要有两步:
1.判断当前显示的是为桌面。这个内容我在前面的帖子里面已经有过介绍,如果还没看过的赶快稳步看一下哦。
2.使用windowmanager往最顶层添加一个view
.这个知识点就是为本文主要讲解的内容哦。在本文的讲解中,我们还会讲到下面的知识点:
a.如果获取到状态栏的高度
b.悬浮窗口的拖动
c.悬浮窗口的点击事件
有开始之前,我们先来看一下效果图:
接下来我们来看看floatview的代码:
public class floatview extends imageview{
private float mtouchx;
private float mtouchy;
private float x;
private float y;
private float mstartx;
private float mstarty;
private onclicklistener mclicklistener;
private windowmanager windowmanager = (windowmanager) getcontext()
.getapplicationcontext().getsystemservice(context.window_service);
// 此windowmanagerparams变量为获取的全局变量,用以保存悬浮窗口的属性
private windowmanager.layoutparams windowmanagerparams = ((floatapplication) getcontext()
.getapplicationcontext()).getwindowparams();
public floatview(context context) {
super(context);
}
@override
public boolean ontouchevent(motionevent event) {
//获取到状态栏的高度
rect frame = new rect();
getwindowvisibledisplayframe(frame);
int statusbarheight = frame.top;
system.out.println("statusbarheight:"+statusbarheight);
// 获取相对屏幕的坐标,即以屏幕左上角为原点
x = event.getrawx();
y = event.getrawy() - statusbarheight; // statusbarheight是系统状态栏的高度
log.i("tag", "currx" + x + "====curry" + y);
switch (event.getaction()) {
case motionevent.action_down: // 捕获手指触摸按下动作
// 获取相对view的坐标,即以此view左上角为原点
mtouchx = event.getx();
mtouchy = event.gety();
mstartx = x;
mstarty = y;
log.i("tag", "startx" + mtouchx + "====starty"
+ mtouchy);
break;
case motionevent.action_move: // 捕获手指触摸移动动作
updateviewposition();
break;
case motionevent.action_up: // 捕获手指触摸离开动作
updateviewposition();
mtouchx = mtouchy = 0;
if ((x - mstartx) < 5 && (y - mstarty) < 5) {
if(mclicklistener!=null) {
mclicklistener.onclick(this);
}
}
break;
}
return true;
}
@override
public void setonclicklistener(onclicklistener l) {
this.mclicklistener = l;
}
private void updateviewposition() {
// 更新浮动窗口位置参数
windowmanagerparams.x = (int) (x - mtouchx);
windowmanagerparams.y = (int) (y - mtouchy);
windowmanager.updateviewlayout(this, windowmanagerparams); // 刷新显示
}
}
代码解释:
int statusbarheight = frame.top;
为获取状态栏的高度,为什么在event.getrawy()的时候减去状态栏的高度呢?
因为我们的悬浮窗口不可能显示到状态栏中去,而后getrawy为获取到屏幕原点的距离。当我们屏幕处于全屏模式时,获取到的状态栏高度会变成0
(x - mstartx) < 5 && (y - mstarty) < 5
如果我们在触摸过程中,移动距离少于5 ,则视为点击,触发点击的回调。
另外我们需要自定义一个application:
public class floatapplication extends application {
private windowmanager.layoutparams windowparams = new windowmanager.layoutparams();
public windowmanager.layoutparams getwindowparams() {
return windowparams;
}
}
代码解释:
自定义application的目的是为了保存windowparams的值 ,因为我们在拖动悬浮窗口的时候,如果每次都重新new一个layoutparams的话,在update
的时候会在异常发现。
windowparams的值也不一定非得在自定义application里面来保存,只要是全局的都行。
最后我们再来看看activity中的实现。
public class mainactivity extends activity implements onclicklistener{
private windowmanager windowmanager = null;
private windowmanager.layoutparams windowmanagerparams = null;
private floatview floatview = null;
@override
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
requestwindowfeature(window.feature_no_title);//取消标题栏
getwindow().setflags(windowmanager.layoutparams. flag_fullscreen ,
windowmanager.layoutparams. flag_fullscreen);//全屏
setcontentview(r.layout.activity_main);
createview();
}
@override
public boolean oncreateoptionsmenu(menu menu) {
getmenuinflater().inflate(r.menu.activity_main, menu);
return true;
}
public void ondestroy() {
super.ondestroy();
// 在程序退出(activity销毁)时销毁悬浮窗口
windowmanager.removeview(floatview);
}
private void createview() {
floatview = new floatview(getapplicationcontext());
floatview.setonclicklistener(this);
floatview.setimageresource(r.drawable.ic_launcher); // 这里简单的用自带的icon来做演示
// 获取windowmanager
windowmanager = (windowmanager) getapplicationcontext().getsystemservice(context.window_service);
// 设置layoutparams(全局变量)相关参数
windowmanagerparams = ((floatapplication) getapplication()).getwindowparams();
windowmanagerparams.type = layoutparams.type_phone; // 设置window type
windowmanagerparams.format = pixelformat.rgba_8888; // 设置图片格式,效果为背景透明
// 设置window flag
windowmanagerparams.flags = layoutparams.flag_not_touch_modal
| layoutparams.flag_not_focusable;
/*
* 注意,flag的值可以为:
* layoutparams.flag_not_touch_modal 不影响后面的事件
* layoutparams.flag_not_focusable 不可聚焦
* layoutparams.flag_not_touchable 不可触摸
*/
// 调整悬浮窗口至左上角,便于调整坐标
windowmanagerparams.gravity = gravity.left | gravity.top;
// 以屏幕左上角为原点,设置x、y初始值
windowmanagerparams.x = 0;
windowmanagerparams.y = 0;
// 设置悬浮窗口长宽数据
windowmanagerparams.width = layoutparams.wrap_content;
windowmanagerparams.height = layoutparams.wrap_content;
// 显示myfloatview图像
windowmanager.addview(floatview, windowmanagerparams);
}
public void onclick(view v) {
toast.maketext(this, "clicked", toast.length_short).show();
}
}
代码解释:
在activity中我们主要是添加悬浮窗,并且设置他的位置。另外需要注意flags的应用:
layoutparams.flag_not_touch_modal 不影响后面的事件
layoutparams.flag_not_focusable 不可聚焦
layoutparams.flag_not_touchable 不可触摸
最后我们在ondestroy()中移除到悬浮窗口。所以,我们测试的时候,记得按home键来切换到桌面。
最后千万记得,在androidmanifest.xml中来申明我们需要用到的android.permission.system_alert_window权限
并且记得申明我们自定义的application哦。
androidmanifest.xml代码如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.krislq.floating"
android:versioncode="1"
android:versionname="1.0" >
<uses-sdk
android:minsdkversion="8"
android:targetsdkversion="15" />
<uses-permission android:name="android.permission.system_alert_window" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/apptheme" android:name="floatapplication">
<activity
android:name=".mainactivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.main" />
<category android:name="android.intent.category.launcher" />
</intent-filter>
</activity>
</application>
</manifest>
其实实现这种功能,主要有两步:
1.判断当前显示的是为桌面。这个内容我在前面的帖子里面已经有过介绍,如果还没看过的赶快稳步看一下哦。
2.使用windowmanager往最顶层添加一个view
.这个知识点就是为本文主要讲解的内容哦。在本文的讲解中,我们还会讲到下面的知识点:
a.如果获取到状态栏的高度
b.悬浮窗口的拖动
c.悬浮窗口的点击事件
有开始之前,我们先来看一下效果图:
接下来我们来看看floatview的代码:
复制代码 代码如下:
public class floatview extends imageview{
private float mtouchx;
private float mtouchy;
private float x;
private float y;
private float mstartx;
private float mstarty;
private onclicklistener mclicklistener;
private windowmanager windowmanager = (windowmanager) getcontext()
.getapplicationcontext().getsystemservice(context.window_service);
// 此windowmanagerparams变量为获取的全局变量,用以保存悬浮窗口的属性
private windowmanager.layoutparams windowmanagerparams = ((floatapplication) getcontext()
.getapplicationcontext()).getwindowparams();
public floatview(context context) {
super(context);
}
@override
public boolean ontouchevent(motionevent event) {
//获取到状态栏的高度
rect frame = new rect();
getwindowvisibledisplayframe(frame);
int statusbarheight = frame.top;
system.out.println("statusbarheight:"+statusbarheight);
// 获取相对屏幕的坐标,即以屏幕左上角为原点
x = event.getrawx();
y = event.getrawy() - statusbarheight; // statusbarheight是系统状态栏的高度
log.i("tag", "currx" + x + "====curry" + y);
switch (event.getaction()) {
case motionevent.action_down: // 捕获手指触摸按下动作
// 获取相对view的坐标,即以此view左上角为原点
mtouchx = event.getx();
mtouchy = event.gety();
mstartx = x;
mstarty = y;
log.i("tag", "startx" + mtouchx + "====starty"
+ mtouchy);
break;
case motionevent.action_move: // 捕获手指触摸移动动作
updateviewposition();
break;
case motionevent.action_up: // 捕获手指触摸离开动作
updateviewposition();
mtouchx = mtouchy = 0;
if ((x - mstartx) < 5 && (y - mstarty) < 5) {
if(mclicklistener!=null) {
mclicklistener.onclick(this);
}
}
break;
}
return true;
}
@override
public void setonclicklistener(onclicklistener l) {
this.mclicklistener = l;
}
private void updateviewposition() {
// 更新浮动窗口位置参数
windowmanagerparams.x = (int) (x - mtouchx);
windowmanagerparams.y = (int) (y - mtouchy);
windowmanager.updateviewlayout(this, windowmanagerparams); // 刷新显示
}
}
代码解释:
int statusbarheight = frame.top;
为获取状态栏的高度,为什么在event.getrawy()的时候减去状态栏的高度呢?
因为我们的悬浮窗口不可能显示到状态栏中去,而后getrawy为获取到屏幕原点的距离。当我们屏幕处于全屏模式时,获取到的状态栏高度会变成0
(x - mstartx) < 5 && (y - mstarty) < 5
如果我们在触摸过程中,移动距离少于5 ,则视为点击,触发点击的回调。
另外我们需要自定义一个application:
复制代码 代码如下:
public class floatapplication extends application {
private windowmanager.layoutparams windowparams = new windowmanager.layoutparams();
public windowmanager.layoutparams getwindowparams() {
return windowparams;
}
}
代码解释:
自定义application的目的是为了保存windowparams的值 ,因为我们在拖动悬浮窗口的时候,如果每次都重新new一个layoutparams的话,在update
的时候会在异常发现。
windowparams的值也不一定非得在自定义application里面来保存,只要是全局的都行。
最后我们再来看看activity中的实现。
复制代码 代码如下:
public class mainactivity extends activity implements onclicklistener{
private windowmanager windowmanager = null;
private windowmanager.layoutparams windowmanagerparams = null;
private floatview floatview = null;
@override
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
requestwindowfeature(window.feature_no_title);//取消标题栏
getwindow().setflags(windowmanager.layoutparams. flag_fullscreen ,
windowmanager.layoutparams. flag_fullscreen);//全屏
setcontentview(r.layout.activity_main);
createview();
}
@override
public boolean oncreateoptionsmenu(menu menu) {
getmenuinflater().inflate(r.menu.activity_main, menu);
return true;
}
public void ondestroy() {
super.ondestroy();
// 在程序退出(activity销毁)时销毁悬浮窗口
windowmanager.removeview(floatview);
}
private void createview() {
floatview = new floatview(getapplicationcontext());
floatview.setonclicklistener(this);
floatview.setimageresource(r.drawable.ic_launcher); // 这里简单的用自带的icon来做演示
// 获取windowmanager
windowmanager = (windowmanager) getapplicationcontext().getsystemservice(context.window_service);
// 设置layoutparams(全局变量)相关参数
windowmanagerparams = ((floatapplication) getapplication()).getwindowparams();
windowmanagerparams.type = layoutparams.type_phone; // 设置window type
windowmanagerparams.format = pixelformat.rgba_8888; // 设置图片格式,效果为背景透明
// 设置window flag
windowmanagerparams.flags = layoutparams.flag_not_touch_modal
| layoutparams.flag_not_focusable;
/*
* 注意,flag的值可以为:
* layoutparams.flag_not_touch_modal 不影响后面的事件
* layoutparams.flag_not_focusable 不可聚焦
* layoutparams.flag_not_touchable 不可触摸
*/
// 调整悬浮窗口至左上角,便于调整坐标
windowmanagerparams.gravity = gravity.left | gravity.top;
// 以屏幕左上角为原点,设置x、y初始值
windowmanagerparams.x = 0;
windowmanagerparams.y = 0;
// 设置悬浮窗口长宽数据
windowmanagerparams.width = layoutparams.wrap_content;
windowmanagerparams.height = layoutparams.wrap_content;
// 显示myfloatview图像
windowmanager.addview(floatview, windowmanagerparams);
}
public void onclick(view v) {
toast.maketext(this, "clicked", toast.length_short).show();
}
}
代码解释:
在activity中我们主要是添加悬浮窗,并且设置他的位置。另外需要注意flags的应用:
layoutparams.flag_not_touch_modal 不影响后面的事件
layoutparams.flag_not_focusable 不可聚焦
layoutparams.flag_not_touchable 不可触摸
最后我们在ondestroy()中移除到悬浮窗口。所以,我们测试的时候,记得按home键来切换到桌面。
最后千万记得,在androidmanifest.xml中来申明我们需要用到的android.permission.system_alert_window权限
并且记得申明我们自定义的application哦。
androidmanifest.xml代码如下:
复制代码 代码如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.krislq.floating"
android:versioncode="1"
android:versionname="1.0" >
<uses-sdk
android:minsdkversion="8"
android:targetsdkversion="15" />
<uses-permission android:name="android.permission.system_alert_window" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/apptheme" android:name="floatapplication">
<activity
android:name=".mainactivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.main" />
<category android:name="android.intent.category.launcher" />
</intent-filter>
</activity>
</application>
</manifest>