鸿蒙DevEco Studio实现腾讯视频播放功能(HarmonyOS 5)
【代码】鸿蒙DevEco Studio实现腾讯视频播放功能(HarmonyOS 5)
·
下面是一个使用DevEco Studio和HarmonyOS 5开发视频播放功能的完整实现方案,模拟腾讯视频的核心播放功能:
1. 项目准备
- 在DevEco Studio中创建新项目,选择"Empty Ability"模板
- 确保SDK版本选择HarmonyOS 5.0或更高
- 在config.json中添加必要的权限:
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.WRITE_MEDIA"
},
{
"name": "ohos.permission.READ_MEDIA"
}
]
}
}
2. 核心功能实现
2.1 主页面布局 (resources/base/layout/ability_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:width="match_parent"
ohos:height="match_parent"
ohos:orientation="vertical"
ohos:background_element="#000000">
<!-- 视频播放器容器 -->
<ohos.agp.components.Component
ohos:id="$+id:video_container"
ohos:width="match_parent"
ohos:height="300vp"
ohos:background_element="#000000"/>
<!-- 视频标题 -->
<Text
ohos:id="$+id:video_title"
ohos:width="match_parent"
ohos:height="40vp"
ohos:text="视频标题"
ohos:text_size="20fp"
ohos:text_color="#FFFFFF"
ohos:margin="10vp"/>
<!-- 播放控制区域 -->
<DirectionalLayout
ohos:width="match_parent"
ohos:height="60vp"
ohos:orientation="horizontal"
ohos:margin="10vp">
<Button
ohos:id="$+id:play_pause_btn"
ohos:width="50vp"
ohos:height="50vp"
ohos:background_element="#4CAF50"
ohos:text="播放"
ohos:text_color="#FFFFFF"/>
<Text
ohos:id="$+id:current_time"
ohos:width="80vp"
ohos:height="match_parent"
ohos:text="00:00"
ohos:text_size="16fp"
ohos:text_color="#FFFFFF"
ohos:margin_left="10vp"/>
<Slider
ohos:id="$+id:progress_slider"
ohos:width="0vp"
ohos:height="match_parent"
ohos:weight="1"
ohos:min_value="0"
ohos:max_value="100"
ohos:progress_value="0"
ohos:margin_left="10vp"
ohos:margin_right="10vp"/>
<Text
ohos:id="$+id:total_time"
ohos:width="80vp"
ohos:height="match_parent"
ohos:text="00:00"
ohos:text_size="16fp"
ohos:text_color="#FFFFFF"/>
</DirectionalLayout>
<!-- 清晰度选择 -->
<DirectionalLayout
ohos:width="match_parent"
ohos:height="50vp"
ohos:orientation="horizontal"
ohos:margin="10vp">
<Text
ohos:width="100vp"
ohos:height="match_parent"
ohos:text="清晰度:"
ohos:text_size="16fp"
ohos:text_color="#FFFFFF"/>
<RadioContainer
ohos:id="$+id:quality_radio"
ohos:width="0vp"
ohos:height="match_parent"
ohos:weight="1"
ohos:orientation="horizontal">
<RadioButton
ohos:width="0vp"
ohos:height="match_parent"
ohos:weight="1"
ohos:text="标清"
ohos:checked="true"/>
<RadioButton
ohos:width="0vp"
ohos:height="match_parent"
ohos:weight="1"
ohos:text="高清"
ohos:margin_left="10vp"/>
<RadioButton
ohos:width="0vp"
ohos:height="match_parent"
ohos:weight="1"
ohos:text="超清"
ohos:margin_left="10vp"/>
</RadioContainer>
</DirectionalLayout>
<!-- 视频推荐列表 -->
<ListContainer
ohos:id="$+id:recommend_list"
ohos:width="match_parent"
ohos:height="0vp"
ohos:weight="1"
ohos:margin="10vp"/>
</DirectionalLayout>
2.2 主逻辑代码 (MainAbilitySlice.java)
package com.example.videoplayer.slice;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.utils.LayoutAlignment;
import ohos.app.Context;
import ohos.media.player.Player;
import ohos.media.common.Source;
import ohos.media.common.StorageProperty;
import ohos.multimodalinput.event.TouchEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class MainAbilitySlice extends AbilitySlice {
private Component videoContainer;
private Text videoTitle;
private Button playPauseBtn;
private Text currentTime;
private Slider progressSlider;
private Text totalTime;
private RadioContainer qualityRadio;
private ListContainer recommendList;
private Player player;
private Timer progressTimer;
private boolean isPlaying = false;
private List<Map<String, String>> videoList = new ArrayList<>();
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
// 初始化组件
initComponents();
// 初始化播放器
initPlayer();
// 初始化推荐列表
initRecommendList();
}
private void initComponents() {
videoContainer = findComponentById(ResourceTable.Id_video_container);
videoTitle = (Text) findComponentById(ResourceTable.Id_video_title);
playPauseBtn = (Button) findComponentById(ResourceTable.Id_play_pause_btn);
currentTime = (Text) findComponentById(ResourceTable.Id_current_time);
progressSlider = (Slider) findComponentById(ResourceTable.Id_progress_slider);
totalTime = (Text) findComponentById(ResourceTable.Id_total_time);
qualityRadio = (RadioContainer) findComponentById(ResourceTable.Id_quality_radio);
recommendList = (ListContainer) findComponentById(ResourceTable.Id_recommend_list);
// 播放/暂停按钮点击事件
playPauseBtn.setClickedListener(component -> {
if (isPlaying) {
pauseVideo();
} else {
playVideo();
}
});
// 进度条滑动事件
progressSlider.setTouchEventListener(new Component.TouchEventListener() {
@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP) {
if (player != null && player.isNowPlaying()) {
int progress = progressSlider.getProgress();
long duration = player.getDuration();
long seekPosition = (long) (duration * (progress / 100.0));
player.rewindTo(seekPosition);
}
}
return true;
}
});
// 清晰度选择事件
qualityRadio.setMarkChangedListener((radioContainer, index, mark) -> {
changeVideoQuality(index);
});
}
private void initPlayer() {
// 创建播放器实例
player = new Player(getContext());
// 设置播放器回调
player.setPlayerCallback(new Player.IPlayerCallback() {
@Override
public void onPrepared() {
getUITaskDispatcher().asyncDispatch(() -> {
videoTitle.setText("测试视频 - 鸿蒙视频播放器演示");
totalTime.setText(formatTime(player.getDuration()));
playVideo();
});
}
@Override
public void onMessage(int i, int i1) {}
@Override
public void onError(int i, int i1) {
getUITaskDispatcher().asyncDispatch(() -> {
playPauseBtn.setText("播放");
isPlaying = false;
new ToastDialog(getContext())
.setText("播放出错: " + i1)
.setAlignment(LayoutAlignment.CENTER)
.show();
});
}
@Override
public void onResolutionChanged(int i, int i1) {}
@Override
public void onPlayBackComplete() {
getUITaskDispatcher().asyncDispatch(() -> {
playPauseBtn.setText("播放");
isPlaying = false;
progressSlider.setProgress(0);
currentTime.setText("00:00");
});
}
@Override
public void onRewindToComplete() {}
@Override
public void onBufferingChange(int i) {}
@Override
public void onNewTimedMetaData(Player.MediaTimedMetaData mediaTimedMetaData) {}
@Override
public void onMediaTimeIncontinuity(Player.MediaTimeInfo mediaTimeInfo) {}
});
// 设置视频源
setVideoSource("https://media.w3.org/2010/05/sintel/trailer.mp4");
}
private void setVideoSource(String url) {
if (player == null) return;
// 停止当前播放
if (player.isNowPlaying()) {
player.stop();
}
// 重置UI状态
getUITaskDispatcher().asyncDispatch(() -> {
playPauseBtn.setText("播放");
isPlaying = false;
progressSlider.setProgress(0);
currentTime.setText("00:00");
totalTime.setText("00:00");
});
// 设置新的视频源
Source source = new Source(url);
StorageProperty storageProperty = new StorageProperty();
storageProperty.setBufferSize(1024 * 1024); // 1MB缓冲区
player.setSource(source, storageProperty);
player.setDisplaySurface(videoContainer.getSurface());
player.prepare();
}
private void playVideo() {
if (player != null) {
player.play();
isPlaying = true;
playPauseBtn.setText("暂停");
startProgressTimer();
}
}
private void pauseVideo() {
if (player != null) {
player.pause();
isPlaying = false;
playPauseBtn.setText("播放");
stopProgressTimer();
}
}
private void startProgressTimer() {
stopProgressTimer();
progressTimer = new Timer();
progressTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (player != null && player.isNowPlaying()) {
long currentPosition = player.getCurrentTime();
long duration = player.getDuration();
getUITaskDispatcher().asyncDispatch(() -> {
currentTime.setText(formatTime(currentPosition));
if (duration > 0) {
int progress = (int) ((currentPosition * 100) / duration);
progressSlider.setProgress(progress);
}
});
}
}
}, 0, 1000);
}
private void stopProgressTimer() {
if (progressTimer != null) {
progressTimer.cancel();
progressTimer = null;
}
}
private void changeVideoQuality(int qualityIndex) {
String[] qualityUrls = {
"https://media.w3.org/2010/05/sintel/trailer.mp4", // 标清
"https://media.w3.org/2010/05/sintel/trailer.mp4", // 高清
"https://media.w3.org/2010/05/sintel/trailer.mp4" // 超清
};
if (qualityIndex >= 0 && qualityIndex < qualityUrls.length) {
setVideoSource(qualityUrls[qualityIndex]);
}
}
private void initRecommendList() {
// 模拟推荐视频数据
for (int i = 1; i <= 10; i++) {
Map<String, String> video = new HashMap<>();
video.put("title", "推荐视频 " + i);
video.put("duration", "12:" + String.format("%02d", i));
video.put("image", "https://via.placeholder.com/150");
videoList.add(video);
}
// 创建适配器
ListContainer.ItemProvider provider = new ListContainer.ItemProvider() {
@Override
public int getCount() {
return videoList.size();
}
@Override
public Object getItem(int i) {
return videoList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public Component getComponent(int i, Component component, ComponentContainer componentContainer) {
DirectionalLayout itemLayout = (DirectionalLayout) LayoutScatter.getInstance(getContext())
.parse(ResourceTable.Layout_video_item, null, false);
Text title = (Text) itemLayout.findComponentById(ResourceTable.Id_item_title);
Text duration = (Text) itemLayout.findComponentById(ResourceTable.Id_item_duration);
Image image = (Image) itemLayout.findComponentById(ResourceTable.Id_item_image);
title.setText(videoList.get(i).get("title"));
duration.setText(videoList.get(i).get("duration"));
// 实际项目中应该使用图片加载库加载网络图片
// image.setPixelMap(loadImage(videoList.get(i).get("image")));
// 设置点击事件
itemLayout.setClickedListener(comp -> {
playRecommendedVideo(i);
});
return itemLayout;
}
};
recommendList.setItemProvider(provider);
}
private void playRecommendedVideo(int position) {
if (position >= 0 && position < videoList.size()) {
// 实际项目中应该使用视频的真实URL
setVideoSource("https://media.w3.org/2010/05/sintel/trailer.mp4");
getUITaskDispatcher().asyncDispatch(() -> {
videoTitle.setText(videoList.get(position).get("title"));
});
}
}
private String formatTime(long milliseconds) {
int seconds = (int) (milliseconds / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
return String.format("%02d:%02d", minutes, seconds);
}
@Override
protected void onStop() {
super.onStop();
stopProgressTimer();
if (player != null) {
player.release();
player = null;
}
}
}
2.3 视频列表项布局 (resources/base/layout/video_item.xml)
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:width="match_parent"
ohos:height="100vp"
ohos:orientation="horizontal"
ohos:margin="5vp"
ohos:background_element="#333333">
<Image
ohos:id="$+id:item_image"
ohos:width="150vp"
ohos:height="90vp"
ohos:image_src="$media:video_thumb"
ohos:scale_mode="zoom_center"/>
<DirectionalLayout
ohos:width="0vp"
ohos:height="match_parent"
ohos:weight="1"
ohos:orientation="vertical"
ohos:margin_left="10vp">
<Text
ohos:id="$+id:item_title"
ohos:width="match_parent"
ohos:height="0vp"
ohos:weight="1"
ohos:text="视频标题"
ohos:text_size="16fp"
ohos:text_color="#FFFFFF"/>
<Text
ohos:id="$+id:item_duration"
ohos:width="match_parent"
ohos:height="30vp"
ohos:text="00:00"
ohos:text_size="14fp"
ohos:text_color="#CCCCCC"/>
</DirectionalLayout>
</DirectionalLayout>
3. 功能说明
-
核心功能:
- 视频播放、暂停、进度控制
- 清晰度切换(标清、高清、超清)
- 视频推荐列表
- 播放进度显示和拖动
-
实现流程:
- 初始化播放器并设置视频源
- 处理播放器状态回调
- 更新UI播放状态
- 实现进度条同步和拖动控制
- 加载推荐视频列表
-
扩展建议:
- 添加全屏播放功能
- 实现视频缓存功能
- 添加收藏和历史记录功能
- 实现弹幕功能
- 添加视频预加载功能
- 实现播放速度调节
4. 实际项目注意事项
-
视频源处理:
- 实际项目中应使用真实的视频流地址
- 考虑使用DRM保护版权内容
- 实现多CDN切换保证播放流畅
-
性能优化:
- 使用硬件加速解码
- 实现自适应码率切换
- 添加缓冲提示和重试机制
-
UI优化:
- 添加加载动画
- 实现更精细的控制面板
- 添加手势控制(亮度、音量、进度)
-
网络处理:
- 实现断点续播
- 添加网络状态监听
- 实现离线缓存功能
更多推荐
所有评论(0)