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

想了解更多内容,分布放器请访问:
和华为官方合作共建的式视鸿蒙技术社区
https://harmonyos.51cto.com
介绍
本示例是在官方Video Play Ability 模板基础上做了扩展开发,官方模板提供基本的视频播放功能,并允许您在手机和电视之间传输视频.
应用分为手机端(entry)和TV端(entrytv),频播以及一个依赖模块(commonlib).
在示例的基础之上,手机端增加了视频播放列表功能,以及播放详情页和评论功能;手机端播放的视频可以流转到TV端,并实现远端遥控的功能。
内容比较多,分布放器会分两期给大家讲解,本期文章主要讲解的内容是手机端部分:
1.实现一个视频播放器 2.实现一个播放列表 3.实现一个评论功能.
[本文正在参与优质创作者激励]
效果展示

搭建环境
安装DevEco Studio,详情请参考DevEco Studio下载。式视
设置DevEco Studio开发环境,频播DevEco Studio开发环境需要依赖于网络环境,分布放器需要连接上网络才能确保工具的式视正常使用,可以根据如下两种情况来配置开发环境:
如果可以直接访问Internet,频播只需进行下载HarmonyOS SDK操作。免费源码下载分布放器
如果网络不能直接访问Internet,式视需要通过代理服务器才可以访问,频播请参考配置开发环境。分布放器
下载源码后,式视使用DevEco 打开项目。频播
代码结构
Java代码
│ config.json │ ├─java │ └─com │ └─buty │ └─distributedvideoplayer │ │ MainAbility.java │ │ MyApplication.java │ │ │ ├─ability │ │ DevicesSelectAbility.java │ │ MainAbilitySlice.java #视频播放列表页 │ │ SyncControlServiceAbility.java │ │ VideoPlayAbility.java #视频播放Ability │ │ VideoPlayAbilitySlice.java #视频播放详情和评论页 │ │ │ ├─components │ │ EpisodesSelectionDialog.java │ │ RemoteController.java │ │ VideoPlayerPlaybackButton.java #播放按钮组件 │ │ VideoPlayerSlider.java #播放时间进度条 │ │ │ ├─constant │ │ Constants.java #常量 │ │ ResolutionEnum.java #分辨率枚举 │ │ RouteRegister.java #自定义路由 │ │ │ ├─data │ │ VideoInfo.java #视频基础信息 │ │ VideoInfoService.java #视频信息服务,用于模拟数据 │ │ Videos.java #视频列表 │ │ │ ├─model │ │ CommentModel.java #评论模型 │ │ DeviceModel.java │ │ ResolutionModel.java #解析度模型 │ │ VideoModel.java #视频模型 │ │ │ ├─provider │ │ CommentItemProvider.java #评论数据提供程序 │ │ DeviceItemProvider.java │ │ ResolutionItemProvider.java #解析度数据提供程序 │ │ VideoItemProvider.java #视频数据提供程序 │ │ │ └─utils │ AppUtil.java #工具类 │ DateUtils.java资源文件
└─resources ├─base │ ├─element │ │ color.json │ │ float.json │ │ strarray.json │ │ string.json │ │ │ ├─graphic │ │ background_ability_control_bg.xml │ │ background_ability_control_middle.xml │ │ background_ability_control_ok.xml │ │ background_ability_devices.xml │ │ background_ability_episodes.xml │ │ background_button_click.xml │ │ background_button_clicked.xml │ │ background_episodes_item.xml │ │ background_episodes_quality.xml │ │ background_episodes_trailer.xml │ │ background_slide_thumb.xml │ │ background_switch_checked.xml │ │ background_switch_empty.xml │ │ background_switch_thumb.xml │ │ background_switch_track.xml │ │ list_divider.xml │ │ shape_slider_thumb.xml │ │ │ ├─layout │ │ ability_main.xml #播放列表布局 │ │ comments_item.xml #单条评论布局 │ │ dialog_playlist.xml │ │ dialog_resolution_list.xml │ │ dialog_table_layout.xml │ │ hm_sample_ability_video_box.xml #视频播放组件页 │ │ hm_sample_ability_video_comments.xml #播放详情布局页 │ │ hm_sample_view_video_box_seek_bar_style1.xml #播放进度条布局 │ │ hm_sample_view_video_box_seek_bar_style2.xml │ │ remote_ability_control.xml │ │ remote_ability_episodes.xml │ │ remote_ability_select_devices.xml │ │ remote_ability_sound_equipment.xml │ │ remote_device_item.xml │ │ remote_episodes_item.xml │ │ remote_video_quality_item.xml │ │ │ ├─media │ │ comments.png │ │ great.png │ │ icon.png │ │ ic_anthology.png │ │ │ └─profile ├─en │ └─element │ string.json │ ├─rawfile │ videos.json #模拟数据JSON文件 │ └─zh └─element string.json实现步骤
1.实现一个视频播放器
引入对commonlib的依赖后,实现一个视频播放器就很容易了.
entry的build.gradle 增加对commonlib的依赖
dependencies { implementation fileTree(dir: libs, include: [*.jar, *.har]) testImplementation junit:junit:4.13 ohosTestImplementation com.huawei.ohos.testkit:runner:1.0.0.200 //引用commonlib 依赖 implementation project(path: :commonlib) }1.1.页面布局 hm_sample_ability_video_comments.xml
添加一个VideoPlayerView组件
<?xml version="1.0" encoding="utf-8"?> <StackLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:id="$+id:root_layout" ohos:height="match_parent" ohos:width="match_parent" ohos:background_element="#FFFFFFFF" ohos:orientation="vertical"> <com.buty.distributedvideoplayer.player.ui.widget.media.VideoPlayerView ohos:id="$+id:video_view" ohos:height="250vp" ohos:width="match_parent"/> ... </StackLayout>1.2.Java代码
获取视频组件对象后,只需要2步就可以了: 1.设置视频资源路径和描述; 2.设置播放器核心组件和自定义组件.几行关键代码 VideoPlayAbilitySlice.java
/** * 初始化播放器 */ private void initPlayer() { HiLog.debug(LABEL, "initPlayer"); //自定义视频播放视图组件 player = (VideoPlayerView) findComponentById(ResourceTable.Id_video_view); if (player != null) { //获取视频信息服务 videoService = new VideoInfoService(getContext()); //获取当前播放视频的路径 String path = videoService .getVideoInfoByIndex(currentPlayingIndex) .getResolutions() .get(currentPlayingResolutionIndex) .getUrl(); //视频描述 String videoDesc = videoService.getVideoInfoByIndex(currentPlayingIndex).getVideoDesc(); HiLog.debug(LABEL, "videoDesc = " + videoDesc + " path = " + path); if (path != null) { //设置路径和描述 player.setVideoPathAndTitle(path, videoDesc); //视频准备完毕就播放并设置播放进度条 player.setPlayerOnPreparedListener( () -> { player.start(); //设置播放位置 player.seekTo(currentPlayingPosition); }); //双击播放或暂停 player.setDoubleClickedListener( component -> { //是否在控制TV端 if (remoteController != null && remoteController.isShown()) { return; } HiLog.debug(LABEL, "VideoPlayView double-click event"); if (player.isPlaying()) { player.pause(); } else { player.start(); } }); //监听播放错误 player.setErrorListener( (errorType, errorCode) -> { ToastDialog toast = new ToastDialog(getContext()); switch (errorType) { case HmPlayerAdapter.ERROR_LOADING_RESOURCE: toast.setText( AppUtil.getStringResource( getContext(), ResourceTable.String_media_file_loading_error)); break; case HmPlayerAdapter.ERROR_INVALID_OPERATION: toast.setText( AppUtil.getStringResource( getContext(), ResourceTable.String_invalid_operation)); break; default: toast.setText( AppUtil.getStringResource( getContext(), ResourceTable.String_undefined_error_type)); break; } getUITaskDispatcher().asyncDispatch(toast::show); }); } //添加核心组件,播放时间进度滑块 addCoreComponent(); //添加自定义组件 addCustomComponent(); HiLog.debug(LABEL, "initPlayer finish"); } }2.实现一个视频播放列表功能
2.1.页面布局 ability_main.xml
使用了DirectionalLayout布局组件,还有ScrollView和ListContainer组件
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:id="$+id:ability_main_root" ohos:height="match_parent" ohos:width="match_parent" ohos:orientation="vertical"> <DirectionalLayout ohos:id="$+id:ability_main_titlebar" ohos:height="match_content" ohos:width="match_parent" ohos:background_element="#B0B0B0" ohos:orientation="horizontal" ohos:padding="10vp"> <DirectionalLayout ohos:height="match_parent" ohos:width="match_content" ohos:weight="1"> <Text ohos:id="$+id:tag_favorite" ohos:height="match_content" ohos:width="match_parent" ohos:text="关注" ohos:text_size="$float:normal_text_size_15" /> </DirectionalLayout> <DirectionalLayout ohos:height="match_content" ohos:width="match_content" ohos:weight="1"> <Text ohos:id="$+id:tag_support" ohos:height="match_content" ohos:width="match_content" ohos:text="推荐" ohos:text_size="$float:normal_text_size_15" /> </DirectionalLayout> <DirectionalLayout ohos:height="match_content" ohos:width="match_content" ohos:weight="1"> <Text ohos:id="$+id:tag_movie" ohos:height="match_content" ohos:width="match_content" ohos:element_end="$id:favorite" ohos:text="电影" ohos:text_color="#1C6AE9" ohos:text_size="$float:normal_text_size_15" ohos:text_weight="600"/> <Component ohos:id="$+id:device_item_divider" ohos:height="2vp" ohos:width="30vp" ohos:background_element="$graphic:list_divider"/> </DirectionalLayout> <DirectionalLayout ohos:height="match_content" ohos:width="match_content" ohos:weight="1"> <Text ohos:id="$+id:tag_live" ohos:height="match_content" ohos:width="match_parent" ohos:text="直播" ohos:text_size="$float:normal_text_size_15" /> </DirectionalLayout> <DirectionalLayout ohos:height="match_content" ohos:width="match_content" ohos:weight="1"> <Text ohos:id="$+id:tag_tv" ohos:height="match_content" ohos:width="match_parent" ohos:text="电视" ohos:text_size="$float:normal_text_size_15" /> </DirectionalLayout> </DirectionalLayout> <ScrollView ohos:height="match_parent" ohos:width="match_parent" ohos:id="$+id:video_list_scroll" > <ListContainer ohos:id="$+id:videos_container" ohos:height="match_parent" ohos:width="match_parent"> </ListContainer> </ScrollView> </DirectionalLayout>2.2.Java代码
申请用户敏感权限授权 MainAbility.java
/** * Entry to the main interface of the program */ public class MainAbility extends Ability { private static final int REQUEST_CODE = 1; //读写媒体权限 private final String[] permissionLists = new String[]{"ohos.permission.READ_MEDIA", "ohos.permission.WRITE_MEDIA"}; @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); super.setMainRoute(MainAbilitySlice.class.getName()); //申请授权 verifyPermissions(); } private void verifyPermissions() { for (String permissionList : permissionLists) { int result = verifySelfPermission(permissionList); if (result != IBundleManager.PERMISSION_GRANTED) { requestPermissionsFromUser(permissionLists, REQUEST_CODE); } } } ...通过VideoInfoService读取videos.json文件初始化视频容器列表MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { public static final HiLogLabel LABEL = new HiLogLabel(0, 0, "=>MainAbilitySlice"); private VideoInfoService videoService; @Override protected void onStart(Intent intent) { super.onStart(intent); //加载视频播放器页面 super.setUIContent(ResourceTable.Layout_ability_main); initVideoContainer(); } /** * 模拟数据 * 初始化视频容器列表 */ private void initVideoContainer() { HiLog.debug(LABEL, "initVideoContainer"); List<VideoModel> videos = new ArrayList<>(); //获取视频信息服务 videoService = new VideoInfoService(getContext()); for (int i = 0; i < 7; i++) { VideoModel video = new VideoModel(); video.setComments(new Random().nextInt(1000)); video.setFavorites(new Random().nextInt(1000)); video.setGreats(new Random().nextInt(10000)); VideoInfo videoInfo = videoService.getVideoInfoByIndex(i); videoInfo.setIndex(i); video.setVideoInfo(videoInfo); videos.add(video); } ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_videos_container); //容器绑定数据提供程序 VideoItemProvider provider = new VideoItemProvider(this, videos, this); listContainer.setItemProvider(provider); } }视频容器列表数据提供程序 VideoItemProvider.java
public class VideoItemProvider extends BaseItemProvider { private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "=>VideoItemProvider"); private final Context context; private final List<VideoModel> list; private AbilitySlice abilitySlice; //当前播放视频分辨率索引 private int currentPlayingResolutionIndex = 0; /** * Initialization */ public VideoItemProvider(Context context, List<VideoModel> list, AbilitySlice abilitySlice) { HiLog.debug(LABEL, "VideoItemProvider"); this.context = context; this.list = list; this.abilitySlice = abilitySlice; } public Context getContext() { return context; } @Override public int getCount() { return list == null ? 0 : list.size(); } @Override public Object getItem(int position) { if (list != null && position >= 0 && position < list.size()) { return list.get(position); } return new VideoModel(); } @Override public long getItemId(int position) { return position; } @Override public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) { HiLog.debug(LABEL, "getComponent:" + convertComponent + "," + componentContainer); final Component cpt; final VideoPlayerView player; VideoModel item = list.get(position); //初次获取组件 if (convertComponent == null) { cpt = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_hm_sample_ability_video_box, componentContainer, false); player = (VideoPlayerView) cpt.findComponentById(ResourceTable.Id_video_view); //初始化播放器组件 initPlayer(player,item.getVideoInfo()); } else { //ScrollView滑动时也会调用getComponent方法, cpt = convertComponent; player = (VideoPlayerView) cpt.findComponentById(ResourceTable.Id_video_view); } //设置其他组件的值 Text text_favorites = (Text) cpt.findComponentById(ResourceTable.Id_text_favorites); Text text_comments = (Text) cpt.findComponentById(ResourceTable.Id_text_comments); Text text_greats = (Text) cpt.findComponentById(ResourceTable.Id_text_greats); //注意setText的参数类型是字符串 text_favorites.setText(item.getFavorites() + ""); text_comments.setText(item.getComments() + ""); text_greats.setText(item.getGreats() + ""); //点击评论图标,停止当前播放器,打开VideoPlayAbility的Slice页面 Image commentImage = (Image) cpt.findComponentById(ResourceTable.Id_image_comment); commentImage.setClickedListener(component -> { //停止播放,列表页面的播放器 player.stopPlayback(); //打开带有评论的播放页面 startVideoPlayDetail(item,player); }); return cpt; } /** * 初始化播放器 */ private void initPlayer(VideoPlayerView player, VideoInfo videoInfo) { HiLog.debug(LABEL, "initPlayer"); if (player != null) { //视频路径 String path = videoInfo .getResolutions() .get(currentPlayingResolutionIndex) .getUrl(); HiLog.debug(LABEL, "path:" + path); //视频描述 String videoDesc = videoInfo.getVideoDesc(); HiLog.debug(LABEL, "videoDesc = " + videoDesc + " path = " + path); if(path!=null) { //设置路径和名称 player.setVideoPathAndTitle(path, videoDesc); //双击播放或暂停 player.setDoubleClickedListener( component -> { HiLog.debug(LABEL, "VideoPlayView double-click event"); if (player.isPlaying()) { player.pause(); } else { player.start(); } }); //监听播放错误 player.setErrorListener( (errorType, errorCode) -> { ToastDialog toast = new ToastDialog(getContext()); switch (errorType) { case HmPlayerAdapter.ERROR_LOADING_RESOURCE: toast.setText( AppUtil.getStringResource( getContext(), ResourceTable.String_media_file_loading_error)); break; case HmPlayerAdapter.ERROR_INVALID_OPERATION: toast.setText( AppUtil.getStringResource( getContext(), ResourceTable.String_invalid_operation)); break; default: toast.setText( AppUtil.getStringResource( getContext(), ResourceTable.String_undefined_error_type)); break; } abilitySlice.getUITaskDispatcher().asyncDispatch(toast::show); }); } //添加核心组件,播放时间进度滑块 addCoreComponent(player); //添加自定义组件 HiLog.debug(LABEL, "initPlayer finish"); } } /** * 添加播放按钮,剧集,b2b信息网进度条等核心组件 * Adding core component like playback button and seek bar */ private void addCoreComponent(VideoPlayerView player) { HiLog.debug(LABEL, "addCoreComponent"); //添加播放按钮组件 player.addPlaybackButton(new VideoPlayerPlaybackButton(getContext()), VideoBoxArea.BOTTOM); //添加播放进度条组件 player.addSeekBar( new VideoPlayerSlider(getContext()), VideoBoxArea.BOTTOM, (int) AppUtil.getFloatResource(getContext(), ResourceTable.Float_normal_margin_24)); } /** * 打开视频播放详情页 */ private void startVideoPlayDetail(VideoModel item,VideoPlayerView player) { HiLog.debug(LABEL, "startVideoPlayDetail "); //启动评论播放页面 Intent intentService = new Intent(); //播放视频评论数 intentService.setParam("comments",String.valueOf(item.getComments())); VideoInfo videoInfo=item.getVideoInfo(); if(videoInfo!=null) { //播放视频在播放列表中的索引 intentService.setParam("currentPlayingIndex", videoInfo.getIndex()); //播放视频的分辨率索引 intentService.setParam("currentPlayingResolutionIndex", currentPlayingResolutionIndex); //当前视频播放的位置 intentService.setParam(RemoteConstant.INTENT_PARAM_REMOTE_START_POSITION,(int)player.getCurrentPosition()); HiLog.debug(LABEL,RemoteConstant.INTENT_PARAM_REMOTE_START_POSITION+":"+player.getCurrentPosition()); } Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(abilitySlice.getBundleName()) .withAbilityName(VideoPlayAbility.class) .build(); intentService.setOperation(operation); abilitySlice.startAbility(intentService); } }3.实现一个评论功能
3.1.页面布局,评论列表布局页 hm_sample_ability_video_comments.xml
使用了StackLayout,DependentLayout布局组件和ListContainer,TextField,Text 组件
... <!-- 工具栏 --> <DependentLayout ohos:id="$+id:comments_bar" ohos:height="40vp" ohos:width="match_parent" ohos:background_element="#FFDDDADA" ohos:layout_alignment="top" ohos:top_margin="250vp"> <DirectionalLayout ohos:id="$+id:favorite" ohos:height="match_parent" ohos:width="match_content" ohos:align_parent_left="true" ohos:left_margin="10vp" ohos:orientation="horizontal" ohos:padding="4vp"> <Text ohos:id="$+id:text_favorites" ohos:height="match_parent" ohos:width="match_parent" ohos:left_padding="5vp" ohos:text="简介" ohos:text_size="16fp" ohos:text_weight="600"> </Text> </DirectionalLayout> <DirectionalLayout ohos:id="$+id:comment" ohos:height="match_parent" ohos:width="match_content" ohos:end_of="$id:favorite" ohos:left_margin="20vp" ohos:orientation="horizontal" ohos:padding="4vp"> <Text ohos:id="$+id:text_comment" ohos:height="match_parent" ohos:width="match_content" ohos:left_padding="5vp" ohos:text="评论" ohos:text_weight="600" ohos:text_size="16fp"> </Text> <Text ohos:id="$+id:text_comments" ohos:height="match_parent" ohos:width="match_content" ohos:left_padding="5vp" ohos:text="1161" ohos:text_weight="600" ohos:text_size="16fp"> </Text> </DirectionalLayout> </DependentLayout> <!-- 评论列表 --> <DependentLayout ohos:id="$+id:comments_view" ohos:height="match_parent" ohos:width="match_parent" ohos:background_element="#FFFDFFFF" ohos:top_margin="290vp"> <ListContainer ohos:id="$+id:comments_container" ohos:height="match_parent" ohos:width="match_parent" ohos:layout_alignment="horizontal_center" ohos:orientation="vertical"/> </DependentLayout> <!-- 评论对话框 --> <DependentLayout ohos:id="$+id:comments_dialog" ohos:height="70vp" ohos:width="match_parent" ohos:background_element="#FFF4F4F8" ohos:layout_alignment="bottom" ohos:padding="5vp"> <TextField ohos:id="$+id:comment_tf" ohos:height="match_parent" ohos:width="match_parent" ohos:hint="发一条友好的评论吧" ohos:right_padding="60vp" ohos:text_size="16vp"></TextField> <Text ohos:id="$+id:sent_comment" ohos:height="match_parent" ohos:width="60vp" ohos:align_parent_right="true" ohos:background_element="#FF7ECCCF" ohos:text="发送" ohos:input_enter_key_type="enter_key_type_send" ohos:text_alignment="center" ohos:text_size="16vp"> </Text> </DependentLayout>3.2.页面布局,单条评论组件布局 comments_item.xml
使用了DependentLayout,DirectionalLayout布局组件和Image,Component,Text 组件
<?xml version="1.0" encoding="utf-8"?> <DependentLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_content" ohos:width="match_parent"> <Image ohos:id="$+id:header_item_icon" ohos:height="50vp" ohos:width="50vp" ohos:end_margin="13vp" ohos:left_margin="5vp" ohos:image_src="$media:ic_header" ohos:scale_mode="stretch" ohos:top_margin="13vp"/> <DirectionalLayout ohos:id="$+id:comment_item_content" ohos:height="match_content" ohos:width="250vp" ohos:bottom_padding="5vp" ohos:orientation="vertical" ohos:right_of="$id:header_item_icon"> <Text ohos:id="$+id:comment_uname" ohos:height="match_content" ohos:width="160vp" ohos:layout_alignment="vertical_center" ohos:padding="5vp" ohos:text="我是一只鱼" ohos:text_color="#FFA09E9E" ohos:text_size="16vp"/> <Text ohos:id="$+id:comment_content" ohos:height="match_content" ohos:width="match_parent" ohos:layout_alignment="vertical_center" ohos:multiple_lines="true" ohos:padding="5vp" ohos:text="这是一条评论" ohos:text_color="$color:default_black_color" ohos:text_size="16vp"/> <Text ohos:id="$+id:comment_date" ohos:height="match_content" ohos:width="match_content" ohos:layout_alignment="vertical_center" ohos:padding="5vp" ohos:text="2天前" ohos:text_color="#FFA09E9E" ohos:text_size="16vp"/> </DirectionalLayout> <DirectionalLayout ohos:height="match_content" ohos:width="match_content" ohos:id="$+id:goods_view" ohos:align_parent_right="true" ohos:padding="5vp" ohos:right_margin="5vp" ohos:right_of="$id:comment_item_content"> <Image ohos:id="$+id:good_icon" ohos:height="25vp" ohos:width="match_parent" ohos:image_src="$media:ic_great" ohos:scale_mode="stretch" ohos:top_margin="13vp" /> <Text ohos:id="$+id:comment_goods" ohos:height="match_content" ohos:width="match_parent" ohos:text="42" ohos:text_alignment="center" ohos:text_color="#FFA09E9E" ohos:text_size="12vp"></Text> </DirectionalLayout> <DirectionalLayout ohos:height="match_content" ohos:width="match_parent" ohos:align_bottom="$id:comment_item_content"> <Component ohos:id="$+id:device_item_divider" ohos:height="1vp" ohos:width="match_parent" ohos:background_element="$graphic:list_divider"/> </DirectionalLayout> </DependentLayout>3.3.Java代码
创建评论列表提供程序类 CommentItemProvider.java
public class CommentItemProvider extends BaseItemProvider { private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "=>CommentsItemProvider"); private final Context context; private final List<CommentModel> list; private AbilitySlice abilitySlice; /** * Initialization */ public CommentItemProvider(Context context, List<CommentModel> list, AbilitySlice slice) { this.context = context; this.list = list; this.abilitySlice = slice; } @Override public int getCount() { return list == null ? 0 : list.size(); } @Override public Object getItem(int position) { if (list != null && position >= 0 && position < list.size()) { return list.get(position); } return new CommentModel(); } public void addComment(CommentModel comment) { if (comment == null) return; list.add(0, comment); notifyDataSetItemInserted(0); } @Override public long getItemId(int position) { return position; } @Override public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) { final Component cpt; if (convertComponent == null) { cpt = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_comments_item, null, false); } else { cpt = convertComponent; } CommentModel item = list.get(position); //评论人头像 Image headerIcon = (Image) cpt.findComponentById(ResourceTable.Id_header_item_icon); headerIcon.setPixelMap(ResourceTable.Media_ic_header); //评论人名称 Text uName = (Text) cpt.findComponentById(ResourceTable.Id_comment_uname); uName.setText(item.getuName()); //评论内容 Text uContent = (Text) cpt.findComponentById(ResourceTable.Id_comment_content); uContent.setText(item.getCommentContent()); //点击事件 uContent.setClickedListener(component -> { DependentLayout commentDialog = (DependentLayout) abilitySlice.findComponentById(ResourceTable.Id_comments_dialog); if (commentDialog.getVisibility() == Component.VISIBLE) { commentDialog.setVisibility(Component.VISIBLE); } TextField comment = (TextField) abilitySlice.findComponentById(ResourceTable.Id_comment_tf); comment.setText(""); comment.setHint("回复 " + item.getuName() + ":"); comment.requestFocus(); }); //评论日期 Text uDate = (Text) cpt.findComponentById(ResourceTable.Id_comment_date); uDate.setText(item.getCommentDate()); //已赞数 Text goods = (Text) cpt.findComponentById(ResourceTable.Id_comment_goods); HiLog.debug(LABEL, "goods:" + item.getCommentGoods()); goods.setText(item.getCommentGoods() + ""); //点赞 Image greatImage = (Image) cpt.findComponentById(ResourceTable.Id_good_icon); //点赞加一 greatImage.setClickedListener(component1 -> { greatImage.setPixelMap(ResourceTable.Media_ic_great_red); goods.setTextColor(Color.RED); goods.setText((item.getCommentGoods() + 1) + ""); }); if (position == list.size() - 1) { Component divider = cpt.findComponentById(ResourceTable.Id_device_item_divider); divider.setVisibility(Component.INVISIBLE); } return cpt; } }初始化评论列表和评论功能 VideoPlayAbilitySlice.java
/** * 初始化评论列表 */ private void initComments(AbilitySlice slice) { //评论列表,以及设置点击的监听事件、传递数据 ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_comments_container); List<CommentModel> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { CommentModel obj = new CommentModel(); obj.setuId(i + ""); obj.setuName(getRandomUname()); obj.setCommentContent(getRandomText()); obj.setCommentDate("刚刚"); obj.setCommentGoods(new Random().nextInt(100)); list.add(obj); } //容器绑定数据提供程序 provider = new CommentItemProvider(this, list, slice); listContainer.setItemProvider(provider); Component sendComment = findComponentById(ResourceTable.Id_sent_comment); TextField comment = (TextField) findComponentById(ResourceTable.Id_comment_tf); //评论功能 sendComment.setClickedListener(component -> { if (comment.getText().isEmpty()) { new ToastDialog(getContext()).setText("评论不能为空").show(); return; } CommentModel model = new CommentModel(); model.setCommentContent(comment.getText()); model.setuId(UUID.randomUUID().toString()); model.setuName("robot"); model.setCommentDate("刚刚"); //添加到数据提供程序中 provider.addComment(model); comment.setText(""); //评论数+1 commentsText.setText(String.valueOf(Integer.valueOf(comments) + 1)); new ToastDialog(getContext()).setText("评论成功").show(); }); }问题总结
1.播放器列表滑动时,会重复添加播放组件的问题
2.评论功能,输入框跟随输入法调整位置的问题(待完善)
3.播放列表滑动时,根据ScrollView的位置控制视频播放(待完善)
4.播放列表中播放视频进度同步到播放详情页的问题(待完善)
文章相关附件可以点击下面的原文链接前往下载
https://harmonyos.51cto.com/resource/1338
想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com

相关文章
揭秘巨人江湖游戏工会卡的魅力与功能(了解工会卡的一般情况以及如何获取和使用)
摘要:巨人江湖是一款备受欢迎的多人在线角色扮演游戏,而工会卡则是游戏中重要的组织形式之一。本文将为你揭秘工会卡的魅力与功能,帮助你更好地了解如何获取和使用。什么是工会卡?...2025-11-04电脑换屏教程(掌握更换电脑屏幕的关键步骤,轻松解决屏幕问题)
摘要:在使用电脑的过程中,经常会遇到电脑屏幕出现故障的情况。如果你有一定的动手能力,并且对于电子设备有一定的了解,那么你可以尝试自行更换电脑屏幕。本文将以教你电脑换屏的流程为主题,详细介...2025-11-04解除Win10激活的影响及相关问题解析(Win10激活解除对电脑和用户数据的影响及解决方案)
摘要:Windows10是微软公司推出的一款操作系统,用户需要激活后才能正常使用。然而,有时候用户可能需要解除激活,这可能会带来一些问题和影响。本文将探讨解除Win10激活可能产生的影响...2025-11-04「探索剪辑软件世界(「解锁创造力的利器,满足你所有视频剪辑需求」)
摘要:在当今数字时代,视频内容已经成为人们日常生活的重要组成部分。为了创造出令人印象深刻的视频作品,专业的剪辑软件成为不可或缺的利器。本文将为您介绍15款优秀的剪辑软件,帮助您选择适合自...2025-11-04如何安装操作系统到硬盘电脑(从硬件准备到系统安装,轻松装机攻略)
摘要:在购买一台全新的硬盘电脑后,第一步就是安装操作系统。本文将为大家详细介绍如何将操作系统安装到硬盘电脑中,从硬件准备到系统安装的全过程,帮助读者轻松完成装机任务。一、准备工作...2025-11-04- 摘要:三星S1050是一款功能强大的智能手机,拥有多项优秀的特性和出色的性能。本文将深入探讨这款手机的各个方面,包括外观设计、摄影功能、处理器性能、存储容量、电池寿命、安全性能等,帮助读...2025-11-04

最新评论