下面是一个使用DevEco Studio和HarmonyOS 5开发视频播放功能的完整实现方案,模拟腾讯视频的核心播放功能:

1. 项目准备

  1. 在DevEco Studio中创建新项目,选择"Empty Ability"模板
  2. 确保SDK版本选择HarmonyOS 5.0或更高
  3. 在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. 功能说明

  1. ​核心功能​​:

    • 视频播放、暂停、进度控制
    • 清晰度切换(标清、高清、超清)
    • 视频推荐列表
    • 播放进度显示和拖动
  2. ​实现流程​​:

    • 初始化播放器并设置视频源
    • 处理播放器状态回调
    • 更新UI播放状态
    • 实现进度条同步和拖动控制
    • 加载推荐视频列表
  3. ​扩展建议​​:

    • 添加全屏播放功能
    • 实现视频缓存功能
    • 添加收藏和历史记录功能
    • 实现弹幕功能
    • 添加视频预加载功能
    • 实现播放速度调节

4. 实际项目注意事项

  1. ​视频源处理​​:

    • 实际项目中应使用真实的视频流地址
    • 考虑使用DRM保护版权内容
    • 实现多CDN切换保证播放流畅
  2. ​性能优化​​:

    • 使用硬件加速解码
    • 实现自适应码率切换
    • 添加缓冲提示和重试机制
  3. ​UI优化​​:

    • 添加加载动画
    • 实现更精细的控制面板
    • 添加手势控制(亮度、音量、进度)
  4. ​网络处理​​:

    • 实现断点续播
    • 添加网络状态监听
    • 实现离线缓存功能
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐