DistributedVideoPlayer 分布式视频播放器(一)

开发 前端 分布式
本示例是在官方Video Play Ability 模板基础上做了扩展开发,官方模板提供基本的视频播放功能,并允许您在手机和电视之间传输视频.

[[429810]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

介绍

本示例是在官方Video Play Ability 模板基础上做了扩展开发,官方模板提供基本的视频播放功能,并允许您在手机和电视之间传输视频.

应用分为手机端(entry)和TV端(entrytv),以及一个依赖模块(commonlib).

在示例的基础之上,手机端增加了视频播放列表功能,以及播放详情页和评论功能;手机端播放的视频可以流转到TV端,并实现远端遥控的功能。

内容比较多,会分两期给大家讲解,本期文章主要讲解的内容是手机端部分:

1.实现一个视频播放器 2.实现一个播放列表 3.实现一个评论功能.

[本文正在参与优质创作者激励]

效果展示

DistributedVideoPlayer 分布式视频播放器(一)-鸿蒙HarmonyOS技术社区

搭建环境

安装DevEco Studio,详情请参考DevEco Studio下载

设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作

如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境

下载源码后,使用DevEco 打开项目。

代码结构

Java代码

  1. │  config.json 
  2. │ 
  3. ├─java 
  4. │  └─com 
  5. │      └─buty 
  6. │          └─distributedvideoplayer 
  7. │              │  MainAbility.java                
  8. │              │  MyApplication.java 
  9. │              │ 
  10. │              ├─ability 
  11. │              │      DevicesSelectAbility.java    
  12. │              │      MainAbilitySlice.java           #视频播放列表页 
  13. │              │      SyncControlServiceAbility.java   
  14. │              │      VideoPlayAbility.java           #视频播放Ability 
  15. │              │      VideoPlayAbilitySlice.java      #视频播放详情和评论页 
  16. │              │ 
  17. │              ├─components 
  18. │              │      EpisodesSelectionDialog.java     
  19. │              │      RemoteController.java 
  20. │              │      VideoPlayerPlaybackButton.java  #播放按钮组件 
  21. │              │      VideoPlayerSlider.java          #播放时间进度条 
  22. │              │ 
  23. │              ├─constant 
  24. │              │      Constants.java                  #常量 
  25. │              │      ResolutionEnum.java             #分辨率枚举 
  26. │              │      RouteRegister.java              #自定义路由 
  27. │              │ 
  28. │              ├─data 
  29. │              │      VideoInfo.java                  #视频基础信息 
  30. │              │      VideoInfoService.java           #视频信息服务,用于模拟数据 
  31. │              │      Videos.java                     #视频列表 
  32. │              │  
  33. │              ├─model 
  34. │              │      CommentModel.java               #评论模型 
  35. │              │      DeviceModel.java                 
  36. │              │      ResolutionModel.java            #解析度模型 
  37. │              │      VideoModel.java                 #视频模型 
  38. │              │ 
  39. │              ├─provider 
  40. │              │      CommentItemProvider.java        #评论数据提供程序 
  41. │              │      DeviceItemProvider.java       
  42. │              │      ResolutionItemProvider.java     #解析度数据提供程序 
  43. │              │      VideoItemProvider.java          #视频数据提供程序 
  44. │              │ 
  45. │              └─utils 
  46. │                      AppUtil.java                   #工具类 
  47. │                      DateUtils.java 

资源文件

  1. └─resources 
  2.     ├─base 
  3.     │  ├─element 
  4.     │  │      color.json 
  5.     │  │      float.json 
  6.     │  │      strarray.json 
  7.     │  │      string.json 
  8.     │  │ 
  9.     │  ├─graphic 
  10.     │  │      background_ability_control_bg.xml           
  11.     │  │      background_ability_control_middle.xml 
  12.     │  │      background_ability_control_ok.xml 
  13.     │  │      background_ability_devices.xml 
  14.     │  │      background_ability_episodes.xml 
  15.     │  │      background_button_click.xml 
  16.     │  │      background_button_clicked.xml 
  17.     │  │      background_episodes_item.xml 
  18.     │  │      background_episodes_quality.xml 
  19.     │  │      background_episodes_trailer.xml 
  20.     │  │      background_slide_thumb.xml 
  21.     │  │      background_switch_checked.xml 
  22.     │  │      background_switch_empty.xml 
  23.     │  │      background_switch_thumb.xml 
  24.     │  │      background_switch_track.xml 
  25.     │  │      list_divider.xml 
  26.     │  │      shape_slider_thumb.xml 
  27.     │  │ 
  28.     │  ├─layout 
  29.     │  │      ability_main.xml                                #播放列表布局 
  30.     │  │      comments_item.xml                               #单条评论布局 
  31.     │  │      dialog_playlist.xml                      
  32.     │  │      dialog_resolution_list.xml 
  33.     │  │      dialog_table_layout.xml 
  34.     │  │      hm_sample_ability_video_box.xml                 #视频播放组件页 
  35.     │  │      hm_sample_ability_video_comments.xml            #播放详情布局页 
  36.     │  │      hm_sample_view_video_box_seek_bar_style1.xml    #播放进度条布局 
  37.     │  │      hm_sample_view_video_box_seek_bar_style2.xml 
  38.     │  │      remote_ability_control.xml 
  39.     │  │      remote_ability_episodes.xml 
  40.     │  │      remote_ability_select_devices.xml 
  41.     │  │      remote_ability_sound_equipment.xml 
  42.     │  │      remote_device_item.xml 
  43.     │  │      remote_episodes_item.xml 
  44.     │  │      remote_video_quality_item.xml 
  45.     │  │ 
  46.     │  ├─media 
  47.     │  │      comments.png 
  48.     │  │      great.png 
  49.     │  │      icon.png 
  50.     │  │      ic_anthology.png 
  51.     │  │ 
  52.     │  └─profile 
  53.     ├─en 
  54.     │  └─element 
  55.     │          string.json 
  56.     │ 
  57.     ├─rawfile 
  58.     │      videos.json                                      #模拟数据JSON文件 
  59.     │ 
  60.     └─zh 
  61.         └─element 
  62.                 string.json 

实现步骤

1.实现一个视频播放器

引入对commonlib的依赖后,实现一个视频播放器就很容易了.

entry的build.gradle 增加对commonlib的依赖

  1. dependencies { 
  2.     implementation fileTree(dir: 'libs', include: ['*.jar''*.har']) 
  3.     testImplementation 'junit:junit:4.13' 
  4.     ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.200' 
  5.     //引用commonlib 依赖 
  6.     implementation project(path: ':commonlib'

1.1.页面布局 hm_sample_ability_video_comments.xml

添加一个VideoPlayerView组件

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <StackLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:id="$+id:root_layout" 
  5.     ohos:height="match_parent" 
  6.     ohos:width="match_parent" 
  7.     ohos:background_element="#FFFFFFFF" 
  8.     ohos:orientation="vertical"
  9.  
  10.     <com.buty.distributedvideoplayer.player.ui.widget.media.VideoPlayerView 
  11.         ohos:id="$+id:video_view" 
  12.         ohos:height="250vp" 
  13.         ohos:width="match_parent"/> 
  14. ... 
  15. </StackLayout> 

1.2.Java代码

获取视频组件对象后,只需要2步就可以了: 1.设置视频资源路径和描述; 2.设置播放器核心组件和自定义组件.几行关键代码 VideoPlayAbilitySlice.java

  1. /** 
  2.  * 初始化播放器 
  3.  */ 
  4. private void initPlayer() { 
  5.     HiLog.debug(LABEL, "initPlayer"); 
  6.     //自定义视频播放视图组件 
  7.     player = (VideoPlayerView) findComponentById(ResourceTable.Id_video_view); 
  8.  
  9.     if (player != null) { 
  10.         //获取视频信息服务 
  11.         videoService = new VideoInfoService(getContext()); 
  12.         //获取当前播放视频的路径 
  13.         String path = videoService 
  14.                 .getVideoInfoByIndex(currentPlayingIndex) 
  15.                 .getResolutions() 
  16.                 .get(currentPlayingResolutionIndex) 
  17.                 .getUrl(); 
  18.         //视频描述 
  19.         String videoDesc = videoService.getVideoInfoByIndex(currentPlayingIndex).getVideoDesc(); 
  20.         HiLog.debug(LABEL, "videoDesc = " + videoDesc + " path = " + path); 
  21.  
  22.         if (path != null) { 
  23.             //设置路径和描述 
  24.             player.setVideoPathAndTitle(path, videoDesc); 
  25.             //视频准备完毕就播放并设置播放进度条 
  26.             player.setPlayerOnPreparedListener( 
  27.                     () -> { 
  28.                         player.start(); 
  29.                         //设置播放位置 
  30.                         player.seekTo(currentPlayingPosition); 
  31.                     }); 
  32.             //双击播放或暂停 
  33.             player.setDoubleClickedListener( 
  34.                     component -> { 
  35.                         //是否在控制TV端 
  36.                         if (remoteController != null && remoteController.isShown()) { 
  37.                             return
  38.                         } 
  39.                         HiLog.debug(LABEL, "VideoPlayView double-click event"); 
  40.                         if (player.isPlaying()) { 
  41.                             player.pause(); 
  42.                         } else { 
  43.                             player.start(); 
  44.                         } 
  45.                     }); 
  46.             //监听播放错误 
  47.             player.setErrorListener( 
  48.                     (errorType, errorCode) -> { 
  49.                         ToastDialog toast = new ToastDialog(getContext()); 
  50.                         switch (errorType) { 
  51.                             case HmPlayerAdapter.ERROR_LOADING_RESOURCE: 
  52.                                 toast.setText( 
  53.                                         AppUtil.getStringResource( 
  54.                                                 getContext(), ResourceTable.String_media_file_loading_error)); 
  55.                                 break; 
  56.                             case HmPlayerAdapter.ERROR_INVALID_OPERATION: 
  57.                                 toast.setText( 
  58.                                         AppUtil.getStringResource( 
  59.                                                 getContext(), ResourceTable.String_invalid_operation)); 
  60.                                 break; 
  61.                             default
  62.                                 toast.setText( 
  63.                                         AppUtil.getStringResource( 
  64.                                                 getContext(), ResourceTable.String_undefined_error_type)); 
  65.                                 break; 
  66.                         } 
  67.                         getUITaskDispatcher().asyncDispatch(toast::show); 
  68.                     }); 
  69.         } 
  70.         //添加核心组件,播放时间进度滑块 
  71.         addCoreComponent(); 
  72.         //添加自定义组件 
  73.         addCustomComponent(); 
  74.         HiLog.debug(LABEL, "initPlayer finish"); 
  75.     } 

2.实现一个视频播放列表功能

2.1.页面布局 ability_main.xml

使用了DirectionalLayout布局组件,还有ScrollView和ListContainer组件

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DirectionalLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:id="$+id:ability_main_root" 
  5.     ohos:height="match_parent" 
  6.     ohos:width="match_parent" 
  7.     ohos:orientation="vertical"
  8.  
  9.     <DirectionalLayout 
  10.         ohos:id="$+id:ability_main_titlebar" 
  11.         ohos:height="match_content" 
  12.         ohos:width="match_parent" 
  13.         ohos:background_element="#B0B0B0" 
  14.         ohos:orientation="horizontal" 
  15.         ohos:padding="10vp"
  16.  
  17.         <DirectionalLayout 
  18.             ohos:height="match_parent" 
  19.             ohos:width="match_content" 
  20.             ohos:weight="1"
  21.  
  22.             <Text 
  23.                 ohos:id="$+id:tag_favorite" 
  24.                 ohos:height="match_content" 
  25.                 ohos:width="match_parent" 
  26.                 ohos:text="关注" 
  27.                 ohos:text_size="$float:normal_text_size_15" 
  28.                 /> 
  29.         </DirectionalLayout> 
  30.  
  31.         <DirectionalLayout 
  32.             ohos:height="match_content" 
  33.             ohos:width="match_content" 
  34.             ohos:weight="1"
  35.  
  36.             <Text 
  37.                 ohos:id="$+id:tag_support" 
  38.                 ohos:height="match_content" 
  39.                 ohos:width="match_content" 
  40.                 ohos:text="推荐" 
  41.                 ohos:text_size="$float:normal_text_size_15" 
  42.                 /> 
  43.         </DirectionalLayout> 
  44.  
  45.         <DirectionalLayout 
  46.             ohos:height="match_content" 
  47.             ohos:width="match_content" 
  48.             ohos:weight="1"
  49.  
  50.             <Text 
  51.                 ohos:id="$+id:tag_movie" 
  52.                 ohos:height="match_content" 
  53.                 ohos:width="match_content" 
  54.                 ohos:element_end="$id:favorite" 
  55.                 ohos:text="电影" 
  56.                 ohos:text_color="#1C6AE9" 
  57.                 ohos:text_size="$float:normal_text_size_15" 
  58.                 ohos:text_weight="600"/> 
  59.  
  60.             <Component 
  61.                 ohos:id="$+id:device_item_divider" 
  62.                 ohos:height="2vp" 
  63.                 ohos:width="30vp" 
  64.                 ohos:background_element="$graphic:list_divider"/> 
  65.         </DirectionalLayout> 
  66.  
  67.         <DirectionalLayout 
  68.             ohos:height="match_content" 
  69.             ohos:width="match_content" 
  70.             ohos:weight="1"
  71.  
  72.             <Text 
  73.                 ohos:id="$+id:tag_live" 
  74.                 ohos:height="match_content" 
  75.                 ohos:width="match_parent" 
  76.                 ohos:text="直播" 
  77.                 ohos:text_size="$float:normal_text_size_15" 
  78.                 /> 
  79.         </DirectionalLayout> 
  80.  
  81.         <DirectionalLayout 
  82.             ohos:height="match_content" 
  83.             ohos:width="match_content" 
  84.             ohos:weight="1"
  85.  
  86.             <Text 
  87.                 ohos:id="$+id:tag_tv" 
  88.                 ohos:height="match_content" 
  89.                 ohos:width="match_parent" 
  90.                 ohos:text="电视" 
  91.                 ohos:text_size="$float:normal_text_size_15" 
  92.                 /> 
  93.         </DirectionalLayout> 
  94.  
  95.     </DirectionalLayout> 
  96.  
  97.     <ScrollView 
  98.         ohos:height="match_parent" 
  99.         ohos:width="match_parent" 
  100.         ohos:id="$+id:video_list_scroll" > 
  101.  
  102.         <ListContainer 
  103.             ohos:id="$+id:videos_container" 
  104.             ohos:height="match_parent" 
  105.             ohos:width="match_parent"
  106.  
  107.         </ListContainer> 
  108.     </ScrollView> 
  109. </DirectionalLayout> 

2.2.Java代码

申请用户敏感权限授权 MainAbility.java

  1. /** 
  2.  * Entry to the main interface of the program 
  3.  */ 
  4. public class MainAbility extends Ability { 
  5.     private static final int REQUEST_CODE = 1; 
  6.  
  7.     //读写媒体权限 
  8.     private final String[] permissionLists 
  9.             = new String[]{"ohos.permission.READ_MEDIA""ohos.permission.WRITE_MEDIA"}; 
  10.  
  11.     @Override 
  12.     public void onStart(Intent intent) { 
  13.         super.onStart(intent); 
  14.         super.setUIContent(ResourceTable.Layout_ability_main); 
  15.         super.setMainRoute(MainAbilitySlice.class.getName()); 
  16.  
  17.         //申请授权 
  18.         verifyPermissions(); 
  19.     } 
  20.     private void verifyPermissions() { 
  21.         for (String permissionList : permissionLists) { 
  22.             int result = verifySelfPermission(permissionList); 
  23.             if (result != IBundleManager.PERMISSION_GRANTED) { 
  24.                 requestPermissionsFromUser(permissionLists, REQUEST_CODE); 
  25.             } 
  26.         } 
  27.     } 
  28. ... 

通过VideoInfoService读取videos.json文件初始化视频容器列表MainAbilitySlice.java

  1. public class MainAbilitySlice extends AbilitySlice { 
  2.     public static final HiLogLabel LABEL = new HiLogLabel(0, 0, "=>MainAbilitySlice"); 
  3.     private VideoInfoService videoService; 
  4.  
  5.     @Override 
  6.     protected void onStart(Intent intent) { 
  7.         super.onStart(intent); 
  8.         //加载视频播放器页面 
  9.         super.setUIContent(ResourceTable.Layout_ability_main); 
  10.         initVideoContainer(); 
  11.  
  12.     } 
  13.  
  14.     /** 
  15.      * 模拟数据 
  16.      * 初始化视频容器列表 
  17.      */ 
  18.     private void initVideoContainer() { 
  19.         HiLog.debug(LABEL, "initVideoContainer"); 
  20.         List<VideoModel> videos = new ArrayList<>(); 
  21.         //获取视频信息服务 
  22.         videoService = new VideoInfoService(getContext()); 
  23.  
  24.         for (int i = 0; i < 7; i++) { 
  25.             VideoModel video = new VideoModel(); 
  26.             video.setComments(new Random().nextInt(1000)); 
  27.             video.setFavorites(new Random().nextInt(1000)); 
  28.             video.setGreats(new Random().nextInt(10000)); 
  29.             VideoInfo videoInfo = videoService.getVideoInfoByIndex(i); 
  30.             videoInfo.setIndex(i); 
  31.             video.setVideoInfo(videoInfo); 
  32.             videos.add(video); 
  33.         } 
  34.  
  35.         ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_videos_container); 
  36.         //容器绑定数据提供程序 
  37.         VideoItemProvider provider = new VideoItemProvider(this, videos, this); 
  38.         listContainer.setItemProvider(provider); 
  39.     } 

 视频容器列表数据提供程序 VideoItemProvider.java

  1. public class VideoItemProvider extends BaseItemProvider { 
  2.     private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "=>VideoItemProvider"); 
  3.     private final Context context; 
  4.     private final List<VideoModel> list; 
  5.     private AbilitySlice abilitySlice; 
  6.     //当前播放视频分辨率索引 
  7.     private int currentPlayingResolutionIndex = 0; 
  8.  
  9.     /** 
  10.      * Initialization 
  11.      */ 
  12.     public VideoItemProvider(Context context, List<VideoModel> list, AbilitySlice abilitySlice) { 
  13.         HiLog.debug(LABEL, "VideoItemProvider"); 
  14.         this.context = context; 
  15.         this.list = list; 
  16.         this.abilitySlice = abilitySlice; 
  17.     } 
  18.  
  19.     public Context getContext() { 
  20.         return context; 
  21.     } 
  22.  
  23.     @Override 
  24.     public int getCount() { 
  25.         return list == null ? 0 : list.size(); 
  26.     } 
  27.  
  28.     @Override 
  29.     public Object getItem(int position) { 
  30.         if (list != null && position >= 0 && position < list.size()) { 
  31.             return list.get(position); 
  32.         } 
  33.         return new VideoModel(); 
  34.     } 
  35.  
  36.     @Override 
  37.     public long getItemId(int position) { 
  38.         return position; 
  39.     } 
  40.  
  41.     @Override 
  42.     public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) { 
  43.         HiLog.debug(LABEL, "getComponent:" + convertComponent + "," + componentContainer); 
  44.         final Component cpt; 
  45.         final VideoPlayerView player; 
  46.  
  47.         VideoModel item = list.get(position); 
  48.  
  49.         //初次获取组件 
  50.         if (convertComponent == null) { 
  51.             cpt = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_hm_sample_ability_video_box, componentContainer, false); 
  52.             player = (VideoPlayerView) cpt.findComponentById(ResourceTable.Id_video_view); 
  53.             //初始化播放器组件 
  54.             initPlayer(player,item.getVideoInfo()); 
  55.         } else { 
  56.             //ScrollView滑动时也会调用getComponent方法, 
  57.             cpt = convertComponent; 
  58.             player = (VideoPlayerView) cpt.findComponentById(ResourceTable.Id_video_view); 
  59.         } 
  60.  
  61.         //设置其他组件的值 
  62.         Text text_favorites = (Text) cpt.findComponentById(ResourceTable.Id_text_favorites); 
  63.         Text text_comments = (Text) cpt.findComponentById(ResourceTable.Id_text_comments); 
  64.         Text text_greats = (Text) cpt.findComponentById(ResourceTable.Id_text_greats); 
  65.         //注意setText的参数类型是字符串 
  66.         text_favorites.setText(item.getFavorites() + ""); 
  67.         text_comments.setText(item.getComments() + ""); 
  68.         text_greats.setText(item.getGreats() + ""); 
  69.         //点击评论图标,停止当前播放器,打开VideoPlayAbility的Slice页面 
  70.         Image commentImage = (Image) cpt.findComponentById(ResourceTable.Id_image_comment); 
  71.         commentImage.setClickedListener(component -> { 
  72.             //停止播放,列表页面的播放器 
  73.             player.stopPlayback(); 
  74.             //打开带有评论的播放页面 
  75.             startVideoPlayDetail(item,player); 
  76.         }); 
  77.         return cpt; 
  78.     } 
  79.  
  80.     /** 
  81.      * 初始化播放器 
  82.      */ 
  83.     private void initPlayer(VideoPlayerView player, VideoInfo videoInfo) { 
  84.         HiLog.debug(LABEL, "initPlayer"); 
  85.         if (player != null) { 
  86.             //视频路径 
  87.             String path = videoInfo 
  88.                     .getResolutions() 
  89.                     .get(currentPlayingResolutionIndex) 
  90.                     .getUrl(); 
  91.             HiLog.debug(LABEL, "path:" + path); 
  92.  
  93.             //视频描述 
  94.             String videoDesc = videoInfo.getVideoDesc(); 
  95.             HiLog.debug(LABEL, "videoDesc  = " + videoDesc + " path = " + path); 
  96.  
  97.             if(path!=null) { 
  98.                 //设置路径和名称 
  99.                 player.setVideoPathAndTitle(path, videoDesc); 
  100.                 //双击播放或暂停 
  101.                 player.setDoubleClickedListener( 
  102.                         component -> { 
  103.                             HiLog.debug(LABEL, "VideoPlayView double-click event"); 
  104.                             if (player.isPlaying()) { 
  105.                                 player.pause(); 
  106.                             } else { 
  107.                                 player.start(); 
  108.                             } 
  109.                         }); 
  110.                 //监听播放错误 
  111.                 player.setErrorListener( 
  112.                         (errorType, errorCode) -> { 
  113.                             ToastDialog toast = new ToastDialog(getContext()); 
  114.                             switch (errorType) { 
  115.                                 case HmPlayerAdapter.ERROR_LOADING_RESOURCE: 
  116.                                     toast.setText( 
  117.                                             AppUtil.getStringResource( 
  118.                                                     getContext(), ResourceTable.String_media_file_loading_error)); 
  119.                                     break; 
  120.                                 case HmPlayerAdapter.ERROR_INVALID_OPERATION: 
  121.                                     toast.setText( 
  122.                                             AppUtil.getStringResource( 
  123.                                                     getContext(), ResourceTable.String_invalid_operation)); 
  124.                                     break; 
  125.                                 default
  126.                                     toast.setText( 
  127.                                             AppUtil.getStringResource( 
  128.                                                     getContext(), ResourceTable.String_undefined_error_type)); 
  129.                                     break; 
  130.                             } 
  131.                             abilitySlice.getUITaskDispatcher().asyncDispatch(toast::show); 
  132.                         }); 
  133.             } 
  134.             //添加核心组件,播放时间进度滑块 
  135.             addCoreComponent(player); 
  136.             //添加自定义组件 
  137.             HiLog.debug(LABEL, "initPlayer finish"); 
  138.         } 
  139.     } 
  140.  
  141.     /** 
  142.      * 添加播放按钮,剧集,进度条等核心组件 
  143.      * Adding core component like playback button and seek bar 
  144.      */ 
  145.     private void addCoreComponent(VideoPlayerView player) { 
  146.         HiLog.debug(LABEL, "addCoreComponent"); 
  147.         //添加播放按钮组件 
  148.         player.addPlaybackButton(new VideoPlayerPlaybackButton(getContext()), VideoBoxArea.BOTTOM); 
  149.         //添加播放进度条组件 
  150.         player.addSeekBar( 
  151.                 new VideoPlayerSlider(getContext()), 
  152.                 VideoBoxArea.BOTTOM, 
  153.                 (int) AppUtil.getFloatResource(getContext(), ResourceTable.Float_normal_margin_24)); 
  154.     } 
  155.  
  156.     /** 
  157.      * 打开视频播放详情页 
  158.      */ 
  159.     private void startVideoPlayDetail(VideoModel item,VideoPlayerView player) { 
  160.         HiLog.debug(LABEL, "startVideoPlayDetail "); 
  161.         //启动评论播放页面 
  162.         Intent intentService = new Intent(); 
  163.         //播放视频评论数 
  164.         intentService.setParam("comments",String.valueOf(item.getComments())); 
  165.  
  166.         VideoInfo videoInfo=item.getVideoInfo(); 
  167.  
  168.         if(videoInfo!=null) { 
  169.             //播放视频在播放列表中的索引 
  170.             intentService.setParam("currentPlayingIndex", videoInfo.getIndex()); 
  171.             //播放视频的分辨率索引 
  172.             intentService.setParam("currentPlayingResolutionIndex", currentPlayingResolutionIndex); 
  173.             //当前视频播放的位置 
  174.             intentService.setParam(RemoteConstant.INTENT_PARAM_REMOTE_START_POSITION,(int)player.getCurrentPosition()); 
  175.             HiLog.debug(LABEL,RemoteConstant.INTENT_PARAM_REMOTE_START_POSITION+":"+player.getCurrentPosition()); 
  176.         } 
  177.         Operation operation = 
  178.                 new Intent.OperationBuilder() 
  179.                         .withDeviceId(""
  180.                         .withBundleName(abilitySlice.getBundleName()) 
  181.                         .withAbilityName(VideoPlayAbility.class) 
  182.                         .build(); 
  183.         intentService.setOperation(operation); 
  184.         abilitySlice.startAbility(intentService); 
  185.     } 

3.实现一个评论功能

3.1.页面布局,评论列表布局页 hm_sample_ability_video_comments.xml

使用了StackLayout,DependentLayout布局组件和ListContainer,TextField,Text 组件

  1. ... 
  2.     <!-- 工具栏 --> 
  3.     <DependentLayout 
  4.         ohos:id="$+id:comments_bar" 
  5.         ohos:height="40vp" 
  6.         ohos:width="match_parent" 
  7.         ohos:background_element="#FFDDDADA" 
  8.         ohos:layout_alignment="top" 
  9.         ohos:top_margin="250vp"
  10.  
  11.         <DirectionalLayout 
  12.             ohos:id="$+id:favorite" 
  13.             ohos:height="match_parent" 
  14.             ohos:width="match_content" 
  15.             ohos:align_parent_left="true" 
  16.             ohos:left_margin="10vp" 
  17.             ohos:orientation="horizontal" 
  18.             ohos:padding="4vp"
  19.  
  20.             <Text 
  21.                 ohos:id="$+id:text_favorites" 
  22.                 ohos:height="match_parent" 
  23.                 ohos:width="match_parent" 
  24.                 ohos:left_padding="5vp" 
  25.                 ohos:text="简介" 
  26.                 ohos:text_size="16fp" 
  27.                 ohos:text_weight="600"
  28.  
  29.             </Text> 
  30.         </DirectionalLayout> 
  31.  
  32.         <DirectionalLayout 
  33.             ohos:id="$+id:comment" 
  34.             ohos:height="match_parent" 
  35.             ohos:width="match_content" 
  36.             ohos:end_of="$id:favorite" 
  37.             ohos:left_margin="20vp" 
  38.             ohos:orientation="horizontal" 
  39.             ohos:padding="4vp"
  40.  
  41.             <Text 
  42.                 ohos:id="$+id:text_comment" 
  43.                 ohos:height="match_parent" 
  44.                 ohos:width="match_content" 
  45.                 ohos:left_padding="5vp" 
  46.                 ohos:text="评论" 
  47.                 ohos:text_weight="600" 
  48.                 ohos:text_size="16fp"
  49.             </Text> 
  50.  
  51.             <Text 
  52.                 ohos:id="$+id:text_comments" 
  53.                 ohos:height="match_parent" 
  54.                 ohos:width="match_content" 
  55.                 ohos:left_padding="5vp" 
  56.                 ohos:text="1161" 
  57.                 ohos:text_weight="600" 
  58.                 ohos:text_size="16fp"
  59.             </Text> 
  60.         </DirectionalLayout> 
  61.  
  62.     </DependentLayout> 
  63.  
  64.     <!-- 评论列表 --> 
  65.     <DependentLayout 
  66.         ohos:id="$+id:comments_view" 
  67.         ohos:height="match_parent" 
  68.         ohos:width="match_parent" 
  69.         ohos:background_element="#FFFDFFFF" 
  70.         ohos:top_margin="290vp"
  71.  
  72.         <ListContainer 
  73.             ohos:id="$+id:comments_container" 
  74.             ohos:height="match_parent" 
  75.             ohos:width="match_parent" 
  76.             ohos:layout_alignment="horizontal_center" 
  77.             ohos:orientation="vertical"/> 
  78.  
  79.     </DependentLayout> 
  80.  
  81.     <!-- 评论对话框 --> 
  82.     <DependentLayout 
  83.         ohos:id="$+id:comments_dialog" 
  84.         ohos:height="70vp" 
  85.         ohos:width="match_parent" 
  86.         ohos:background_element="#FFF4F4F8" 
  87.         ohos:layout_alignment="bottom" 
  88.         ohos:padding="5vp"
  89.  
  90.         <TextField 
  91.             ohos:id="$+id:comment_tf" 
  92.             ohos:height="match_parent" 
  93.             ohos:width="match_parent" 
  94.             ohos:hint="发一条友好的评论吧" 
  95.             ohos:right_padding="60vp" 
  96.             ohos:text_size="16vp"></TextField> 
  97.  
  98.         <Text 
  99.             ohos:id="$+id:sent_comment" 
  100.             ohos:height="match_parent" 
  101.             ohos:width="60vp" 
  102.             ohos:align_parent_right="true" 
  103.             ohos:background_element="#FF7ECCCF" 
  104.             ohos:text="发送" 
  105.             ohos:input_enter_key_type="enter_key_type_send" 
  106.             ohos:text_alignment="center" 
  107.             ohos:text_size="16vp"
  108.         </Text> 
  109.     </DependentLayout> 

3.2.页面布局,单条评论组件布局 comments_item.xml

使用了DependentLayout,DirectionalLayout布局组件和Image,Component,Text 组件

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DependentLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:height="match_content" 
  5.     ohos:width="match_parent"
  6.  
  7.     <Image 
  8.         ohos:id="$+id:header_item_icon" 
  9.         ohos:height="50vp" 
  10.         ohos:width="50vp" 
  11.         ohos:end_margin="13vp" 
  12.         ohos:left_margin="5vp" 
  13.         ohos:image_src="$media:ic_header" 
  14.         ohos:scale_mode="stretch" 
  15.         ohos:top_margin="13vp"/> 
  16.  
  17.     <DirectionalLayout 
  18.         ohos:id="$+id:comment_item_content" 
  19.         ohos:height="match_content" 
  20.         ohos:width="250vp" 
  21.         ohos:bottom_padding="5vp" 
  22.         ohos:orientation="vertical" 
  23.         ohos:right_of="$id:header_item_icon"
  24.  
  25.         <Text 
  26.             ohos:id="$+id:comment_uname" 
  27.             ohos:height="match_content" 
  28.             ohos:width="160vp" 
  29.             ohos:layout_alignment="vertical_center" 
  30.             ohos:padding="5vp" 
  31.             ohos:text="我是一只鱼" 
  32.             ohos:text_color="#FFA09E9E" 
  33.             ohos:text_size="16vp"/> 
  34.  
  35.         <Text 
  36.             ohos:id="$+id:comment_content" 
  37.             ohos:height="match_content" 
  38.             ohos:width="match_parent" 
  39.             ohos:layout_alignment="vertical_center" 
  40.             ohos:multiple_lines="true" 
  41.             ohos:padding="5vp" 
  42.             ohos:text="这是一条评论" 
  43.             ohos:text_color="$color:default_black_color" 
  44.             ohos:text_size="16vp"/> 
  45.  
  46.         <Text 
  47.             ohos:id="$+id:comment_date" 
  48.             ohos:height="match_content" 
  49.             ohos:width="match_content" 
  50.             ohos:layout_alignment="vertical_center" 
  51.             ohos:padding="5vp" 
  52.             ohos:text="2天前" 
  53.             ohos:text_color="#FFA09E9E" 
  54.             ohos:text_size="16vp"/> 
  55.  
  56.     </DirectionalLayout> 
  57.  
  58.     <DirectionalLayout 
  59.         ohos:height="match_content" 
  60.         ohos:width="match_content" 
  61.         ohos:id="$+id:goods_view" 
  62.         ohos:align_parent_right="true" 
  63.         ohos:padding="5vp" 
  64.         ohos:right_margin="5vp" 
  65.         ohos:right_of="$id:comment_item_content"
  66.  
  67.         <Image 
  68.             ohos:id="$+id:good_icon" 
  69.             ohos:height="25vp" 
  70.             ohos:width="match_parent" 
  71.             ohos:image_src="$media:ic_great" 
  72.             ohos:scale_mode="stretch" 
  73.             ohos:top_margin="13vp" 
  74.             /> 
  75.  
  76.         <Text 
  77.             ohos:id="$+id:comment_goods" 
  78.             ohos:height="match_content" 
  79.             ohos:width="match_parent" 
  80.             ohos:text="42" 
  81.             ohos:text_alignment="center" 
  82.             ohos:text_color="#FFA09E9E" 
  83.             ohos:text_size="12vp"></Text> 
  84.  
  85.     </DirectionalLayout> 
  86.  
  87.     <DirectionalLayout 
  88.         ohos:height="match_content" 
  89.         ohos:width="match_parent" 
  90.         ohos:align_bottom="$id:comment_item_content"
  91.         <Component 
  92.             ohos:id="$+id:device_item_divider" 
  93.             ohos:height="1vp" 
  94.             ohos:width="match_parent" 
  95.             ohos:background_element="$graphic:list_divider"/> 
  96.     </DirectionalLayout> 
  97. </DependentLayout> 

3.3.Java代码

创建评论列表提供程序类 CommentItemProvider.java

  1. public class CommentItemProvider extends BaseItemProvider { 
  2.     private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "=>CommentsItemProvider"); 
  3.     private final Context context; 
  4.     private final List<CommentModel> list; 
  5.     private AbilitySlice abilitySlice; 
  6.  
  7.     /** 
  8.      * Initialization 
  9.      */ 
  10.     public CommentItemProvider(Context context, List<CommentModel> list, AbilitySlice slice) { 
  11.         this.context = context; 
  12.         this.list = list; 
  13.         this.abilitySlice = slice; 
  14.     } 
  15.  
  16.     @Override 
  17.     public int getCount() { 
  18.         return list == null ? 0 : list.size(); 
  19.     } 
  20.  
  21.     @Override 
  22.     public Object getItem(int position) { 
  23.         if (list != null && position >= 0 && position < list.size()) { 
  24.             return list.get(position); 
  25.         } 
  26.         return new CommentModel(); 
  27.     } 
  28.  
  29.     public void addComment(CommentModel comment) { 
  30.         if (comment == nullreturn
  31.  
  32.         list.add(0, comment); 
  33.         notifyDataSetItemInserted(0); 
  34.     } 
  35.  
  36.     @Override 
  37.     public long getItemId(int position) { 
  38.         return position; 
  39.     } 
  40.  
  41.     @Override 
  42.     public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) { 
  43.         final Component cpt; 
  44.         if (convertComponent == null) { 
  45.             cpt = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_comments_item, nullfalse); 
  46.         } else { 
  47.             cpt = convertComponent; 
  48.         } 
  49.  
  50.  
  51.         CommentModel item = list.get(position); 
  52.         //评论人头像 
  53.         Image headerIcon = (Image) cpt.findComponentById(ResourceTable.Id_header_item_icon); 
  54.         headerIcon.setPixelMap(ResourceTable.Media_ic_header); 
  55.  
  56.         //评论人名称 
  57.         Text uName = (Text) cpt.findComponentById(ResourceTable.Id_comment_uname); 
  58.         uName.setText(item.getuName()); 
  59.  
  60.         //评论内容 
  61.         Text uContent = (Text) cpt.findComponentById(ResourceTable.Id_comment_content); 
  62.         uContent.setText(item.getCommentContent()); 
  63.         //点击事件 
  64.         uContent.setClickedListener(component -> { 
  65.             DependentLayout commentDialog = (DependentLayout) abilitySlice.findComponentById(ResourceTable.Id_comments_dialog); 
  66.             if (commentDialog.getVisibility() == Component.VISIBLE) { 
  67.                 commentDialog.setVisibility(Component.VISIBLE); 
  68.             } 
  69.             TextField comment = (TextField) abilitySlice.findComponentById(ResourceTable.Id_comment_tf); 
  70.             comment.setText(""); 
  71.             comment.setHint("回复 " + item.getuName() + ":"); 
  72.             comment.requestFocus(); 
  73.         }); 
  74.  
  75.  
  76.         //评论日期 
  77.         Text uDate = (Text) cpt.findComponentById(ResourceTable.Id_comment_date); 
  78.         uDate.setText(item.getCommentDate()); 
  79.  
  80.         //已赞数 
  81.         Text goods = (Text) cpt.findComponentById(ResourceTable.Id_comment_goods); 
  82.         HiLog.debug(LABEL, "goods:" + item.getCommentGoods()); 
  83.         goods.setText(item.getCommentGoods() + ""); 
  84.  
  85.         //点赞 
  86.         Image greatImage = (Image) cpt.findComponentById(ResourceTable.Id_good_icon); 
  87.         //点赞加一 
  88.         greatImage.setClickedListener(component1 -> { 
  89.             greatImage.setPixelMap(ResourceTable.Media_ic_great_red); 
  90.             goods.setTextColor(Color.RED); 
  91.             goods.setText((item.getCommentGoods() + 1) + ""); 
  92.         }); 
  93.  
  94.         if (position == list.size() - 1) { 
  95.             Component divider = cpt.findComponentById(ResourceTable.Id_device_item_divider); 
  96.             divider.setVisibility(Component.INVISIBLE); 
  97.         } 
  98.  
  99.         return cpt; 
  100.     } 

 初始化评论列表和评论功能 VideoPlayAbilitySlice.java

  1. /** 
  2.  * 初始化评论列表 
  3.  */ 
  4. private void initComments(AbilitySlice slice) { 
  5.     //评论列表,以及设置点击的监听事件、传递数据 
  6.     ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_comments_container); 
  7.     List<CommentModel> list = new ArrayList<>(); 
  8.     for (int i = 0; i < 10; i++) { 
  9.         CommentModel obj = new CommentModel(); 
  10.         obj.setuId(i + ""); 
  11.         obj.setuName(getRandomUname()); 
  12.         obj.setCommentContent(getRandomText()); 
  13.         obj.setCommentDate("刚刚"); 
  14.         obj.setCommentGoods(new Random().nextInt(100)); 
  15.         list.add(obj); 
  16.     } 
  17.  
  18.     //容器绑定数据提供程序 
  19.     provider = new CommentItemProvider(this, list, slice); 
  20.     listContainer.setItemProvider(provider); 
  21.  
  22.     Component sendComment = findComponentById(ResourceTable.Id_sent_comment); 
  23.     TextField comment = (TextField) findComponentById(ResourceTable.Id_comment_tf); 
  24.  
  25.     //评论功能 
  26.     sendComment.setClickedListener(component -> { 
  27.         if (comment.getText().isEmpty()) { 
  28.             new ToastDialog(getContext()).setText("评论不能为空").show(); 
  29.             return
  30.         } 
  31.         CommentModel model = new CommentModel(); 
  32.         model.setCommentContent(comment.getText()); 
  33.         model.setuId(UUID.randomUUID().toString()); 
  34.         model.setuName("robot"); 
  35.         model.setCommentDate("刚刚"); 
  36.  
  37.         //添加到数据提供程序中 
  38.         provider.addComment(model); 
  39.  
  40.         comment.setText(""); 
  41.         //评论数+1 
  42.         commentsText.setText(String.valueOf(Integer.valueOf(comments) + 1)); 
  43.  
  44.         new ToastDialog(getContext()).setText("评论成功").show(); 
  45.  
  46.     }); 

问题总结

1.播放器列表滑动时,会重复添加播放组件的问题

2.评论功能,输入框跟随输入法调整位置的问题(待完善)

3.播放列表滑动时,根据ScrollView的位置控制视频播放(待完善)

4.播放列表中播放视频进度同步到播放详情页的问题(待完善)

文章相关附件可以点击下面的原文链接前往下载

https://harmonyos.51cto.com/resource/1338

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-10-21 16:00:07

鸿蒙HarmonyOS应用

2021-08-24 15:13:06

鸿蒙HarmonyOS应用

2022-11-08 15:48:35

应用开发音乐播放器

2022-08-16 17:37:06

视频播放器鸿蒙

2011-07-20 16:21:20

iPhone 视频 播放器

2015-05-21 15:25:42

VLC播放器

2023-03-28 09:38:34

开发应用鸿蒙

2022-06-21 14:41:38

播放器适配西瓜视频

2022-11-12 08:26:04

VLC视频播放器裁剪视频

2018-05-25 14:37:58

2023-03-28 09:44:02

开发应用鸿蒙

2023-03-29 09:37:49

视频播放器应用鸿蒙

2023-03-06 16:20:08

视频播放器VLC

2015-09-01 16:48:44

ios暴风视频播放器

2023-03-29 09:32:15

视频播放器应用鸿蒙

2023-08-26 19:07:40

VLC旋转视频

2011-06-13 09:33:04

2019-10-10 09:16:34

Zookeeper架构分布式

2021-11-22 16:30:30

分布式一致性分布式系统

2012-06-04 13:44:08

点赞
收藏

51CTO技术栈公众号