服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - android实现可自由移动、监听点击事件的悬浮窗

android实现可自由移动、监听点击事件的悬浮窗

2022-08-31 16:35石炜贤的工作室 Android

这篇文章主要为大家详细介绍了android实现可自由移动、监听点击事件的悬浮窗,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

最近因为项目需要,自己实现了个可以自由移动,并且长按可以跳出一个控制播放的,大的悬浮窗。

好,开始吧。首先我们先聊权限,悬浮窗需要在manifest中声明一个权限:

?
1
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

然后呢,嗯,我们来讲讲关于悬浮窗实现的原理。

在Andriod中,所有的界面元素都要通过windowmanger来实现,像Activity、Fragment等等这些也是在其上实现。因此,我们的悬浮窗自然要通过这个实现。

这个项目中,我们自定义了两个悬浮窗view。我们以其中一个比较简单的为例:

我们自定义一个管理可以统一管理悬浮窗的类MyWindowManager,负责创建,删除悬浮窗

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
 * Created by shiwe on 2017/3/7.
 * 悬浮窗管理
 * 创建,移除
 * 单例模式
 */
 
public class MyWindowManager {
 
 private FloatNormalView normalView;
 private FloatControlView controlView;
 
 private static MyWindowManager instance;
 
 private MyWindowManager() {
 }
 
 public static MyWindowManager getInstance() {
  if (instance == null)
   instance = new MyWindowManager();
  return instance;
 }
 
 
 /**
  * 创建小型悬浮窗
  */
 public void createNormalView(Context context) {
  if (normalView == null)
   normalView = new FloatNormalView(context);
 }
 
 /**
  * 移除悬浮窗
  *
  * @param context
  */
 public void removeNormalView(Context context) {
  if (normalView != null) {
   WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
   windowManager.removeView(normalView);
   normalView = null;
  }
 }
 
 /**
  * 创建小型悬浮窗
  */
 public void createControlView(Context context) {
  if (controlView == null)
   controlView = new FloatControlView(context);
 }
 
 /**
  * 移除悬浮窗
  *
  * @param context
  */
 public void removeControlView(Context context) {
  if (controlView != null) {
   WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
   windowManager.removeView(controlView);
   controlView = null;
  }
 }
}

然后看看我们自定义的一个view,其继承自LinearLayout,我们在initLayoutParams初始化这个控件的位置等其他参数;在initEvent方法中定义随手指移动的监听事件以及长按的监听事件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
public class FloatNormalView extends LinearLayout {
 
 private Context context = null;
 private View view = null;
 private ImageView ivShowControlView = null;
 private WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
 private static WindowManager windowManager;
 
 private float mTouchStartX;
 private float mTouchStartY;
 private float x;
 private float y;
 private boolean initViewPlace = false;
 private MyWindowManager myWindowManager;
 private boolean isControlViewShowing = false;
 
 public FloatNormalView(Context context) {
  super(context);
  this.context = context;
  myWindowManager = MyWindowManager.getInstance();
  LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
  view = findViewById(R.id.ll_float_normal);
  ivShowControlView = (ImageView) findViewById(R.id.iv_show_control_view);
  windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  initLayoutParams();
  initEvent();
 }
 
 
 /**
  * 初始化参数
  */
 private void initLayoutParams() {
  //屏幕宽高
  int screenWidth = windowManager.getDefaultDisplay().getWidth();
  int screenHeight = windowManager.getDefaultDisplay().getHeight();
 
  //总是出现在应用程序窗口之上。
  lp.type = WindowManager.LayoutParams.TYPE_PHONE;
 
  // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
  // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
  lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 
  //悬浮窗默认显示的位置
  lp.gravity = Gravity.START | Gravity.TOP;
  //指定位置
  lp.x = screenWidth - view.getLayoutParams().width * 2;
  lp.y = screenHeight / 2 + view.getLayoutParams().height * 2;
  //悬浮窗的宽高
  lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
  lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
  lp.format = PixelFormat.TRANSPARENT;
  windowManager.addView(this, lp);
 }
 
 /**
  * 设置悬浮窗监听事件
  */
 private void initEvent() {
  ivShowControlView.setOnLongClickListener(new OnLongClickListener() {
   @Override
   public boolean onLongClick(View view) {
    if (!isControlViewShowing) {
     myWindowManager.createControlView(context);
     isControlViewShowing = true;
    } else {
     myWindowManager.removeControlView(context);
     isControlViewShowing = false;
    }
    return true;
   }
  });
  view.setOnTouchListener(new OnTouchListener() {
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
     case MotionEvent.ACTION_DOWN:
      if (!initViewPlace) {
       initViewPlace = true;
       //获取初始位置
       mTouchStartX += (event.getRawX() - lp.x);
       mTouchStartY += (event.getRawY() - lp.y);
      } else {
       //根据上次手指离开的位置与此次点击的位置进行初始位置微调
       mTouchStartX += (event.getRawX() - x);
       mTouchStartY += (event.getRawY() - y);
      }
      break;
     case MotionEvent.ACTION_MOVE:
      // 获取相对屏幕的坐标,以屏幕左上角为原点
      x = event.getRawX();
      y = event.getRawY();
      updateViewPosition();
      break;
 
     case MotionEvent.ACTION_UP:
      break;
    }
    return true;
   }
  });
 }
 
 /**
  * 更新浮动窗口位置
  */
 private void updateViewPosition() {
  lp.x = (int) (x - mTouchStartX);
  lp.y = (int) (y - mTouchStartY);
  windowManager.updateViewLayout(this, lp);
 }

最后,只需要在Activity中调用mywindowManager中调用createxxx方法就可以。

?
1
2
3
4
5
6
7
8
9
10
11
12
public class MainActivity extends AppCompatActivity {
 
 MyWindowManager myWindowManager;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  myWindowManager = MyWindowManager.getInstance();
  myWindowManager.createNormalView(this.getApplicationContext());
 }
}

最后,附上demo项目的下载地址: android实现悬浮窗

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/code_better/article/details/60886614

延伸 · 阅读

精彩推荐
  • AndroidAndroid中AsyncTask与handler用法实例分析

    Android中AsyncTask与handler用法实例分析

    这篇文章主要介绍了Android中AsyncTask与handler用法,以实例形式较为详细的分析了Android中AsyncTask与handler的功能、用法与相关注意事项,并附带完整实例源码供读...

    阳光岛主10432021-04-07
  • AndroidAndroid中shape定义控件的使用

    Android中shape定义控件的使用

    Android中常常使用shape来定义控件的一些显示属性,接下来通过本篇文章给大家介绍Android中shape定义控件的使用,对android shape定义控件相关知识感兴趣的朋友...

    旧念5742021-05-06
  • AndroidAndroid实现歌词滚动效果

    Android实现歌词滚动效果

    这篇文章主要为大家详细介绍了Android实现歌词滚动效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    书弋江山4672022-08-17
  • AndroidAndroid Mms之:联系人管理的应用分析

    Android Mms之:联系人管理的应用分析

    本篇文章是对Android中的联系人管理进行了详细的分析介绍,需要的朋友参考下...

    Android开发网9812021-01-18
  • Androidweb app与原生app的区别

    web app与原生app的区别

    本文主要给大家分析介绍了web app与原生app的区别,以及各自的优势和劣势,推荐给大家,有需要的小伙伴来参考下吧...

    Android开发网6282021-03-14
  • AndroidAndroid drawable微技巧,你不知道的drawable细节

    Android drawable微技巧,你不知道的drawable细节

    今天小编就为大家分享一篇关于Android drawable微技巧,你不知道的drawable细节,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一...

    guolin5942022-08-09
  • AndroidAndroid笔记之:App模块化及工程扩展的应用

    Android笔记之:App模块化及工程扩展的应用

    这篇文章是android开发人员的必备知识,是我特别为大家整理和总结的,不求完美,但是有用...

    Android开发网10842021-01-13
  • AndroidAndroid编程实现对文件夹里文件排序的方法

    Android编程实现对文件夹里文件排序的方法

    这篇文章主要介绍了Android编程实现对文件夹里文件排序的方法,涉及Android文件操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    nosxcy11572021-05-13