Android实现类似360,QQ管家那样的悬浮窗
时间:2022-07-04 02:58:05
一、前言:
我手机从来不装这些东西,不过,有次看到同事的android手机上,有个QQ管家在桌面上浮着,同事拖动管家时,管家就变成一只鸟,桌面下方还有个弹弓,桌面顶部有只乌鸦,把管家也就是鸟拖动到弹弓那,然后,松手,鸟就飞出去。这个过程是动画过程,做的事,实际上是清楚内存。
二:原理:
其实,没什么原理,用到的就是WindowManager以及WindowManager.LayoutParams,对这个LayoutParams做文章,当设置为属性后,然后,创建一个View,将这个View添加到WindowManager中就行。
package com.chris.floats.window;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.WindowManager;
import android.app.Activity;
import android.content.Context;
public class MainActivity extends Activity {
private static WindowManager mWindowMgr = null;
private WindowManager.LayoutParams mWindowMgrParams = null;
private static FloatsWindowView mFloatsWindowView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/*
* 显示应用主界面时,去除悬浮层
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if(hasFocus){
if(mFloatsWindowView != null){
mWindowMgr.removeView(mFloatsWindowView);
mFloatsWindowView = null;
}
}else{
getWindowLayout();
}
}
private void initParams(){
DisplayMetrics dm = getResources().getDisplayMetrics();
mWindowMgrParams.x = dm.widthPixels - 136;
mWindowMgrParams.y = 300;
mWindowMgrParams.width = 136;
mWindowMgrParams.height = 136;
}
private void getWindowLayout(){
if(mFloatsWindowView == null){
mWindowMgr = (WindowManager)getBaseContext().getSystemService(Context.WINDOW_SERVICE);
mWindowMgrParams = new WindowManager.LayoutParams();
/*
* 2003 在指悬浮在所有界面之上
* (4.0+系统中,在下拉菜单下面,而在2.3中,在上拉菜单之上)
*/
mWindowMgrParams.type = 2003;
mWindowMgrParams.format = 1;
/*
* 代码实际是wmParams.flags |= FLAG_NOT_FOCUSABLE;
* 40的由来是wmParams的默认属性(32)+ FLAG_NOT_FOCUSABLE(8)
*/
mWindowMgrParams.flags = 40;
mWindowMgrParams.gravity = Gravity.LEFT | Gravity.TOP;
initParams();
mFloatsWindowView = new FloatsWindowView(this);
mWindowMgr.addView(mFloatsWindowView, mWindowMgrParams);
}
}
}
上面代码,主要在getWindowLayout函数中,最后两行就是创建一个View,并加入到WindowManager中。
继承View的悬浮View:
package com.chris.floats.window;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowManager;
public class FloatsWindowView extends View {
private Context mContext = null;
private WindowManager mWindowMgr = null;
private WindowManager.LayoutParams mWindowMgrParams = null;
private AnimationDrawable mAnimationDrawable = null;
private int iPosX = 0;
private int iPosY = 0;
private int iLastPosX = 0;
private int iLastPosY = 0;
private boolean bMoved = false;
public FloatsWindowView(Context context) {
this(context, null, 0);
}
public FloatsWindowView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatsWindowView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
mWindowMgr = (WindowManager)getContext().getApplicationContext().getSystemService("window");
mWindowMgrParams = new WindowManager.LayoutParams();
initParams();
mAnimationDrawable = new AnimationDrawable();
for(int i = 0; i < 4; i++){
int id = getResources().getIdentifier("a"+ i, "drawable", mContext.getPackageName());
mAnimationDrawable.addFrame(getResources().getDrawable(id), 100);
}
mAnimationDrawable.setOneShot(false);
this.setBackgroundDrawable(mAnimationDrawable);
OnPreDrawListener listener = new OnPreDrawListener(){
@Override
public boolean onPreDraw() {
mAnimationDrawable.start();
return true;
}
};
this.getViewTreeObserver().addOnPreDrawListener(listener);
}
private void initParams(){
DisplayMetrics dm = getResources().getDisplayMetrics();
mWindowMgrParams.x = dm.widthPixels - 136;
mWindowMgrParams.y = 300;
mWindowMgrParams.width = 136;
mWindowMgrParams.height = 136;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
iPosX = (int)event.getX();
iPosY = (int)event.getY();
bMoved = false;
break;
case MotionEvent.ACTION_MOVE:
bMoved = true;
iLastPosX = (int)event.getX();
iLastPosY = (int)event.getY();
updatePostion(iLastPosX - iPosX, iLastPosY - iPosY);
break;
case MotionEvent.ACTION_UP:
if(!bMoved){
Intent it=new Intent(mContext, MainActivity.class);
mContext.startActivity(it);
}
break;
default:
break;
}
return true;
}
private void updatePostion(int x, int y){
mWindowMgrParams.type = 2003;
mWindowMgrParams.format = 1;
mWindowMgrParams.flags = 40;
mWindowMgrParams.gravity = Gravity.LEFT | Gravity.TOP;
mWindowMgrParams.x += x;
mWindowMgrParams.y += y;
mWindowMgr.updateViewLayout(this, mWindowMgrParams);
}
}
之所以将updatePosition中的参数与Activity中设置一样,是为了确保在MOVE时,造成相对位置的不一样,而导致闪砾,大家要是不理解,可以实验下。
三、小结:
这篇文章实现了简单的悬浮窗口动画效果,如果要想做成像360,QQ管家那样,还需要一些其它的操作:
1. 比如启动一个后台服务来监控系统信息;
2. ACTION_DOWN时,修改悬浮窗口上的图片;
3. ACTION_MOVE时窗口跟随;
4. ACTION_UP时,创建一个线程,来完成释放后,向上运动的动画过程等;
标签:360悬浮窗,QQ管家悬浮窗
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
MyBatis-Plus代码生成器的使用详解
2022-09-04 23:14:40
![](https://img.aspxhome.com/file/2023/4/85824_0s.png)
Java线程Timer定时器用法详细总结
2022-02-16 23:11:54
Android10 分区存储的适配规则
2022-11-25 12:22:53
SpringBoot使用自动配置xxxAutoConfiguration
2022-11-20 09:05:56
WPF使用Geometry绘制几何图形
2023-08-14 14:13:31
![](https://img.aspxhome.com/file/2023/7/108717_0s.gif)
Mybatis 动态表名+Map参数传递+批量操作详解
2023-04-02 04:08:00
![](https://img.aspxhome.com/file/2023/1/92631_0s.jpg)
springboot @WebFilter注解过滤器的实现
2023-07-06 11:14:54
java与JSON数据的转换实例详解
2022-07-03 22:48:25
spring Roo安装使用简介
2023-04-10 09:31:50
![](https://img.aspxhome.com/file/2023/0/119120_0s.png)
kafka生产者发送消息流程深入分析讲解
2022-10-12 08:39:30
![](https://img.aspxhome.com/file/2023/6/125216_0s.png)
Kotlin Navigation可视化开发详解
2022-10-18 10:55:56
![](https://img.aspxhome.com/file/2023/4/111714_0s.png)
浅析JAVA中的内存结构、重载、this与继承
2023-09-24 19:15:07
SpringBoot中如何统一接口返回与全局异常处理详解
2021-08-11 02:22:35
![](https://img.aspxhome.com/file/2023/9/129229_0s.png)
JDK1.8中的ConcurrentHashMap源码分析
2023-11-27 06:02:32
![](https://img.aspxhome.com/file/2023/1/60481_0s.jpg)
Java进阶知识之反射的概念与获取方法
2023-12-09 19:09:14
![](https://img.aspxhome.com/file/2023/8/108418_0s.jpg)
C# Linq延迟查询的执行实例代码
2023-04-24 05:34:59
C#中常用的正则表达式
2023-10-15 21:10:42
Java String类的常用方法汇总
2022-04-10 11:58:24
Java微信支付-微信红包
2023-11-01 01:07:05
![](https://img.aspxhome.com/file/2023/6/66166_0s.png)
Spring Security OAuth2实现使用JWT的示例代码
2022-11-02 22:46:25