Android悬浮窗源码实战:实现可拖动悬浮歌词

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:悬浮Activity(悬浮窗)在Android开发中广泛应用,用于显示浮动歌词或控制面板。本源码项目提供了创建可拖动悬浮Activity的完整实现,涵盖了WindowManager的使用、自定义布局、触摸事件处理、权限管理和用户交互设计等关键知识点。通过实战项目,开发者可以掌握悬浮窗的实现原理,为音乐播放器、视频应用等场景的开发奠定基础。 Android应用源码之悬浮Activity并可拖动(访悬浮歌词).zip

1. Android悬浮窗简介

悬浮窗是一种在Android系统中可以悬浮在其他应用之上的窗口。它经常用于显示重要的信息、提供快捷操作或实现多任务处理。悬浮窗的使用需要获得必要的权限,并遵循特定的布局和事件处理规则。

2. WindowManager的使用

2.1 WindowManager类的介绍和使用

WindowManager类是Android系统中用于管理窗口的类。它提供了创建、管理和销毁窗口的方法,以及控制窗口属性和行为的方法。

要使用WindowManager类,首先需要获取它的实例。这可以通过调用 getSystemService(Context.WINDOW_SERVICE) 方法来实现。

WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

获取WindowManager实例后,就可以使用它来创建和管理窗口了。要创建窗口,需要调用 addView() 方法,并传入一个View对象和一个LayoutParams对象。LayoutParams对象指定了窗口的属性,例如位置、大小、透明度等。

WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
windowManager.addView(view, layoutParams);

创建窗口后,就可以使用WindowManager类来管理它了。可以通过调用 updateViewLayout() 方法来更新窗口的属性,也可以通过调用 removeView() 方法来销毁窗口。

2.2 悬浮窗类型和权限申请

悬浮窗是一种特殊的窗口类型,它可以叠加在其他应用之上。要创建悬浮窗,需要使用 TYPE_APPLICATION_OVERLAY 类型的LayoutParams对象。

WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

创建悬浮窗后,还需要申请相应的权限。悬浮窗权限需要在AndroidManifest.xml文件中声明。

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

申请权限后,需要在运行时检查权限是否被授予。如果权限未被授予,则需要向用户请求权限。

if (ContextCompat.checkSelfPermission(this, Manifest.permission.SYSTEM_ALERT_WINDOW) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW}, 1);
}

2.3 悬浮窗布局参数设置

悬浮窗的布局参数设置与普通窗口的布局参数设置类似。可以通过LayoutParams对象来设置窗口的位置、大小、透明度等属性。

WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.gravity = Gravity.CENTER;

除了这些基本属性外,悬浮窗还可以设置一些额外的属性,例如:

  • flags :可以设置窗口的各种标志,例如是否可聚焦、是否可触摸等。
  • format :可以设置窗口的像素格式,例如是否透明、是否支持硬件加速等。
  • token :可以设置窗口的令牌,用于标识窗口所属的应用。

3. 自定义悬浮窗布局设计

3.1 悬浮窗布局元素的添加和配置

在创建自定义悬浮窗布局时,需要添加和配置各种布局元素以实现所需的功能和外观。以下是一些常见的悬浮窗布局元素:

  • ImageView: 用于显示图标或图像。
  • TextView: 用于显示文本信息。
  • Button: 用于触发操作。
  • LinearLayout: 用于组织布局元素并设置其方向(水平或垂直)。
  • RelativeLayout: 用于定位布局元素相对于其他元素。

添加布局元素:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/icon"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="悬浮窗" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点击" />

</LinearLayout>

配置布局元素:

<TextView
    android:id="@+id/text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="悬浮窗"
    android:textSize="20sp"
    android:textColor="#ffffff"
    android:gravity="center" />

3.2 悬浮窗样式和主题的定制

悬浮窗的样式和主题可以根据应用的品牌和设计规范进行定制。可以通过设置以下属性来实现:

  • 背景颜色: android:background
  • 边框颜色和宽度: android:borderWidth, android:borderColor
  • 圆角: android:cornerRadius
  • 阴影: android:elevation

设置悬浮窗样式:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#ffffff"
    android:borderWidth="1dp"
    android:borderColor="#000000"
    android:cornerRadius="5dp"
    android:elevation="5dp">

    ...

</LinearLayout>

设置悬浮窗主题:

<style name="MyTheme" parent="Theme.AppCompat.Light">
    <item name="android:windowBackground">#ffffff</item>
    <item name="android:windowBorderColor">#000000</item>
    <item name="android:windowCornerRadius">5dp</item>
    <item name="android:windowElevation">5dp</item>
</style>

3.3 悬浮窗动画效果的实现

悬浮窗动画效果可以增强用户体验并吸引注意力。以下是一些常见的悬浮窗动画效果:

  • 淡入淡出: android:alpha
  • 缩放: android:scaleX, android:scaleY
  • 平移: android:translationX, android:translationY
  • 旋转: android:rotation

实现悬浮窗动画效果:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:alpha="0"
    android:scaleX="0"
    android:scaleY="0"
    android:translationX="0"
    android:translationY="0"
    android:rotation="0">

    ...

</LinearLayout>
// 淡入动画
ObjectAnimator.ofFloat(悬浮窗, "alpha", 0f, 1f).setDuration(500).start();

// 缩放动画
ObjectAnimator.ofFloat(悬浮窗, "scaleX", 0f, 1f).setDuration(500).start();
ObjectAnimator.ofFloat(悬浮窗, "scaleY", 0f, 1f).setDuration(500).start();

// 平移动画
ObjectAnimator.ofFloat(悬浮窗, "translationX", 0f, 100f).setDuration(500).start();
ObjectAnimator.ofFloat(悬浮窗, "translationY", 0f, 100f).setDuration(500).start();

// 旋转动画
ObjectAnimator.ofFloat(悬浮窗, "rotation", 0f, 360f).setDuration(500).start();

4. 触摸事件处理和拖动逻辑

4.1 悬浮窗触摸事件的监听和处理

悬浮窗作为一种与用户交互的控件,需要响应用户的触摸操作。Android 提供了强大的触摸事件处理机制,允许开发者监听和处理悬浮窗上的触摸事件。

要监听悬浮窗的触摸事件,需要在悬浮窗布局中添加一个 View ,并为该 View 注册一个 TouchListener TouchListener 是一个接口,它提供了 onTouch() 方法,用于处理触摸事件。

class MyTouchListener : View.OnTouchListener {
    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        // 处理触摸事件
        return true
    }
}

onTouch() 方法中,可以获取触摸事件的详细信息,例如触摸位置、触摸类型(按下、移动、抬起等)。根据这些信息,可以实现相应的业务逻辑,例如拖动悬浮窗、显示菜单等。

4.2 悬浮窗拖动逻辑的实现

悬浮窗拖动是用户交互中常见的功能,它允许用户将悬浮窗移动到屏幕上的任意位置。要实现悬浮窗拖动,需要监听 View 的触摸事件,并根据触摸事件的移动信息更新悬浮窗的位置。

class MyTouchListener : View.OnTouchListener {
    private var initialX: Float = 0f
    private var initialY: Float = 0f

    override fun onTouch(v: View?, event: MotionEvent?): Boolean {
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {
                // 记录按下时的坐标
                initialX = event.rawX
                initialY = event.rawY
            }
            MotionEvent.ACTION_MOVE -> {
                // 计算悬浮窗移动的距离
                val dx = event.rawX - initialX
                val dy = event.rawY - initialY

                // 更新悬浮窗的位置
                val params = v?.layoutParams as WindowManager.LayoutParams
                params.x += dx.toInt()
                params.y += dy.toInt()
                windowManager.updateViewLayout(v, params)

                // 更新初始坐标
                initialX = event.rawX
                initialY = event.rawY
            }
            MotionEvent.ACTION_UP -> {
                // 拖动结束
            }
        }
        return true
    }
}

onTouch() 方法中,记录了按下时的坐标,并在 ACTION_MOVE 事件中计算悬浮窗移动的距离,并更新悬浮窗的位置。

4.3 悬浮窗位置和大小的动态调整

除了拖动,悬浮窗的位置和大小也可以通过代码动态调整。可以通过 WindowManager.LayoutParams 对象来设置悬浮窗的位置和大小。

val params = WindowManager.LayoutParams()
params.x = 100 // 设置悬浮窗的 X 坐标
params.y = 200 // 设置悬浮窗的 Y 坐标
params.width = 300 // 设置悬浮窗的宽度
params.height = 400 // 设置悬浮窗的高度
windowManager.updateViewLayout(悬浮窗View, params)

通过更新 WindowManager.LayoutParams 对象,可以随时调整悬浮窗的位置和大小。

5. 权限管理和用户交互设计

5.1 悬浮窗权限的申请和管理

悬浮窗需要在 Android 设备上申请权限才能正常显示和运行。权限申请分为两种方式:

  • 动态申请权限: 在运行时向用户请求权限,用户可以接受或拒绝。
  • 静态申请权限: 在应用清单文件中声明权限,用户在安装应用时授予权限。

对于悬浮窗,通常采用动态申请权限的方式,因为用户可以在运行时决定是否授予权限。

动态申请权限步骤:

  1. 在 AndroidManifest.xml 文件中声明权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  1. 在代码中使用 ContextCompat.checkSelfPermission() 检查权限是否已授予:
if (ContextCompat.checkSelfPermission(context, Manifest.permission.SYSTEM_ALERT_WINDOW) != PackageManager.PERMISSION_GRANTED) {
    // 权限未授予,请求权限
    ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.SYSTEM_ALERT_WINDOW), REQUEST_CODE_PERMISSION);
}
  1. onRequestPermissionsResult() 方法中处理权限请求结果:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == REQUEST_CODE_PERMISSION) {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限已授予,显示悬浮窗
            showFloatingWindow()
        } else {
            // 权限未授予,提示用户
            Toast.makeText(this, "悬浮窗权限未授予", Toast.LENGTH_SHORT).show()
        }
    }
}

5.2 用户交互设计原则和最佳实践

悬浮窗的用户交互设计至关重要,直接影响用户体验。以下是一些原则和最佳实践:

  • 简洁明了: 悬浮窗应简洁明了,仅显示必要的信息和功能。
  • 易于操作: 悬浮窗应易于操作,触摸区域足够大,操作按钮清晰可见。
  • 位置合理: 悬浮窗的位置应合理,不遮挡重要内容或影响用户操作。
  • 可拖动: 悬浮窗应可拖动,方便用户移动到合适的位置。
  • 交互反馈: 悬浮窗应提供交互反馈,如按钮点击效果或拖动时的动画。
  • 考虑不同屏幕尺寸: 悬浮窗应适应不同屏幕尺寸,在各种设备上都能正常显示。

5.3 悬浮窗的显示、隐藏和交互控制

悬浮窗的显示、隐藏和交互控制可以通过 WindowManager 类实现。

显示悬浮窗:

val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
val layoutParams = WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT
)
windowManager.addView(floatingWindowView, layoutParams)

隐藏悬浮窗:

windowManager.removeView(floatingWindowView)

交互控制:

floatingWindowView.setOnTouchListener { view, event ->
    // 处理触摸事件
    true
}

6.1 悬浮窗Activity的创建和配置

在创建悬浮窗之前,我们需要创建一个Activity来承载悬浮窗的布局。这个Activity需要继承自 Activity 类,并实现 WindowManager.LayoutParams 接口。

public class FloatingWindowActivity extends Activity {

    private WindowManager windowManager;
    private WindowManager.LayoutParams windowParams;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 创建WindowManager对象
        windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

        // 创建悬浮窗布局参数
        windowParams = new WindowManager.LayoutParams();
        windowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        windowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        windowParams.gravity = Gravity.CENTER;
    }
}

onCreate() 方法中,我们创建了 WindowManager 对象和悬浮窗布局参数。 WindowManager.LayoutParams 对象用于配置悬浮窗的类型、权限、大小和位置。

6.2 悬浮窗布局的加载和渲染

接下来,我们需要加载悬浮窗的布局并将其添加到Activity中。

// 加载悬浮窗布局
View悬浮窗View = LayoutInflater.from(this).inflate(R.layout.悬浮窗布局, null);

// 将悬浮窗添加到WindowManager
windowManager.addView(悬浮窗View, windowParams);

在上面的代码中,我们使用 LayoutInflater 加载了悬浮窗布局,然后将其添加到 WindowManager 中。 windowParams 对象用于指定悬浮窗的位置和大小。

6.3 触摸事件的处理和拖动实现

为了使悬浮窗可以拖动,我们需要处理触摸事件。

// 设置悬浮窗触摸监听器
悬浮窗View.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // 处理拖动逻辑
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录按下时的位置
                startX = event.getRawX();
                startY = event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算移动距离
                float dx = event.getRawX() - startX;
                float dy = event.getRawY() - startY;

                // 更新悬浮窗位置
                windowParams.x += dx;
                windowParams.y += dy;
                windowManager.updateViewLayout(悬浮窗View, windowParams);

                // 更新起始位置
                startX = event.getRawX();
                startY = event.getRawY();
                break;
            case MotionEvent.ACTION_UP:
                // 拖动结束
                break;
        }
        return true;
    }
});

在上面的代码中,我们设置了悬浮窗的触摸监听器。当用户按下悬浮窗时,我们会记录按下时的位置。当用户移动悬浮窗时,我们会计算移动距离并更新悬浮窗的位置。当用户松开手指时,拖动结束。

6.4 悬浮窗权限的申请和管理

在Android中,悬浮窗需要申请权限才能显示。

// 检查悬浮窗权限
if (!Settings.canDrawOverlays(this)) {
    // 申请悬浮窗权限
    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
    startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION);
}

在上面的代码中,我们首先检查悬浮窗权限是否已经获得。如果没有获得,我们会弹出权限申请对话框。

6.5 悬浮窗显示、隐藏和交互控制

最后,我们需要实现悬浮窗的显示、隐藏和交互控制。

// 显示悬浮窗
windowManager.addView(悬浮窗View, windowParams);

// 隐藏悬浮窗
windowManager.removeView(悬浮窗View);

// 设置悬浮窗交互控制
悬浮窗View.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // 处理悬浮窗交互逻辑
    }
});

在上面的代码中,我们通过 WindowManager 对象来显示和隐藏悬浮窗。我们还设置了悬浮窗的点击监听器,以便处理悬浮窗的交互逻辑。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:悬浮Activity(悬浮窗)在Android开发中广泛应用,用于显示浮动歌词或控制面板。本源码项目提供了创建可拖动悬浮Activity的完整实现,涵盖了WindowManager的使用、自定义布局、触摸事件处理、权限管理和用户交互设计等关键知识点。通过实战项目,开发者可以掌握悬浮窗的实现原理,为音乐播放器、视频应用等场景的开发奠定基础。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值