当前位置:   article > 正文

【Android】跟着chatGPT学习悬浮窗的实现_android 悬浮窗 service

android 悬浮窗 service

请实现Android里的悬浮窗,这个悬浮窗里有2个TextView

实现 Android 悬浮窗需要涉及到以下几个步骤:

  1. 创建一个悬浮窗口的布局,包含两个 TextView。

  1. 创建一个 Service,并在 Service 的 onCreate() 方法中创建悬浮窗口。

  1. 在悬浮窗口的布局中添加两个 TextView。

  1. 在 Service 中实现悬浮窗口的拖动和关闭功能。

  1. 在 AndroidManifest.xml 文件中声明 Service。

下面是一个简单的示例代码:

csharpCopy

  1. public class FloatingWindowService extends Service {
  2. private WindowManager windowManager;
  3. private View floatingView;
  4. @Override
  5. public IBinder onBind(Intent intent) {
  6. return null;
  7. }
  8. @Override
  9. public void onCreate() {
  10. super.onCreate();
  11. // 创建悬浮窗口的布局
  12. floatingView = LayoutInflater.from(this).inflate(R.layout.floating_window, null);
  13. // 获取 WindowManager
  14. windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
  15. // 设置悬浮窗口的参数
  16. WindowManager.LayoutParams params = new WindowManager.LayoutParams(
  17. WindowManager.LayoutParams.WRAP_CONTENT,
  18. WindowManager.LayoutParams.WRAP_CONTENT,
  19. Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
  20. ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
  21. : WindowManager.LayoutParams.TYPE_PHONE,
  22. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  23. PixelFormat.TRANSLUCENT);
  24. // 设置悬浮窗口的位置和大小
  25. params.gravity = Gravity.TOP | Gravity.START;
  26. params.x = 0;
  27. params.y = 100;
  28. // 添加悬浮窗口的布局
  29. windowManager.addView(floatingView, params);
  30. // 添加拖动和关闭功能
  31. floatingView.setOnTouchListener(new FloatingOnTouchListener(params));
  32. floatingView.findViewById(R.id.close_button).setOnClickListener(new FloatingOnClickListener());
  33. }
  34. @Override
  35. public void onDestroy() {
  36. super.onDestroy();
  37. // 移除悬浮窗口的布局
  38. if (floatingView != null) {
  39. windowManager.removeView(floatingView);
  40. }
  41. }
  42. // 实现拖动功能
  43. private class FloatingOnTouchListener implements View.OnTouchListener {
  44. private int initialX;
  45. private int initialY;
  46. private float initialTouchX;
  47. private float initialTouchY;
  48. private WindowManager.LayoutParams params;
  49. FloatingOnTouchListener(WindowManager.LayoutParams params) {
  50. this.params = params;
  51. }
  52. @Override
  53. public boolean onTouch(View v, MotionEvent event) {
  54. switch (event.getAction()) {
  55. case MotionEvent.ACTION_DOWN:
  56. // 记录触摸点的位置和悬浮窗口的位置
  57. initialX = params.x;
  58. initialY = params.y;
  59. initialTouchX = event.getRawX();
  60. initialTouchY = event.getRawY();
  61. return true;
  62. case MotionEvent.ACTION_UP:
  63. // 拖动结束,更新悬浮窗口的位置
  64. int newX = initialX + (int) (event.getRawX() - initialTouchX);
  65. int newY = initialY + (int) (event.getRawY() - initialTouchY);
  66. params.x = newX;
  67. params.y = newY;
  68. windowManager.updateViewLayout(floatingView, params);
  69. return true;
  70. case MotionEvent.ACTION_MOVE:
  71. // 拖动中,更新悬浮窗口的位置
  72. int newX1 = initialX + (int) (event.getRawX() - initialTouchX);
  73. int newY1 = initialY + (int) (event.getRawY() - initialTouchY);
  74. params.x = newX1;
  75. params.y = newY1;
  76. windowManager.updateViewLayout(floatingView, params);
  77. return true;
  78. }
  79. return false;
  80. }
  81. }
  82. // 实现关闭功能
  83. private class FloatingOnClickListener implements View.OnClickListener {
  84. @Override
  85. public void onClick(View v) {
  86. stopSelf();
  87. }
  88. }
  89. }

在这个代码中,我们创建了一个 FloatingWindowService 类,并在 onCreate() 方法中创建了一个悬浮窗口的布局。我们还在 Service 中实现了悬浮窗口的拖动和关闭功能。

悬浮窗口的布局文件(floating_window.xml)可以包含两个 TextView,如下所示:

iniCopy

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:id="@+id/floating_layout"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content"
  5. android:background="@android:color/white">
  6. <TextView
  7. android:id="@+id/text_view1"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:text="TextView 1" />
  11. <TextView
  12. android:id="@+id/text_view2"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:text="TextView 2"
  16. android:layout_below="@id/text_view1" />
  17. <Button
  18. android:id="@+id/close_button"
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:text="Close"
  22. android:layout_alignParentEnd="true" />
  23. </RelativeLayout>

最后,在 AndroidManifest.xml 文件中声明 Service:

Copy

<service android:name=".FloatingWindowService" />

这样,我们就完成了 Android 悬浮窗的实现。

很好,例子里的java代码请修改为kotlin

好的,下面是 Kotlin 版本的代码实现:

kotlinCopy

  1. class FloatingWindowService : Service() {
  2. private lateinit var windowManager: WindowManager
  3. private lateinit var floatingView: View
  4. override fun onBind(intent: Intent?): IBinder? {
  5. return null
  6. }
  7. override fun onCreate() {
  8. super.onCreate()
  9. // 创建悬浮窗口的布局
  10. floatingView = LayoutInflater.from(this).inflate(R.layout.floating_window, null)
  11. // 获取 WindowManager
  12. windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
  13. // 设置悬浮窗口的参数
  14. val params = WindowManager.LayoutParams(
  15. WindowManager.LayoutParams.WRAP_CONTENT,
  16. WindowManager.LayoutParams.WRAP_CONTENT,
  17. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
  18. WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
  19. else
  20. WindowManager.LayoutParams.TYPE_PHONE,
  21. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  22. PixelFormat.TRANSLUCENT
  23. )
  24. // 设置悬浮窗口的位置和大小
  25. params.gravity = Gravity.TOP or Gravity.START
  26. params.x = 0
  27. params.y = 100
  28. // 添加悬浮窗口的布局
  29. windowManager.addView(floatingView, params)
  30. // 添加拖动和关闭功能
  31. floatingView.setOnTouchListener(FloatingOnTouchListener(params))
  32. floatingView.findViewById<View>(R.id.close_button).setOnClickListener(FloatingOnClickListener())
  33. }
  34. override fun onDestroy() {
  35. super.onDestroy()
  36. // 移除悬浮窗口的布局
  37. if (::floatingView.isInitialized) {
  38. windowManager.removeView(floatingView)
  39. }
  40. }
  41. // 实现拖动功能
  42. private inner class FloatingOnTouchListener(private val params: WindowManager.LayoutParams) :
  43. View.OnTouchListener {
  44. private var initialX: Int = 0
  45. private var initialY: Int = 0
  46. private var initialTouchX: Float = 0f
  47. private var initialTouchY: Float = 0f
  48. override fun onTouch(v: View?, event: MotionEvent?): Boolean {
  49. when (event?.action) {
  50. MotionEvent.ACTION_DOWN -> {
  51. // 记录触摸点的位置和悬浮窗口的位置
  52. initialX = params.x
  53. initialY = params.y
  54. initialTouchX = event.rawX
  55. initialTouchY = event.rawY
  56. return true
  57. }
  58. MotionEvent.ACTION_UP -> {
  59. // 拖动结束,更新悬浮窗口的位置
  60. val newX = initialX + (event.rawX - initialTouchX).toInt()
  61. val newY = initialY + (event.rawY - initialTouchY).toInt()
  62. params.x = newX
  63. params.y = newY
  64. windowManager.updateViewLayout(floatingView, params)
  65. return true
  66. }
  67. MotionEvent.ACTION_MOVE -> {
  68. // 拖动中,更新悬浮窗口的位置
  69. val newX = initialX + (event.rawX - initialTouchX).toInt()
  70. val newY = initialY + (event.rawY - initialTouchY).toInt()
  71. params.x = newX
  72. params.y = newY
  73. windowManager.updateViewLayout(floatingView, params)
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. }
  80. // 实现关闭功能
  81. private inner class FloatingOnClickListener : View.OnClickListener {
  82. override fun onClick(v: View?) {
  83. stopSelf()
  84. }
  85. }
  86. }

在这个 Kotlin 版本的代码中,我们使用了 inner class 来实现了 FloatingOnTouchListener 和 FloatingOnClickListener 两个内部类,以及使用了 lateinit 修饰符来延迟初始化悬浮窗口布局。其它部分与 Java 版本的代码类似。

怎么设置悬浮窗的大小

要设置悬浮窗口的大小,可以在 WindowManager.LayoutParams 中设置其宽度和高度。具体来说,可以通过设置以下两个参数来控制悬浮窗口的大小:

  • width:悬浮窗口的宽度。

  • height:悬浮窗口的高度。

例如,我们可以将悬浮窗口的宽度设置为屏幕宽度的一半,高度设置为 200dp,代码如下:

stylusCopy

  1. // 获取屏幕宽度
  2. val displayMetrics = DisplayMetrics()
  3. windowManager.defaultDisplay.getMetrics(displayMetrics)
  4. val screenWidth = displayMetrics.widthPixels
  5. // 设置悬浮窗口的参数
  6. val params = WindowManager.LayoutParams(
  7. screenWidth / 2, // 悬浮窗口的宽度为屏幕宽度的一半
  8. resources.getDimensionPixelSize(R.dimen.floating_window_height), // 悬浮窗口的高度为 200dp
  9. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
  10. WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
  11. else
  12. WindowManager.LayoutParams.TYPE_PHONE,
  13. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  14. PixelFormat.TRANSLUCENT
  15. )

在这个示例代码中,我们首先获取了屏幕宽度,然后将悬浮窗口的宽度设置为屏幕宽度的一半,高度设置为 200dp,使用了 resources.getDimensionPixelSize() 方法来获得 200dp 对应的像素值。

需要注意的是,悬浮窗口的大小不应该超过屏幕的可见范围,否则可能会导致悬浮窗口无法完全显示。另外,如果悬浮窗口的大小发生变化,需要调用 WindowManager.updateViewLayout() 方法来更新悬浮窗口的布局。

Copy

  1. // 更新悬浮窗口的布局
  2. windowManager.updateViewLayout(floatingView, params)

在这个示例代码中,我们将更新悬浮窗口的布局放在了拖动事件的处理中,如果需要在其它地方更新悬浮窗口的布局,也可以使用同样的方式调用 WindowManager.updateViewLayout() 方法。

params.gravity 是什么

params.gravity 是指悬浮窗口的重心,即悬浮窗口在屏幕上的对齐方式。Gravity 是 Android 中一个常用的布局参数,用于控制视图在屏幕上的位置、大小和对齐方式等。

在 WindowManager.LayoutParams 中,gravity 是一个 int 类型的参数,可以使用 | 运算符进行组合。常见的 gravity 参数值有以下几种:

  • Gravity.TOP:将视图的顶部与其父容器的顶部对齐。

  • Gravity.BOTTOM:将视图的底部与其父容器的底部对齐。

  • Gravity.LEFT:将视图的左侧与其父容器的左侧对齐。

  • Gravity.RIGHT:将视图的右侧与其父容器的右侧对齐。

  • Gravity.CENTER_HORIZONTAL:将视图水平居中对齐。

  • Gravity.CENTER_VERTICAL:将视图垂直居中对齐。

  • Gravity.CENTER:将视图水平和垂直居中对齐。

可以通过将这些参数值组合起来,来控制视图在屏幕上的对齐方式。例如,params.gravity = Gravity.TOP or Gravity.START 将悬浮窗口的重心设置为左上角。

在悬浮窗口的实现中,params.gravity 可以用于设置悬浮窗口相对于屏幕左上角的位置。例如,params.gravity = Gravity.TOP or Gravity.START 将悬浮窗口设置在屏幕左上角,而 params.gravity = Gravity.BOTTOM or Gravity.END 将悬浮窗口设置在屏幕右下角。

需要注意的是,params.gravity 的默认值是 Gravity.TOP | Gravity.START,即悬浮窗口默认位于屏幕左上角。如果需要调整悬浮窗口的位置,可以通过修改 params.gravity 的值来实现。

上面的代码中,WindowManager.LayoutParams.WRAP_CONTENT 是多少

WindowManager.LayoutParams.WRAP_CONTENT 表示悬浮窗口的宽度或高度使用内容的大小,即根据悬浮窗口内部的视图内容来动态调整悬浮窗口的大小。

在悬浮窗口实现中,通常使用 WindowManager.LayoutParams.WRAP_CONTENT 来指定悬浮窗口的宽度或高度,以便悬浮窗口的大小能够自适应其内部的视图内容。例如,可以使用以下代码来创建一个宽度为 WRAP_CONTENT,高度为 200dp 的悬浮窗口:

stylusCopy

  1. val params = WindowManager.LayoutParams(
  2. WindowManager.LayoutParams.WRAP_CONTENT,
  3. resources.getDimensionPixelSize(R.dimen.floating_window_height),
  4. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
  5. WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
  6. else
  7. WindowManager.LayoutParams.TYPE_PHONE,
  8. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
  9. PixelFormat.TRANSLUCENT
  10. )

在这个代码示例中,我们将悬浮窗口的宽度设置为 WindowManager.LayoutParams.WRAP_CONTENT,以便悬浮窗口的宽度能够自适应其内部的视图内容。同时,我们将悬浮窗口的高度设置为 200dp,以确保悬浮窗口在高度上有一定的大小。

需要注意的是,使用 WindowManager.LayoutParams.WRAP_CONTENT 作为悬浮窗口的宽度或高度时,需要确保悬浮窗口内部的视图内容已经正确地布局和测量完成,否则悬浮窗口可能会显示异常。另外,如果悬浮窗口的内容发生变化,需要调用 WindowManager.updateViewLayout() 方法来更新悬浮窗口的布局。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/500734
推荐阅读
相关标签
  

闽ICP备14008679号