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

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

简介:Android悬浮窗(悬浮Activity)是一种常见的需求,尤其用于显示悬浮歌词或控制面板。本源码项目提供了可拖动悬浮窗的实现方案,涉及WindowManager服务、自定义布局、触摸事件处理和权限管理等知识点。通过 FloatingActivity DraggableLayout 类,开发者可以轻松创建可拖动的悬浮窗,在音乐播放器或视频应用中实现浮动歌词或控制面板功能。 Android应用源码之悬浮Activity并可拖动(访悬浮歌词).zip

1. Android悬浮窗基础

悬浮窗是一种在其他应用程序之上显示的窗口,通常用于提供额外的功能或信息。在Android中,悬浮窗由WindowManager服务管理,该服务提供创建、管理和销毁悬浮窗的API。

悬浮窗通常用于显示通知、控制媒体播放或提供快速访问常用功能。它们可以根据需要进行定位和调整大小,并可以包含各种UI元素,如按钮、文本和图像。

2.1 WindowManager服务的介绍

WindowManager服务概述

WindowManager服务是Android系统中负责管理窗口和显示的系统服务。它提供了创建、管理和销毁窗口的API,并负责将窗口内容绘制到屏幕上。WindowManager服务由 android.view.WindowManager 类表示,它提供了以下主要功能:

  • 创建和管理窗口:WindowManager服务可以创建和管理各种类型的窗口,包括应用程序窗口、系统窗口和悬浮窗口。
  • 控制窗口属性:WindowManager服务允许应用程序设置窗口的属性,例如大小、位置、透明度和焦点。
  • 处理窗口事件:WindowManager服务负责处理窗口事件,例如触摸事件、键盘事件和焦点事件。
  • 绘制窗口内容:WindowManager服务将窗口内容绘制到屏幕上。它使用SurfaceFlinger服务来实际执行绘制操作。

WindowManager服务的架构

WindowManager服务由以下主要组件组成:

  • WindowManagerPolicy :负责管理窗口的整体策略,例如窗口的优先级、显示顺序和焦点分配。
  • WindowManagerService :负责创建、管理和销毁窗口,并处理窗口事件。
  • SurfaceFlinger :负责将窗口内容绘制到屏幕上。

获取WindowManager服务

要获取WindowManager服务,可以使用以下代码:

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

WindowManager服务的使用

WindowManager服务提供了多种API用于创建、管理和销毁窗口。常用的方法包括:

  • addView() :将一个View添加到窗口中。
  • removeView() :从窗口中移除一个View。
  • updateViewLayout() :更新窗口中View的布局。
  • setAttributes() :设置窗口的属性。
  • requestFocus() :请求窗口获取焦点。

代码示例

以下代码示例演示了如何使用WindowManager服务创建和显示一个简单的悬浮窗口:

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

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT);

View view = new TextView(this);
view.setText("悬浮窗口");

windowManager.addView(view, params);

在上面的代码中,我们首先获取WindowManager服务,然后创建了一个WindowManager.LayoutParams对象来指定窗口的属性。接下来,我们创建一个TextView并将其添加到窗口中。最后,我们将窗口添加到WindowManager服务中。

3. 悬浮窗交互与事件处理

3.1 触摸事件处理

悬浮窗的触摸事件处理与普通视图的触摸事件处理基本一致,可以通过 onTouchEvent() 方法来处理触摸事件。 onTouchEvent() 方法的签名如下:

public boolean onTouchEvent(MotionEvent event)

其中, MotionEvent 对象包含了触摸事件的详细信息,如触摸点的位置、触摸动作类型等。

onTouchEvent() 方法中,可以通过 event.getAction() 方法获取触摸动作类型,然后根据不同的触摸动作类型进行相应的处理。常见的触摸动作类型有:

  • MotionEvent.ACTION_DOWN :手指按下
  • MotionEvent.ACTION_MOVE :手指移动
  • MotionEvent.ACTION_UP :手指抬起
  • MotionEvent.ACTION_CANCEL :触摸事件被取消

例如,以下代码展示了如何处理手指按下的事件:

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        // 手指按下时,记录按下时的坐标
        mDownX = event.getRawX();
        mDownY = event.getRawY();
    }
    return super.onTouchEvent(event);
}

3.2 拖动实现

为了实现悬浮窗的拖动,需要在 onTouchEvent() 方法中处理手指移动事件。当手指移动时,可以通过计算手指移动的距离来更新悬浮窗的位置。

以下代码展示了如何实现悬浮窗的拖动:

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_MOVE) {
        // 手指移动时,计算手指移动的距离
        float moveX = event.getRawX() - mDownX;
        float moveY = event.getRawY() - mDownY;

        // 更新悬浮窗的位置
        mWindowManager.updateViewLayout(m悬浮窗View, m悬浮窗LayoutParams);
    }
    return super.onTouchEvent(event);
}

3.3 权限管理

悬浮窗需要使用 SYSTEM_ALERT_WINDOW 权限才能显示。该权限是一个危险权限,需要在清单文件中声明。

以下代码展示了如何在清单文件中声明 SYSTEM_ALERT_WINDOW 权限:

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

在运行时,还可以通过 ContextCompat.checkSelfPermission() 方法检查是否拥有 SYSTEM_ALERT_WINDOW 权限。如果未拥有该权限,可以通过 ActivityCompat.requestPermissions() 方法请求该权限。

以下代码展示了如何在运行时请求 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}, REQUEST_CODE_SYSTEM_ALERT_WINDOW);
}

4. 悬浮窗高级应用

4.1 悬浮窗显示/隐藏控制

悬浮窗的显示和隐藏控制是悬浮窗应用中常见的功能。可以通过以下步骤实现悬浮窗的显示和隐藏控制:

  1. 获取WindowManager服务 :首先需要获取WindowManager服务,可以通过以下代码获取:
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
  1. 创建悬浮窗布局 :创建悬浮窗布局,包括悬浮窗的大小、位置、背景等属性。

  2. 添加悬浮窗 :将悬浮窗布局添加到WindowManager服务中,可以通过以下代码添加悬浮窗:

WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.gravity = Gravity.CENTER;
windowManager.addView(悬浮窗布局, layoutParams);
  1. 移除悬浮窗 :当需要隐藏悬浮窗时,可以通过以下代码移除悬浮窗:
windowManager.removeView(悬浮窗布局);

4.2 用户交互设计

悬浮窗的用户交互设计至关重要,需要考虑以下因素:

  1. 悬浮窗大小和位置 :悬浮窗的大小和位置应根据实际使用场景进行设计,既要保证用户操作方便,又不能遮挡其他重要内容。

  2. 触摸事件处理 :悬浮窗需要处理触摸事件,包括点击、拖动、长按等操作。可以通过覆写悬浮窗布局的onTouchEvent()方法来处理触摸事件。

  3. 拖动实现 :悬浮窗可以通过拖动来改变位置,可以通过覆写悬浮窗布局的onTouchEvent()方法来实现拖动功能。

  4. 权限管理 :悬浮窗需要申请权限才能正常显示,可以通过以下代码申请权限:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (!Settings.canDrawOverlays(this)) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION);
    }
}

5. 悬浮窗实战应用

5.1 悬浮歌词应用实现

需求分析

悬浮歌词应用需要实现以下功能:

  • 在视频播放器上方显示歌词
  • 同步播放器进度,实时更新歌词
  • 支持歌词搜索和收藏

技术选型

  • 悬浮窗技术:WindowManager
  • 歌词解析:正则表达式
  • 播放器控制:MediaController

代码实现

public class LyricWindowService extends Service {

    private WindowManager windowManager;
    private LyricsView lyricsView;

    @Override
    public void onCreate() {
        super.onCreate();
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        lyricsView = new LyricsView(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 获取播放器信息
        MediaController mediaController = intent.getParcelableExtra("mediaController");

        // 同步播放器进度
        mediaController.addOnTimelineChangeListener(new TimelineChangeListener() {
            @Override
            public void onTimelineChange(Timeline timeline, Object reason) {
                lyricsView.updateLyrics(timeline.getCurrentPosition());
            }
        });

        // 显示悬浮窗
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.gravity = Gravity.CENTER;
        windowManager.addView(lyricsView, params);

        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        windowManager.removeView(lyricsView);
    }
}

歌词解析

public class LyricsParser {

    private static final Pattern LYRICS_PATTERN = Pattern.compile("(?<time>[0-9:.]+)\t(?<lyrics>.+)");

    public static List<Lyric> parseLyrics(String lyrics) {
        List<Lyric> lyricsList = new ArrayList<>();
        Matcher matcher = LYRICS_PATTERN.matcher(lyrics);
        while (matcher.find()) {
            String time = matcher.group("time");
            String lyricsText = matcher.group("lyrics");
            Lyric lyric = new Lyric(time, lyricsText);
            lyricsList.add(lyric);
        }
        return lyricsList;
    }
}

歌词显示

public class LyricsView extends View {

    private List<Lyric> lyricsList;
    private int currentPosition;

    public LyricsView(Context context) {
        super(context);
    }

    public void setLyrics(List<Lyric> lyricsList) {
        this.lyricsList = lyricsList;
    }

    public void updateLyrics(long position) {
        currentPosition = position;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 查找当前时间对应的歌词
        Lyric currentLyric = null;
        for (Lyric lyric : lyricsList) {
            if (lyric.getTime() <= currentPosition) {
                currentLyric = lyric;
            }
        }

        // 绘制歌词
        if (currentLyric != null) {
            Paint paint = new Paint();
            paint.setColor(Color.WHITE);
            paint.setTextSize(30);
            canvas.drawText(currentLyric.getLyrics(), 0, 30, paint);
        }
    }
}

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

简介:Android悬浮窗(悬浮Activity)是一种常见的需求,尤其用于显示悬浮歌词或控制面板。本源码项目提供了可拖动悬浮窗的实现方案,涉及WindowManager服务、自定义布局、触摸事件处理和权限管理等知识点。通过 FloatingActivity DraggableLayout 类,开发者可以轻松创建可拖动的悬浮窗,在音乐播放器或视频应用中实现浮动歌词或控制面板功能。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值