赞
踩
请实现Android里的悬浮窗,这个悬浮窗里有2个TextView
实现 Android 悬浮窗需要涉及到以下几个步骤:
创建一个悬浮窗口的布局,包含两个 TextView。
创建一个 Service,并在 Service 的 onCreate() 方法中创建悬浮窗口。
在悬浮窗口的布局中添加两个 TextView。
在 Service 中实现悬浮窗口的拖动和关闭功能。
在 AndroidManifest.xml 文件中声明 Service。
下面是一个简单的示例代码:
csharpCopy
public class FloatingWindowService extends Service { private WindowManager windowManager; private View floatingView; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); // 创建悬浮窗口的布局 floatingView = LayoutInflater.from(this).inflate(R.layout.floating_window, null); // 获取 WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); // 设置悬浮窗口的参数 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.addView(floatingView, params); // 添加拖动和关闭功能 floatingView.setOnTouchListener(new FloatingOnTouchListener(params)); floatingView.findViewById(R.id.close_button).setOnClickListener(new FloatingOnClickListener()); } @Override public void onDestroy() { super.onDestroy(); // 移除悬浮窗口的布局 if (floatingView != null) { windowManager.removeView(floatingView); } } // 实现拖动功能 private class FloatingOnTouchListener implements View.OnTouchListener { private int initialX; private int initialY; private float initialTouchX; private float initialTouchY; private WindowManager.LayoutParams params; FloatingOnTouchListener(WindowManager.LayoutParams params) { this.params = params; } @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_UP: // 拖动结束,更新悬浮窗口的位置 int newX = initialX + (int) (event.getRawX() - initialTouchX); int newY = initialY + (int) (event.getRawY() - initialTouchY); params.x = newX; params.y = newY; windowManager.updateViewLayout(floatingView, params); return true; case MotionEvent.ACTION_MOVE: // 拖动中,更新悬浮窗口的位置 int newX1 = initialX + (int) (event.getRawX() - initialTouchX); int newY1 = initialY + (int) (event.getRawY() - initialTouchY); params.x = newX1; params.y = newY1; windowManager.updateViewLayout(floatingView, params); return true; } return false; } } // 实现关闭功能 private class FloatingOnClickListener implements View.OnClickListener { @Override public void onClick(View v) { stopSelf(); } } }
在这个代码中,我们创建了一个 FloatingWindowService 类,并在 onCreate() 方法中创建了一个悬浮窗口的布局。我们还在 Service 中实现了悬浮窗口的拖动和关闭功能。
悬浮窗口的布局文件(floating_window.xml)可以包含两个 TextView,如下所示:
iniCopy
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/floating_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/white"> <TextView android:id="@+id/text_view1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView 1" /> <TextView android:id="@+id/text_view2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView 2" android:layout_below="@id/text_view1" /> <Button android:id="@+id/close_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Close" android:layout_alignParentEnd="true" /> </RelativeLayout>
最后,在 AndroidManifest.xml 文件中声明 Service:
Copy
<service android:name=".FloatingWindowService" />
这样,我们就完成了 Android 悬浮窗的实现。
很好,例子里的java代码请修改为kotlin
好的,下面是 Kotlin 版本的代码实现:
kotlinCopy
class FloatingWindowService : Service() { private lateinit var windowManager: WindowManager private lateinit var floatingView: View override fun onBind(intent: Intent?): IBinder? { return null } override fun onCreate() { super.onCreate() // 创建悬浮窗口的布局 floatingView = LayoutInflater.from(this).inflate(R.layout.floating_window, null) // 获取 WindowManager windowManager = getSystemService(WINDOW_SERVICE) as WindowManager // 设置悬浮窗口的参数 val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY else WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ) // 设置悬浮窗口的位置和大小 params.gravity = Gravity.TOP or Gravity.START params.x = 0 params.y = 100 // 添加悬浮窗口的布局 windowManager.addView(floatingView, params) // 添加拖动和关闭功能 floatingView.setOnTouchListener(FloatingOnTouchListener(params)) floatingView.findViewById<View>(R.id.close_button).setOnClickListener(FloatingOnClickListener()) } override fun onDestroy() { super.onDestroy() // 移除悬浮窗口的布局 if (::floatingView.isInitialized) { windowManager.removeView(floatingView) } } // 实现拖动功能 private inner class FloatingOnTouchListener(private val params: WindowManager.LayoutParams) : View.OnTouchListener { private var initialX: Int = 0 private var initialY: Int = 0 private var initialTouchX: Float = 0f private var initialTouchY: Float = 0f override fun onTouch(v: View?, event: MotionEvent?): Boolean { when (event?.action) { MotionEvent.ACTION_DOWN -> { // 记录触摸点的位置和悬浮窗口的位置 initialX = params.x initialY = params.y initialTouchX = event.rawX initialTouchY = event.rawY return true } MotionEvent.ACTION_UP -> { // 拖动结束,更新悬浮窗口的位置 val newX = initialX + (event.rawX - initialTouchX).toInt() val newY = initialY + (event.rawY - initialTouchY).toInt() params.x = newX params.y = newY windowManager.updateViewLayout(floatingView, params) return true } MotionEvent.ACTION_MOVE -> { // 拖动中,更新悬浮窗口的位置 val newX = initialX + (event.rawX - initialTouchX).toInt() val newY = initialY + (event.rawY - initialTouchY).toInt() params.x = newX params.y = newY windowManager.updateViewLayout(floatingView, params) return true } } return false } } // 实现关闭功能 private inner class FloatingOnClickListener : View.OnClickListener { override fun onClick(v: View?) { stopSelf() } } }
在这个 Kotlin 版本的代码中,我们使用了 inner class 来实现了 FloatingOnTouchListener 和 FloatingOnClickListener 两个内部类,以及使用了 lateinit 修饰符来延迟初始化悬浮窗口布局。其它部分与 Java 版本的代码类似。
怎么设置悬浮窗的大小
要设置悬浮窗口的大小,可以在 WindowManager.LayoutParams 中设置其宽度和高度。具体来说,可以通过设置以下两个参数来控制悬浮窗口的大小:
width:悬浮窗口的宽度。
height:悬浮窗口的高度。
例如,我们可以将悬浮窗口的宽度设置为屏幕宽度的一半,高度设置为 200dp,代码如下:
stylusCopy
- // 获取屏幕宽度
- val displayMetrics = DisplayMetrics()
- windowManager.defaultDisplay.getMetrics(displayMetrics)
- val screenWidth = displayMetrics.widthPixels
- // 设置悬浮窗口的参数
- val params = WindowManager.LayoutParams(
- screenWidth / 2, // 悬浮窗口的宽度为屏幕宽度的一半
- resources.getDimensionPixelSize(R.dimen.floating_window_height), // 悬浮窗口的高度为 200dp
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
- else
- WindowManager.LayoutParams.TYPE_PHONE,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT
- )
在这个示例代码中,我们首先获取了屏幕宽度,然后将悬浮窗口的宽度设置为屏幕宽度的一半,高度设置为 200dp,使用了 resources.getDimensionPixelSize() 方法来获得 200dp 对应的像素值。
需要注意的是,悬浮窗口的大小不应该超过屏幕的可见范围,否则可能会导致悬浮窗口无法完全显示。另外,如果悬浮窗口的大小发生变化,需要调用 WindowManager.updateViewLayout() 方法来更新悬浮窗口的布局。
Copy
- // 更新悬浮窗口的布局
- 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
- val params = WindowManager.LayoutParams(
- WindowManager.LayoutParams.WRAP_CONTENT,
- resources.getDimensionPixelSize(R.dimen.floating_window_height),
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
- else
- WindowManager.LayoutParams.TYPE_PHONE,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT
- )
在这个代码示例中,我们将悬浮窗口的宽度设置为 WindowManager.LayoutParams.WRAP_CONTENT,以便悬浮窗口的宽度能够自适应其内部的视图内容。同时,我们将悬浮窗口的高度设置为 200dp,以确保悬浮窗口在高度上有一定的大小。
需要注意的是,使用 WindowManager.LayoutParams.WRAP_CONTENT 作为悬浮窗口的宽度或高度时,需要确保悬浮窗口内部的视图内容已经正确地布局和测量完成,否则悬浮窗口可能会显示异常。另外,如果悬浮窗口的内容发生变化,需要调用 WindowManager.updateViewLayout() 方法来更新悬浮窗口的布局。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。