47、Android视频播放与画中画模式全解析

Android视频播放与画中画模式全解析

1. Android视频播放基础

1.1 Android视频播放类介绍

在Android应用开发中, VideoView MediaController 这两个类为实现视频播放提供了便捷的途径。
- VideoView类 :是在Android应用中显示视频的最简单方式。它作为一个可视化组件,添加到活动布局后,能提供播放视频的界面。目前,Android支持多种视频格式,如H.263、H.264 AVC、H.265 HEVC、MPEG - 4 SP、VP8和VP9。其常用方法如下:
| 方法 | 描述 |
| ---- | ---- |
| setVideoPath(String path) | 指定要播放的视频媒体路径(字符串形式),可以是远程视频文件URL或设备本地视频文件 |
| setVideoUri(Uri uri) | 与 setVideoPath() 方法功能相同,但接受 Uri 对象作为参数 |
| start() | 开始视频播放 |
| stopPlayback() | 停止视频播放 |
| pause() | 暂停视频播放 |
| isPlaying() | 返回布尔值,指示视频是否正在播放 |
| setOnPreparedListener(MediaPlayer.OnPreparedListener) | 当视频准备好播放时,调用回调方法 |
| setOnErrorListener(MediaPlayer.OnErrorListener) | 视频播放过程中出现错误时,调用回调方法 |
| setOnCompletionListener(MediaPlayer.OnCompletionListener) | 视频播放结束时,调用回调方法 |
| getDuration() | 返回视频的时长,通常在 OnPreparedListener() 回调方法中调用才会返回正确值,否则返回 -1 |
| getCurrentPosition() | 返回一个整数值,表示当前播放位置 |
| setMediaController(MediaController) | 指定一个 MediaController 实例,允许向用户显示播放控件 |

  • MediaController类 :如果仅使用 VideoView 类播放视频,用户将无法控制播放,视频会一直播放到结束。而将 MediaController 类的实例附加到 VideoView 实例上,就能解决这个问题。它提供了一组控件,让用户可以管理播放(如暂停、在视频时间轴上前后快进)。该类的关键方法如下:
    | 方法 | 描述 |
    | ---- | ---- |
    | setAnchorView(View view) | 指定控制器要锚定的视图,从而确定控件在屏幕上的位置 |
    | show() | 显示控件 |
    | show(int timeout) | 控件显示指定的持续时间(以毫秒为单位) |
    | hide() | 对用户隐藏控制器 |
    | isShowing() | 返回布尔值,指示控件当前是否对用户可见 |

1.2 创建视频播放示例

下面我们将创建一个使用 VideoView MediaController 类播放MPEG - 4视频文件的示例应用。

1.2.1 创建项目
  • 从欢迎屏幕中选择“New Project”选项。
  • 在新的项目对话框中,选择“Empty Views Activity”模板,然后点击“Next”按钮。
  • 在“Name”字段中输入“VideoPlayer”,并指定“com.ebookfrenzy.videoplayer”作为包名。
  • 在点击“Finish”按钮之前,将“Minimum API level”设置为“API 33: Android 13 (Tiramisu)”,并将“Language”菜单设置为“Java”。
  • 按照相关步骤启用项目的视图绑定。
1.2.2 设计布局
  • 使用项目工具窗口定位到 app -> res -> layout -> activity_main.xml 文件,双击打开。
  • 将布局编辑器工具切换到“Design”模式,删除默认的 TextView 小部件。
  • 从“Palette”面板的“Widgets”类别中,拖放一个 VideoView 实例到布局中,填充可用的画布区域。
  • 使用“Attributes”面板,将 layout_width layout_height 属性分别更改为 match_constraint wrap_content
  • 移除 VideoView 底部与父 ConstraintLayout 底部的约束。
  • 将组件的ID更改为 videoView1
1.2.3 下载视频文件
  • 使用网络浏览器,导航到 https://www.ebookfrenzy.com/android_book/demo.mp4 播放视频。
  • 在浏览器窗口中,右键单击视频播放区域,选择保存或下载视频到本地文件,并选择合适的临时文件系统位置,将文件命名为 demo.mp4
  • 在Android Studio中,使用项目工具窗口定位到 res 文件夹,右键单击它,选择“New -> Directory”菜单选项,输入 raw 作为目录名,然后按回车键。
  • 使用操作系统的文件系统导航器,找到上面下载的 demo.mp4 文件并复制。
  • 返回Android Studio,右键单击新创建的 raw 目录,选择“Paste”选项,将视频文件复制到项目中。
1.2.4 配置VideoView

以下是配置 VideoView 并开始播放视频的代码:

package com.ebookfrenzy.videoplayer;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.net.Uri;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        View view = binding.getRoot();
        setContentView(view);
        configureVideoView();
    }

    private void configureVideoView() {
        binding.videoView1.setVideoURI(Uri.parse("android.resource://"
                + getPackageName() + "/" + R.raw.demo));
        binding.videoView1.start();
    }
}

1.3 添加MediaController

当前的“VideoPlayer”应用中,用户无法控制视频播放。我们可以通过修改 configureVideoView() 方法来添加 MediaController

package com.ebookfrenzy.videoplayer;

import android.widget.MediaController;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;
    private MediaController mediaController;

    private void configureVideoView() {
        binding.videoView1.setVideoURI(Uri.parse("android.resource://"
                + getPackageName() + "/" + R.raw.demo));
        mediaController = new MediaController(this);
        mediaController.setAnchorView(binding.videoView1);
        binding.videoView1.setMediaController(mediaController);
        binding.videoView1.start();
    }
}

1.4 设置onPreparedListener

为了展示视频媒体的更多功能,我们实现一个监听器,用于在Android Studio的Logcat面板中输出视频时长,并配置视频循环播放:

package com.ebookfrenzy.videoplayer;

import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;
    private MediaController mediaController;
    String TAG = "VideoPlayer";

    private void configureVideoView() {
        binding.videoView1.setVideoURI(Uri.parse("android.resource://"
                + getPackageName() + "/" + R.raw.demo));
        MediaController mediaController = new MediaController(this);
        mediaController.setAnchorView(binding.videoView1);
        binding.videoView1.setMediaController(mediaController);
        binding.videoView1.setOnPreparedListener(mp -> {
            mp.setLooping(true);
            Log.i(TAG, "Duration = " + binding.videoView1.getDuration());
        });
        binding.videoView1.start();
    }
}

2. Android画中画模式

2.1 画中画模式概述

画中画(PiP)模式主要用于视频播放,允许将活动屏幕缩小并定位在屏幕的任何位置。在这种状态下,活动继续运行,窗口保持可见,使用户可以在执行其他任务(如查看电子邮件或处理电子表格)的同时继续观看视频播放。

2.2 画中画模式功能

  • 模式切换 :通过运行应用内的API调用将活动置于PiP模式。
  • 配置选项 :可以指定配置选项,控制PiP窗口的宽高比,并定义要包含的活动屏幕区域。
  • 窗口交互 :用户点击PiP窗口时,窗口会变大,中心有全屏操作按钮,点击可将窗口恢复到全屏模式;右上角有退出按钮,点击可关闭窗口并将应用置于后台。自定义操作按钮也会显示在屏幕上。

2.3 启用画中画模式

PiP模式目前仅在运行API 26(Android 8.0 Oreo)或更高版本的设备上受支持。要在项目的清单文件中启用PiP模式,需要为每个需要PiP支持的活动元素添加以下代码:

<activity 
    android:name=".MainActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

其中, android:supportsPictureInPicture 条目为活动启用PiP模式, android:configChanges 属性通知Android活动可以处理布局配置更改。

2.4 配置画中画参数

PiP行为通过 PictureInPictureParams 类定义,可以使用 Builder 类创建其实例:

PictureInPictureParams params = new PictureInPictureParams.Builder().build();

可以使用以下可选方法调用自定义参数:
- setActions() :定义在活动处于PiP模式时,PiP窗口内可以执行的操作。
- setAspectRatio() :声明PiP窗口外观的首选宽高比,接受一个包含宽高比的 Rational 对象作为参数。
- setSourceRectHint() :接受一个 Rect 对象,定义要在PiP窗口中显示的活动屏幕区域。

以下代码示例展示了如何配置宽高比和操作参数:

Rational rational = new Rational(videoView.getWidth(), videoView.getHeight());
PictureInPictureParams params = new PictureInPictureParams.Builder()
       .setAspectRatio(rational)
       .setActions(actions)
       .build();
setPictureInPictureParams(params);

2.5 进入画中画模式

通过调用 enterPictureInPictureMode() 方法并传递 PictureInPictureParams 对象,将活动置于PiP模式:

enterPictureInPictureMode(params);

如果不需要参数,可以创建一个默认的 PictureInPictureParams 对象。如果之前使用 setPictureInPictureParams() 方法设置了参数,这些参数将与 enterPictureInPictureMode() 方法调用时指定的参数合并。

2.6 检测画中画模式变化

通过重写 onPictureInPictureModeChanged() 方法,可以处理活动在PiP模式和全屏模式之间转换时的任务:

@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode);
    if (isInPictureInPictureMode) {
        // 活动进入画中画模式
    } else {
        // 活动进入全屏模式
    }
}

2.7 添加画中画操作

PiP操作以图标形式出现在用户点击PiP窗口时。实现PiP操作是一个多步骤过程:
- 设置广播接收器,用于接收PiP窗口通知活动某个操作已被选择的信息。
- 在PiP操作中创建待定意图,配置为广播广播接收器正在监听的意图。
- 当意图触发广播接收器时,使用意图中存储的数据识别执行的操作,并在活动中采取必要的操作。

以下代码片段展示了如何创建 Intent PendingIntent RemoteAction 对象,以及如何应用PiP设置:

final ArrayList<RemoteAction> actions = new ArrayList<>();
Intent actionIntent = new Intent("MY_PIP_ACTION");
final PendingIntent pendingIntent = PendingIntent.getBroadcast(MyActivity.this,
        REQUEST_CODE, actionIntent, FLAG_IMMUTABLE);
final Icon icon = Icon.createWithResource(MyActivity.this, R.drawable.action_icon);
RemoteAction remoteAction = new RemoteAction(icon, "My Action Title",
        "My Action Description", pendingIntent);
actions.add(remoteAction);
PictureInPictureParams params = new PictureInPictureParams.Builder()
       .setActions(actions)
       .build();
setPictureInPictureParams(params);

2.8 画中画模式在项目中的应用示例

2.8.1 添加画中画支持到清单文件

AndroidManifest.xml 文件中,为 MainActivity 启用PiP支持:

<activity 
    android:name=".MainActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
2.8.2 添加画中画按钮
  • 加载 activity_main.xml 文件到布局编辑器。
  • 从调色板中拖放一个 Button 对象到布局中,使其位置符合要求。
  • 将按钮上的文本更改为“Enter PiP Mode”,并将字符串提取到名为 enter_pip_mode 的资源中。
  • 将按钮的ID更改为 pipButton ,并配置 onClick 属性以调用名为 enterPipMode 的方法。

通过以上步骤,我们可以在Android应用中实现视频播放和画中画模式的功能。这种方式使得用户在使用应用时能够更加灵活地观看视频,提高了用户体验。

3. 总结与展望

3.1 技术总结

在本次的技术探索中,我们深入研究了 Android 平台上的视频播放与画中画模式的实现。以下是对关键技术点的总结:

技术点 描述
VideoView 类 用于在 Android 应用中显示视频,支持多种视频格式,提供了丰富的方法来控制视频播放,如开始、暂停、停止等。
MediaController 类 为 VideoView 提供播放控制功能,用户可以通过它来管理视频的播放进度、暂停和继续等操作。
画中画模式 允许用户在执行其他任务的同时继续观看视频,通过配置清单文件、设置参数和处理模式变化等步骤实现。

3.2 代码回顾

我们通过一系列代码示例展示了如何实现视频播放和画中画模式:
- 视频播放示例

package com.ebookfrenzy.videoplayer;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.net.Uri;
import android.view.View;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        View view = binding.getRoot();
        setContentView(view);
        configureVideoView();
    }

    private void configureVideoView() {
        binding.videoView1.setVideoURI(Uri.parse("android.resource://"
                + getPackageName() + "/" + R.raw.demo));
        binding.videoView1.start();
    }
}
  • 添加 MediaController
package com.ebookfrenzy.videoplayer;

import android.widget.MediaController;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;
    private MediaController mediaController;

    private void configureVideoView() {
        binding.videoView1.setVideoURI(Uri.parse("android.resource://"
                + getPackageName() + "/" + R.raw.demo));
        mediaController = new MediaController(this);
        mediaController.setAnchorView(binding.videoView1);
        binding.videoView1.setMediaController(mediaController);
        binding.videoView1.start();
    }
}
  • 设置 onPreparedListener
package com.ebookfrenzy.videoplayer;

import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;
    private MediaController mediaController;
    String TAG = "VideoPlayer";

    private void configureVideoView() {
        binding.videoView1.setVideoURI(Uri.parse("android.resource://"
                + getPackageName() + "/" + R.raw.demo));
        MediaController mediaController = new MediaController(this);
        mediaController.setAnchorView(binding.videoView1);
        binding.videoView1.setMediaController(mediaController);
        binding.videoView1.setOnPreparedListener(mp -> {
            mp.setLooping(true);
            Log.i(TAG, "Duration = " + binding.videoView1.getDuration());
        });
        binding.videoView1.start();
    }
}
  • 画中画模式相关代码
    • 启用画中画模式的清单文件配置:
<activity 
    android:name=".MainActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
- 配置画中画参数:
Rational rational = new Rational(videoView.getWidth(), videoView.getHeight());
PictureInPictureParams params = new PictureInPictureParams.Builder()
       .setAspectRatio(rational)
       .setActions(actions)
       .build();
setPictureInPictureParams(params);
- 进入画中画模式:
enterPictureInPictureMode(params);
- 检测画中画模式变化:
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
    super.onPictureInPictureModeChanged(isInPictureInPictureMode);
    if (isInPictureInPictureMode) {
        // 活动进入画中画模式
    } else {
        // 活动进入全屏模式
    }
}
- 添加画中画操作:
final ArrayList<RemoteAction> actions = new ArrayList<>();
Intent actionIntent = new Intent("MY_PIP_ACTION");
final PendingIntent pendingIntent = PendingIntent.getBroadcast(MyActivity.this,
        REQUEST_CODE, actionIntent, FLAG_IMMUTABLE);
final Icon icon = Icon.createWithResource(MyActivity.this, R.drawable.action_icon);
RemoteAction remoteAction = new RemoteAction(icon, "My Action Title",
        "My Action Description", pendingIntent);
actions.add(remoteAction);
PictureInPictureParams params = new PictureInPictureParams.Builder()
       .setActions(actions)
       .build();
setPictureInPictureParams(params);

3.3 未来展望

随着 Android 系统的不断发展,视频播放和画中画模式可能会迎来更多的优化和新功能。例如:
- 更高的视频质量支持 :随着网络和硬件的不断提升,未来可能会支持更高分辨率、更高帧率的视频播放。
- 更智能的画中画模式 :画中画模式可能会更加智能,例如根据用户的使用习惯自动调整窗口大小和位置,或者与其他应用进行更深度的交互。
- 增强的用户交互体验 :可能会引入更多的交互方式,如手势控制、语音控制等,让用户更加便捷地操作视频播放和画中画模式。

3.4 流程图总结

下面是一个 mermaid 格式的流程图,展示了实现视频播放和画中画模式的主要步骤:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B(创建项目):::process
    B --> C(设计布局):::process
    C --> D(下载视频文件):::process
    D --> E(配置 VideoView):::process
    E --> F(添加 MediaController):::process
    F --> G(设置 onPreparedListener):::process
    G --> H(启用画中画模式):::process
    H --> I(配置画中画参数):::process
    I --> J(进入画中画模式):::process
    J --> K(检测画中画模式变化):::process
    K --> L(添加画中画操作):::process
    L --> M([结束]):::startend

通过这个流程图,我们可以清晰地看到实现视频播放和画中画模式的整个流程,从项目创建到最终功能的实现,每个步骤都紧密相连。

3.5 实际应用建议

在实际开发中,我们可以根据具体的需求对这些技术进行灵活运用。例如:
- 视频类应用 :对于专门的视频播放应用,可以充分利用 VideoView 和 MediaController 提供的功能,为用户提供流畅的视频播放体验。同时,画中画模式可以让用户在切换到其他应用时继续观看视频,提高用户的使用效率。
- 多媒体应用 :在多媒体应用中,视频播放可能只是其中的一部分功能。可以将视频播放与其他功能(如音频播放、图片展示等)结合起来,为用户提供更加丰富的多媒体体验。
- 教育类应用 :在教育类应用中,视频教学是一种常见的方式。通过实现画中画模式,学生可以在学习视频的同时查看相关的资料或进行笔记记录,提高学习效果。

总之,Android 平台上的视频播放和画中画模式为开发者提供了强大的工具,能够帮助我们开发出更加优秀的应用,满足用户多样化的需求。通过不断地学习和实践,我们可以更好地掌握这些技术,为用户带来更好的体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值