当前位置:   article > 正文

HarmonyOS实战—影视类卡片应用_harmonyos刷入电视

harmonyos刷入电视

HarmonyOS实战

影视类卡片应用实战

在前面,博主详细介绍了HarmonyOS原子化服务的优点,通过卡片式操作,我们可以快速找到我们想要的功能模块。
最终效果图

而在实际的用户体验中,比如影视类用户,有这么一群人,他们只关注最近的影视剧,而不在乎以前的影视剧。

这种情况下,普通的视频应用功能太过多,往往并不方便这些用户的查找。我们可以通过原子化服务创建一个近一个月的影视剧服务,来满足这群用户。

最终实现效果动图
这样,用户在鸿蒙手机上打开卡片就能快速浏览近一个月的视频,而不必去搜索框打字找寻,或者翻页查找,大大方便了用户的体验效果。(顺便去掉广告哦)

那么,今天博主就通过原子化服务开发一个影视剧类的App服务。

swiper选项卡推荐功能

首先,对于大多数的影视剧App来说,其顶部都有一个swiper组件,也就是自动浏览推荐窗口,推荐大家视频。

所以,我们需要通过swiper组件来实现影视剧的推荐。index.hml代码如下:

<div class="container">
    <swiper class="swiper" id="swiper" index="{{ swiper_index }}" indicator="true" duration="1000" autoplay="true" loop="true">
        <div for="{{responseData.vipList}}" onclick="swiper_onClick({{$item}})" data="{{$item}}">
            <image class="swiper_image" src="{{$item.img_url}}"/>
            <text class="swiper_text">{{$item.title}}</text>
        </div>
    </swiper>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其中,有3个数据需要注意:

数据意义
responseData.vipList网络获取的JSON数据(下面会介绍网络数据的获取)
swiper_onClick用户点击肯定是需要观看视频的,这是一个点击事件跳转到视频详情页面
autoplay与loop自动循环播放swiper推荐

运行之后,显示的效果如下:
实现效果

网络数据请求

在鸿蒙开发中,网络数据请求非常简单,主要会用到@system.fetch模块。所以在Js文件中,进行网络请求时,先导入该模块。(index.js)

import fetch from '@system.fetch';
export default {
    data: {
        title: "",
        url: "https://liyuanjing-1300376177.cos.ap-shanghai.myqcloud.com/viptopmodel.json",
        responseData: '正在加载中',
        swiper_index: 0,
    },
    //界面初始化网络JSON数据获取
    fetch: function () {
        var that = this;
        fetch.fetch({
            url: that.url,
            success: function (response) {
                console.info("网络请求成功");
                that.responseData = JSON.parse(response.data);
                console.info(that.responseData.vipList[0].title);
            },
            fail: function () {
                console.info("网络请求错误");
            }
        });
    },
    onInit() {
        this.fetch();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

如上面代码所示,获取网络数据的主要方法是fetch()。而因为界面首先肯定需要数据,所以必须在onInit()方法中就开始调用。

页面路由

当用户点击了swiper组件中的一个图片之后,肯定会跳转到该图片对应的视频,那么我们就需要用到鸿蒙页面路由的相关知识。示例如下(index.js):

import router from '@system.router';
export default {
	//路由跳转
    jump_router:function(item){
        router.push({
            uri: 'pages/index/playvideo/playvideo',
            params: {
                data1: encodeURIComponent(item.img_url),
                data2: encodeURIComponent(item.mp4_url),
                data3: encodeURIComponent(item.title),
            },
        });
    },
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

如上面代码所示,我们通过router.push()进行路由的跳转。当然,这里也要用到路由跳转的模块,需要先导入。

同时,因为我们的视频播放组件<video>需要2个基本的参数,也就是视频链接与视频显示的封面图,所以我们需要传递3个数据:标题,图片链接,视频链接。

标题是跳转路由详情页面需要用到的数据,并不是<video>的数据,但同样也需要。

不过,我们这里并没有跳转路由,因为swiper组件的点击事件是swiper_onClick(),我们需要通过该函数来跳转路由。代码如下:

export default {
	//滑动模块点击事件
    swiper_onClick: function (item) {
        this.jump_router(item);
    },
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里,之所以把跳转路由的代码独立出来,是为了减少代码的冗余,后面的list列表以及grid网格都是上面的路由跳转代码。

list列表横向视频推荐

接着,我们需要实现横向的list视频列表。读者可以自行随便打开一个影视类App,下面跟着的是不是一个横向的视频推荐模块呢?具体效果如下:

横向list
在鸿蒙原子化服务开发中,也提供了list列表组件。我们可以直接通过它来实现该样式与功能。代码如下所示(index.hml):

<div class="container">
	<!--上面的swiper代码-->
	<div style="flex-direction: row;height: 40px;">
        <text class="divi_text">猜你会追 ></text>
        <marquee class="divi_marquee">精彩的内容等着你来寻找</marquee>
    </div>
    <list class="list">
        <list-item id="listItem" for="{{responseData.vipList}}" class="list_item" onclick="list_item_onclick({{$item}})" data="{{$item}}">
            <image class="list_item_image" src="{{$item.img_url}}"/>
            <text class="list_item_text">{{$item.title}}</text>
        </list-item>
    </list>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这里的list列表很好理解,基本上与swiper组件的代码类似。需要特别注意的是我们list列表上面还有一个模块。

也就是横排文字推荐,左边的只是一个文本,而右边是一个横向滚动的文本组件<marquee>。

这里的list列表同样需要通过list_item_onclick()方法进行路由页面的跳转。具体的代码如下所示(index.js):

export default {
	//list列表点击事件
    list_item_onclick:function(item){
        this.jump_router(item);
    },
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

因为上面的路由代码已经写了,所以这里实现起来就比较简单了,只需要定义一个函数,通过jump_router()方法调用即可。

这里list列表还是使用的同一样Json数据,但原理是一样的,如果你有其他的视频接口数据,可以自行改变网址。

grid网格视频推荐

有木有发现,主流的视频App,其首页基本都是一样的。先来一个swiper组件,再是一个横向的list列表,接着就是一个grid网格视频推荐。

所以,我们这里需要实现鸿蒙系统的网格效果,鸿蒙原子化服务开发给我们提供的网格组件是:grid-container。示例如下(index.hml):

<div class="container">
	<!--上面的list,swiper代码-->
	<div style="flex-direction: row;height: 40px;">
        <text class="divi_text">重磅推荐 ></text>
        <marquee class="divi_marquee">精彩的内容等着你来寻找</marquee>
    </div>
    <grid-container for="{{responseData.vipList}}" columns="3" style="margin-top: 10px;">
        <grid-row if="{{parseInt($item.uid)%2==0}}" class="grid-row">
            <grid-col class="grid-col" span="2" onclick="grid_onclick({{$item}})">
                <image class="grid-col_image" src="{{$item.img_url}}"/>
                <text class="grid-col_text">{{$item.title}}</text>
            </grid-col>
            <grid-col class="grid-col" span="2" onclick="grid_onclick({{$item}})">
                <image class="grid-col_image" src="{{$item.img_url}}" />
                <text class="grid-col_text">{{$item.title}}</text>
            </grid-col>
        </grid-row>
    </grid-container>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

运行之后,效果如下所示:
网格实现
对于鸿蒙的网格组件grid-container,使用的同样是上面的JSON数据,它的点击事件是grid_onclick()方法。实现代码如下(index.js):

export default {
	//网格点击事件
    grid_onclick:function(item){
        this.jump_router(item);
    },
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

到这里,我们的影视类卡片应用的主页基本上都实现完成了。当然,对于视频类App来说,这整个页面都是可以下拉刷新的。

这会用到refresh组件,实现起来其实很简单,无非就是再调用一次fetch()函数获取一次新的json数据即可。(下次实战讲解,本篇内容有点多)

样式文件(index.css)

当然,上面那么多代码之所以搭配起来是前文显示的效果,多亏了css文件的样式调配。这里,我们就直接上代码了:

.container {
    flex-direction: column;
    margin: 10px;
}

.divi_text{
    position: absolute;
    font-size: 20px;
    margin-top: 10px;
    color: darkorange;
    margin-left: 10px;
    left: 0;
}

.divi_marquee{
    width: 130px;
    position: absolute;
    right: 0;
    font-size: 20px;
    color: darkorange;
    margin-top: 10px;
}

.list {
    font-size: 40px;
    height: 100px;
    color: #000000;
    opacity: 0.9;
    flex-direction: row;
}

.list_item{
    flex-direction: column;
    align-content: center;
    margin-top: 10px;
    margin-right: 10px;
}

.list_item_text{
    font-size: 15px;
    color: red;
    margin-top: 5px;
    align-content: center;
}

.list_item_image{
    width: 150px;
    height: 75px;
    border-radius:5px;
}

.swiper{
    height: 200px;
    indicator-bottom: 20px;
    indicator-right: 30px;
    indicator-color: #cf2411;
    indicator-size: 14px;
}

.swiper_image{
    width: 100%;
    border-radius:25px;
}

.swiper_text{
    position:absolute;
    margin-bottom: 20px;
    margin-left: 10px;
    font-size: 20px;
    color: darkturquoise;
    left:0;
    bottom:0;
}

.grid-row{
    height:100px;
    margin-left: -15px;
}

.grid-col{
    flex-direction: column;
    display: flex;
    margin-right: 10px;
    margin-left: -10px;
}
.grid-col_text{
    font-size: 15px;
    color: red;
}
.grid-col_image{
    width: 150px;
    height: 75px;
    border-radius:5px;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

视频播放页面

主页我们已经完全实现了。下面,需要实现点击这些视频展示图片跳转的视频播放页面。我们先来看看其最终的效果图:

效果图

页面(pagevideo.hml)

这里,我们先剖析一下这个界面有哪些东西?

  1. 顶部的视频播放组件:<video>
  2. Vip会员橙色按钮:<button>
  3. 图像金字塔以及视频的简介:标题<text>、评分<text>、vip<image>、播放量以及简介<text>。
  4. 点赞,喜欢,下载,搜藏:4个<image>。
  5. 专辑列表:<text>
  6. 其他推荐列表:<list>

既然,我们已经分析出来了界面的布局,那么直接上代码即可。示例如下:

<div style="flex-direction : column;">
    <video id='videoId' src='{{ mp4_url }}' muted='false' autoplay='false' poster='{{ url }}' controls="true"
           onprepared='preparedCallback' onstart='startCallback' onpause='pauseCallback' onfinish='finishCallback'
           onerror='errorCallback' onseeking='seekingCallback' onseeked='seekedCallback'
           ontimeupdate='timeupdateCallback' style="object-fit : fill; width : 100%; height : 250px;"
           onlongpress='change_fullscreenchange' onclick="change_start_pause"/>
    <div class="div_list">
        <button class="vip_button">VIP会员低至30元每季度,618狂欢进行中</button>
        <text class="text_title">{{ title }}</text>
        <div class="div_row">
            <text class="div_row_text">7.5分</text>
            <image class="div_row_image" src="../../../common/images/vip.png"/>
            <text class="div_row_text">2333.3万次播放 · 简介 ></text>
        </div>
        <div class="div_row">
            <image class="div_row2_image" src="../../../common/images/dianzan.png"/>
            <div class="div_row2">
                <image class="div_row2_image" src="../../../common/images/like.png"/>
                <image class="div_row2_image" src="../../../common/images/download.png"/>
                <image class="div_row2_image" src="../../../common/images/share.png"/>
            </div>
        </div>
        <text style="font-size : 15px; font-weight : bold;margin-top: 10px;">专辑列表</text>
        <div class="container">
            <list class="list" divider="true">
                <list-item for="{{responseData.vipList}}" class="list_item">
                    <image class="list_image" src="{{$item.img_url}}"/>
                    <text class="list_title">{{$item.title}}</text>
                </list-item>
            </list>
        </div>
    </div>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

用户交互(pagevideo.js)

从上面的界面中,我们除了返现各种搭配的组件之外。还可以明显看到,专辑列表的视频推荐是从网络获取的,视频的播放交互,需要JS操作。

而且,最重要的是我们从前面主页传递过来的参数,如何获取呢?代码如下所示:

import fetch from '@system.fetch';
import router from '@system.router';

export default {
    data: {
        title: 'World',
        url: "",
        mp4_url: "",
        event: '',
        seekingtime: '',
        timeupdatetime: '',
        seekedtime: '',
        isStart: true,
        isfullscreenchange: false,
        duration: '',
        responseData:'正在加载中',
    },
    seekingCallback: function (e) {
        this.seekingtime = e.currenttime;
    },
    timeupdateCallback: function (e) {
        this.timeupdatetime = e.currenttime;
    },
    change_start_pause: function () {
        if (this.isStart) {
            this.$element('vedioId').pause();
            this.isStart = false;
        } else {
            this.$element('vedioId').start();
            this.isStart = true;
        }
    },
    change_fullscreenchange: function () { //全屏
        if (!this.isfullscreenchange) {
            this.$element('videoId').requestFullscreen({
                screenOrientation: 'default'
            });
            this.isfullscreenchange = true;
        } else {
            this.$element('vedioId').exitFullscreen();
            this.isfullscreenchange = false;
        }
    },
    fetch: function () {
        var that = this;
        fetch.fetch({
            url: 'https://liyuanjing-1300376177.cos.ap-shanghai.myqcloud.com/viptopmodel.json',
            success: function (response) {
                console.info("网络请求成功");
                that.responseData = JSON.parse(response.data);
                console.info(that.responseData.vipList[0].title);
            },
            fail: function () {
                console.info("网络请求错误");
            }
        });
    },
    onInit() {
        this.url = decodeURIComponent(this.data1);
        this.mp4_url = decodeURIComponent(this.data2);
        this.title= decodeURIComponent(this.data3);
        this.fetch();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

可以发现,鸿蒙开发获取参数非常简单,你怎么传递的参数,就是什么名字,这里就不在赘述了。

界面样式(pagevideo.css)

最后,就是我们的视频播放页面的界面样式。示例代码如下所示:

.div_list {
    flex-direction: column;
    margin: 10px;
}

.div_row {
    flex-direction: row;
    margin-top: 5px;
}

.div_row_text {
    font-size: 10px;
    font-family: sans-serif;
    color: darkgrey;
}

.div_row_image {
    width: 20px;
    height: 20px;
    margin-left: 10px;
    margin-right: 10px;
}

.div_row2 {
    flex-direction: row;
    position: absolute;
    right: 0;
}

.div_row2_image {
    width: 30px;
    height: 30px;
    margin-left: 10px;
    margin-right: 20px;
    margin-left: -2px;
    left: 0;
}

.text_title {
    font-size: 20px;
    font-family: sans-serif;
    font-weight: bold;
    margin-top: 10px;
}

.vip_button {
    background-color: darkorange;
    font-size: 20px;
    font-family: sans-serif;
    color: aqua;
    border-radius: 0px;
    box-shadow: 2px 2px 5px 2px #FF7F00;
    padding: 10px;
}

.container {
    display: flex;
    justify-content: center;
    align-items: center;
    left: 0px;
    top: 0px;
    width: 454px;
    height: 454px;
}

.list {
    width: 100%;
    margin-top: 10px;
    divider-color: #32CD99;
}

.list_item {
    width: 100%;
    flex-direction: row;
    margin-top: 5px;
    padding-bottom: 5px;
}

.list_title {
    text-align: center;
    height: 100px;
    font-size: 15px;
    font-weight: bold;
    color: coral;
    margin-left: 10px;
}

.list_image {
    width: 200px;
    height: 100px;
    border-radius: 5px;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

其他设置

默认应用是不支持网络请求的,也就是说,纯粹使用上面的代码。肯定会报错,那么我们如何赋予应用网络权限呢?

只需要在config.json文件中,添加如下代码即可:

"module": {
    "reqPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ],
    "metaData": {
      "customizeData": [
        {
          "name": "hwc-theme",
          "value": "androidhwext:style/Theme.Emui.Light.NoTitleBar"
        }
      ]
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

其中,reqPermissions是用于添加权限的,这里我们添加了ohos.permission.INTERNET网络权限。

还有默认应用创建都是有标题栏的,这里没有标题栏是因为博主设置了metaData样式,这里设置为无标题栏NoTitleBar。

本项目源代码文件下载:点击下载

本文正在参与“有奖征文 | HarmonyOS征文大赛”活动:

活动链接:https://marketing.csdn.net/p/ad3879b53f4b8b31db27382b5fc65bbc

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/337689
推荐阅读
相关标签
  

闽ICP备14008679号