在HarmonyOS NEXT开发环境中,我们可以使用多种组件和库来构建丰富且交互友好的应用。本文将展示如何使用HarmonyOS NEXT框架和
nutpi/axios
库,从零开始实现一个简单的影视APP的首页,主要关注最近上映电影的滚动展示及加载更多功能的实现。
安装nutpi/axios
首先,我们需要在项目中安装nutpi/axios
库。
ohpm install @ohos/axios
实现最近上映电影接口
我们使用nutpi/axios
库来实现最近上映电影的接口请求。接口请求如下:
import {axiosClient,HttpPromise} from '../../utils/axiosClient';
import { HotMovieReq, MovieRespData, SwiperData } from '../bean/ApiTypes';
// 1.获取轮播图接口
export const getSwiperData = (): HttpPromise<SwiperData> => axiosClient.get({url:'/swiperdata'});
// 2.获取即将上映影视接口
export const getSoonMovie = (start:number,count:number): HttpPromise<MovieRespData> => axiosClient.post({url:'/soonmovie',data: { start:start,count:count }});
// 3.获取热门影视接口
export const getHotMovie = (req:HotMovieReq): HttpPromise<MovieRespData> => axiosClient.post({url:'/hotmovie',data:req});
代码讲解
- 创建axios客户端:我们创建了一个axios客户端
axiosClient
,并设置了基础URL和请求超时时间。 - 定义接口函数:
getSoonMovies
函数向接口发送GET请求以获取最近上映的电影数据,并返回一个Promise对象。
实现首页组件
接下来,我们将实现首页组件,包括最近上映电影的滚动展示和加载更多功能。
import { getSoonMovies } from '../../common/api/movie';
import { MovieItem } from '../../common/bean/MovieItem';
import { Log } from '../../utils/logutil';
import { NavPathStack } from '@ohos.router';
import { BusinessError } from '@kit.BasicServicesKit';
@Builder
export function HomePageBuilder() {
HomePage()
}
@Component
struct HomePage {
pageStack: NavPathStack = new NavPathStack()
@State soonMvList: MovieItem[] = []
// 组件生命周期
aboutToAppear() {
Log.info('HomePage aboutToAppear');
this.fetchSoonMovies();
}
// 获取最近上映电影数据
fetchSoonMovies() {
getSoonMovies().then((res) => {
Log.debug(res.data.message)
Log.debug("request", "res.data.code:%{public}d", res.data.code)
if (res.data.code == 0) {
this.soonMvList = res.data.data
}
})
.catch((err: BusinessError) => {
Log.debug("request", "err.data.code:%d", err.code)
Log.debug("request", err.message)
});
}
build() {
Column({ space: 0 }) {
Flex({
direction: FlexDirection.Row,
justifyContent: FlexAlign.SpaceBetween,
alignContent: FlexAlign.SpaceBetween
}) {
Text('即将上映').fontWeight(FontWeight.Bold)
Text('加载更多 >>').fontSize(14).fontColor(Color.Black)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieMorePage", { types: 1 }).catch((e: Error) => {
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(() => {
// 跳转成功
});
})
}.padding(10)
Scroll() {
Row({ space: 5 }) {
ForEach(this.soonMvList, (item: MovieItem) => {
Column({ space: 0 }) {
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieDetailPage", { id: item.id }).catch((e: Error) => {
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(() => {
// 跳转成功
});
})
Text(item.title)
.alignSelf(ItemAlign.Center)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width(100)
.fontSize(14).fontWeight(FontWeight.Bold).padding(10)
}.justifyContent(FlexAlign.Center)
}, (itm: MovieItem) => itm.id)
}
}.scrollable(ScrollDirection.Horizontal)
}
.width('100%')
.height('100%')
}
}
代码讲解
- 导入模块:我们导入了之前定义的
getSoonMovies
函数,以及一些其他必要的模块。 - 定义组件状态:
soonMvList
:用于存储最近上映电影的数据。
- 组件生命周期:
aboutToAppear
:组件即将出现在页面时执行的日志记录,并调用fetchSoonMovies
函数获取数据。
- 构建页面:
Column
:垂直布局容器。Flex
:弹性布局容器,用于布局“即将上映”和“加载更多”按钮。Text('即将上映')
:显示“即将上映”标题。Text('加载更多 >>')
:显示“加载更多”按钮,并设置点击事件,点击时导航到加载更多页面。
Scroll
:滚动容器,设置为水平滚动。Row
:水平布局容器,用于放置电影项。ForEach
:遍历soonMvList
数组,为每个电影项创建一个Column
。Column
:垂直布局容器,用于放置电影的图片和标题。Image
:显示电影封面图片,并设置点击事件,点击时导航到电影详情页面。Text
:显示电影标题,并设置样式。
配置路由
为了实现页面跳转,我们需要在entry/src/main/resources/base/profile
路径下的route_map.json
文件中配置路由。
{
"routerMap": [
{
"name": "HomePage",
"pageSourceFile": "src/main/ets/pages/home/Home.ets",
"buildFunction": "HomePageBuilder",
"data": {
"description": "this is HomePage"
}
},
{
"name": "MovieMorePage",
"pageSourceFile": "src/main/ets/pages/home/MovieMore.ets",
"buildFunction": "MovieMorePageBuilder",
"data": {
"description": "this is MovieMorePage"
}
},
{
"name": "MovieDetailPage",
"pageSourceFile": "src/main/ets/pages/home/Detail.ets",
"buildFunction": "MovieDetailPageBuilder",
"data": {
"description": "this is MovieDetailPage"
}
}
]
}
代码讲解
- 配置路由:
HomePage
:配置首页的路由信息。MovieMorePage
:配置加载更多页面的路由信息。MovieDetailPage
:配置电影详情页面的路由信息。
涉及到的常用组件
Flex
Flex
组件用于创建弹性布局容器,可以控制子组件的排列方式和对齐方式。
Flex({
direction: FlexDirection.Row,
justifyContent: FlexAlign.SpaceBetween,
alignContent: FlexAlign.SpaceBetween
}) {
Text('即将上映').fontWeight(FontWeight.Bold)
Text('加载更多 >>').fontSize(14).fontColor(Color.Black)
}
Scroll
Scroll
组件用于创建可滚动的容器,可以设置滚动方向为水平或垂直。
Scroll() {
Row({ space: 5 }) {
ForEach(this.soonMvList, (item: MovieItem) => {
Column({ space: 0 }) {
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
}
}, (itm: MovieItem) => itm.id)
}
}.scrollable(ScrollDirection.Horizontal)
ForEach
ForEach
组件用于遍历数组并为每个元素生成一个子组件。
ForEach(this.soonMvList, (item: MovieItem) => {
Column({ space: 0 }) {
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
}
}, (itm: MovieItem) => itm.id)
Image
Image
组件用于显示图片,可以设置图片的显示方式、大小和形状。
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
Text
Text
组件用于显示文本,可以设置文本的大小、颜色、对齐方式等。
Text(item.title).fontSize(14).fontWeight(FontWeight.Bold).padding(10)
Column
和 Row
Column
和Row
组件分别用于创建垂直和水平布局容器。
Column({ space: 0 }) {
// 子组件
}
Row({ space: 5 }) {
// 子组件
}
onClick
onClick
事件用于处理组件的点击事件。
Text('加载更多 >>').fontSize(14).fontColor(Color.Black)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieMorePage", { types: 1 }).catch((e: Error) => {
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(() => {
// 跳转成功
});
})
总结
通过本文,我们展示了如何使用HarmonyOS NEXT框架和nutpi/axios
库来实现一个简单的影视APP的首页,包括最近上映电影的滚动展示和加载更多功能。nutpi/axios
库的使用大大简化了网络请求的操作,使代码更加简洁易读。同时,我们还介绍了涉及到的常用组件的使用方法。
希望这篇文章对你有所帮助,让你在开发HarmonyOS NEXT应用时更加得心应手。如果你有任何问题或建议,欢迎在评论区留言交流!
参考代码
以下是完整的HomePage
组件代码:
import { Log } from '../../utils/logutil';
import { BusinessError } from '@kit.BasicServicesKit';
import { router } from '@kit.ArkUI';
import { MovieItem, SwiperItem } from '../../common/bean/ApiTypes';
import { getHotMovie, getHotTv,
getMvTop250,
getNewMovie,
getPiaoMovie,
getSoonMovie, getSwiperData, getUsHotMv } from '../../common/api/movie';
import { PiaoFangRespData } from '../../common/bean/PiaoFangResp';
class BasicDataSource<T> implements IDataSource {
private listeners: DataChangeListener[] = [];
private originDataArray: T[] = [];
totalCount(): number {
return this.originDataArray.length;
}
getData(index: number): T {
return this.originDataArray[index];
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.slice(pos, 1);
}
}
// 通知LazyForEach组件需要重新重载所有子组件
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
// 通知LazyForEach组件需要在index对应索引处添加子组件
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
}
class SwiperDataSource<T> extends BasicDataSource<T> {
private dataArray: T[] = [];
totalCount(): number {
return this.dataArray.length;
}
getData(index: number): T {
return this.dataArray[index];
}
// 在列表末尾添加数据并通知监听器
pushData(data: T): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
// 重载数据
reloadData(): void {
// 不会引起状态变化
this.dataArray = [];
// 必须通过DataChangeListener来更新
this.notifyDataReload();
}
}
@Component
export default struct Home {
@Consume pageStack: NavPathStack
private swiperController: SwiperController = new SwiperController()
private swiperData: SwiperDataSource<SwiperItem> = new SwiperDataSource()
@State soonMvList:MovieItem[]=[]
@State hotMvList:MovieItem[]=[]
@State hotTvList:MovieItem[]=[]
@State usMvList:MovieItem[]=[]
@State topMvList:MovieItem[]=[]
@State piaoList:PiaoFangRespData[]=[]
// 组件生命周期
aboutToAppear() {
Log.info('Home aboutToAppear');
getSwiperData().then((res) => {
Log.debug(res.data.message)
Log.debug("request","res.data.code:%{public}d",res.data.code)
for (const itm of res.data.data) {
this.swiperData.pushData(itm)
}
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
getPiaoMovie().then((res) => {
Log.debug(res.data.message)
for (const itm of res.data.data) {
this.piaoList.push(itm)
}
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
getSoonMovie(1,10).then((res) => {
Log.debug(res.data.message)
Log.debug("request","res.data.code:%{public}d",res.data.code)
for (const itm of res.data.data) {
this.soonMvList.push(itm)
}
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
getHotMovie({start:1,count:10,city:'郑州'}).then((res) => {
Log.debug(res.data.message)
Log.debug("request","res.data.code:%{public}d",res.data.code)
for (const itm of res.data.data) {
this.hotMvList.push(itm)
}
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
getHotTv(1,10).then((res) => {
Log.debug(res.data.message)
Log.debug("request","res.data.code:%{public}d",res.data.code)
for (const itm of res.data.data) {
this.hotTvList.push(itm)
}
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
getUsHotMv(1,10).then((res) => {
Log.debug(res.data.message)
Log.debug("request","res.data.code:%{public}d",res.data.code)
for (const itm of res.data.data) {
this.usMvList.push(itm)
}
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
getMvTop250(1,10).then((res) => {
Log.debug(res.data.message)
for (const itm of res.data.data) {
this.topMvList.push(itm)
}
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
}
// 组件生命周期
aboutToDisappear() {
Log.info('Home aboutToDisappear');
}
build() {
Scroll() {
Column({ space: 0 }) {
//title
Flex({
direction: FlexDirection.Row,
justifyContent: FlexAlign.SpaceBetween,
alignContent: FlexAlign.SpaceBetween
})
{
Blank().width(40)
Text('爱影家').fontSize(26).fontWeight(FontWeight.Bold)
Image($r('app.media.search')).width(42).height(42).padding({bottom:8}).onClick(() => {
this.pageStack.pushDestinationByName("SearchPage", { }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
}
.padding(10)
.width('100%').height(50)
// 轮播图
Swiper(this.swiperController) {
LazyForEach(this.swiperData, (item: SwiperItem) => {
Stack({ alignContent: Alignment.Center }) {
Image(item.imageUrl)
.width('100%')
.height(180)
.zIndex(1)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
// 显示轮播图标题
Text(item.title)
.padding(5)
.margin({ top: 135 })
.width('100%')
.height(60)
.textAlign(TextAlign.Center)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Clip })
.fontSize(22)
.fontColor(Color.White)
.opacity(100)// 设置标题的透明度 不透明度设为100%,表示完全不透明
.backgroundColor('#808080AA')// 背景颜色设为透明
.zIndex(2)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
}
}, (item: SwiperItem) => item.id)
}
.cachedCount(2)
.index(1)
.autoPlay(true)
.interval(4000)
.loop(true)
.indicatorInteractive(true)
.duration(1000)
.itemSpace(0)
.curve(Curve.Linear)
.onChange((index: number) => {
console.info(index.toString())
})
.onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
console.info("index: " + index)
console.info("current offset: " + extraInfo.currentOffset)
})
.height(180) // 设置高度
Text('今日票房').fontWeight(FontWeight.Bold).padding(10).alignSelf(ItemAlign.Start)
Scroll() {
Row({ space: 5 }) {
ForEach(this.piaoList, (item: PiaoFangRespData,idx) => {
Column({ space: 2 }) {
Text(idx.toString()).fontSize(20).fontColor(Color.Orange)
Text(item.name)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontSize(14).fontWeight(FontWeight.Bold).alignSelf(ItemAlign.Center).padding(5)
Text(item.release_date).fontSize(12).fontColor(Color.White)
.alignSelf(ItemAlign.Center)
Text('票房:'+item.box_million).fontSize(12).fontColor(Color.White)
.alignSelf(ItemAlign.Center)
Text('占比:'+item.share_box).fontSize(12).fontColor(Color.White)
.alignSelf(ItemAlign.Center)
}.width(120).height(120).backgroundColor('rgba(85, 170, 255, 0.60)').borderRadius(5).justifyContent(FlexAlign.Center)
}, (itm: PiaoFangRespData, idx) => itm.name)
}
}.scrollable(ScrollDirection.Horizontal)
Flex({
direction: FlexDirection.Row,
justifyContent: FlexAlign.SpaceBetween,
alignContent: FlexAlign.SpaceBetween
}) {
Text('即将上映').fontWeight(FontWeight.Bold)
Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
this.pageStack.pushDestinationByName("MovieMorePage", {types:1}).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
}.padding(10)
Scroll() {
Row({ space: 5 }) {
ForEach(this.soonMvList, (item: MovieItem) => {
Column({ space: 0 }) {
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
Text(item.title)
.alignSelf(ItemAlign.Center)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width(100)
.fontSize(14).fontWeight(FontWeight.Bold).padding(10)
}.justifyContent(FlexAlign.Center)
}, (itm: MovieItem, idx) => itm.id)
}
}.scrollable(ScrollDirection.Horizontal)
Flex({
direction: FlexDirection.Row,
justifyContent: FlexAlign.SpaceBetween,
alignContent: FlexAlign.SpaceBetween
}) {
Text('热映电影').fontWeight(FontWeight.Bold)
Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
this.pageStack.pushDestinationByName("MovieMorePage", {types:2}).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
}.padding(10)
Scroll() {
Row({ space: 5 }) {
ForEach(this.hotMvList, (item: MovieItem) => {
Column({ space: 0 }) {
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
Text(item.title)
.alignSelf(ItemAlign.Center)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width(100)
.fontSize(14).fontWeight(FontWeight.Bold).padding(10)
}.justifyContent(FlexAlign.Center)
}, (itm: MovieItem, idx) => itm.id)
}
}.scrollable(ScrollDirection.Horizontal)
// Flex({
// direction: FlexDirection.Row,
// justifyContent: FlexAlign.SpaceBetween,
// alignContent: FlexAlign.SpaceBetween
// }) {
// Text('热门资讯').fontWeight(FontWeight.Bold)
// Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
//
// })
// }.padding(10)
Flex({
direction: FlexDirection.Row,
justifyContent: FlexAlign.SpaceBetween,
alignContent: FlexAlign.SpaceBetween
}) {
Text('热门剧集').fontWeight(FontWeight.Bold)
Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
this.pageStack.pushDestinationByName("MovieMorePage", {types:3}).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
}.padding(10)
Scroll() {
Row({ space: 5 }) {
ForEach(this.hotTvList, (item: MovieItem) => {
Column({ space: 0 }) {
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
Text(item.title)
.alignSelf(ItemAlign.Center)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width(100)
.fontSize(14).fontWeight(FontWeight.Bold).padding(10)
}.justifyContent(FlexAlign.Center)
}, (itm: MovieItem, idx) => itm.id)
}
}.scrollable(ScrollDirection.Horizontal)
Flex({
direction: FlexDirection.Row,
justifyContent: FlexAlign.SpaceBetween,
alignContent: FlexAlign.SpaceBetween
}) {
Text('豆瓣排行榜').fontWeight(FontWeight.Bold)
Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
this.pageStack.pushDestinationByName("MovieMorePage", {types:4}).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
}.padding(10)
Scroll() {
Row({ space: 5 }) {
ForEach(this.topMvList, (item: MovieItem) => {
Column({ space: 0 }) {
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
Text(item.title)
.alignSelf(ItemAlign.Center)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width(100)
.fontSize(14).fontWeight(FontWeight.Bold).padding(10)
}.justifyContent(FlexAlign.Center)
}, (itm: MovieItem, idx) => itm.id)
}
}.scrollable(ScrollDirection.Horizontal)
Flex({
direction: FlexDirection.Row,
justifyContent: FlexAlign.SpaceBetween,
alignContent: FlexAlign.SpaceBetween
}) {
Text('北美票房榜').fontWeight(FontWeight.Bold)
Text('加载更多 >>').fontSize(14).fontColor(Color.Black) .onClick(() => {
this.pageStack.pushDestinationByName("MovieMorePage", {types:5}).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
}.padding(10)
Scroll() {
Row({ space: 5 }) {
ForEach(this.usMvList, (item: MovieItem) => {
Column({ space: 0 }) {
Image(item.cover).objectFit(ImageFit.Auto).height(160).borderRadius(5)
.onClick(() => {
this.pageStack.pushDestinationByName("MovieDetailPage", { id:item.id }).catch((e:Error)=>{
// 跳转失败,会返回错误码及错误信息
console.log(`catch exception: ${JSON.stringify(e)}`)
}).then(()=>{
// 跳转成功
});
})
Text(item.title)
.alignSelf(ItemAlign.Center)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.width(100)
.fontSize(14).fontWeight(FontWeight.Bold).padding(10)
}.justifyContent(FlexAlign.Center)
}, (itm: MovieItem, idx) => itm.id)
}
}.scrollable(ScrollDirection.Horizontal)
}.width('100%')
}
.width('100%').height('100%')
.scrollable(ScrollDirection.Vertical)
}
}