前言;
各位同学大家好之前写了一些基于 OpenHarmony 系统写arkui的项目。所以移植到arkui-x上面来
效果图
-
OpenHarmony os 设备效果图 :
-
安卓设备效果图
我们可以看到arkui-x的项目可以直接在OpenHarmony 的设备上面直接运行而且我们对比开发者选项的显示布局边界.akrui-x的项目使用arkts 开发对于OpenHarmony 来说就是原生开发的 但是在安卓 和iOS 上面来说就是类似flutter一样是一个跨平台框架那么我们在使用此框架的时候 只需要编写一次代码就可以在 安卓iOS OpenHarmony 上面直接运行
具体实现:
-
顶部导航
// @ts-nocheck
import { TopBar } from '../view/common/TopBar';
import { PageAll } from '../view/tabcontent/PageAll';
import { CommonConstants } from '../common/constants/CommonConstant';
import { PageEntertainment } from '../view/tabcontent/PageEntertainment';
import { PageMovie } from '../view/tabcontent/PageMovie';
import PageAnime, {PageAnime} from '../view/tabcontent/PageAnime';
import PageTV, { PageTV } from '../view/tabcontent/PageTV';
/**
* 创建人:xuqing
* 创建时间:2023年8月6日14:13:33
* 类说明: 顶部导航
*/
@Entry
@Component
struct SwiperIndex {
@State index: number = 0;
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start }) {
TopBar({ index: $index })
Swiper() {
PageAll()
PageMovie()
PageTV()
PageEntertainment()
PageAnime()
}
.index(this.index)
.indicator(false)
.loop(false)
.duration(CommonConstants.DURATION_PAGE)
.onChange((index: number) => {
this.index = index;
})
}
.backgroundColor($r('app.color.start_window_background'))
}
}
导航器布局
import { TopBarItem } from '../../common/bean/TopBarItem';
import { initializeOnStartup } from '../../viewmodel/TopBarViewModel';
import { CommonConstants } from '../../common/constants/CommonConstant';
/**
* TopBar component.
*/
@Component
export struct TopBar {
@Link index: number;
private tabArray: Array<TopBarItem> = initializeOnStartup();
build() {
Row({ space: CommonConstants.SPACE_TOP_BAR }) {
ForEach(this.tabArray,
(item: TopBarItem) => {
Text(item.name)
.fontSize(this.index === item.id ? CommonConstants.FONT_SIZE_CHECKED : CommonConstants.FONT_SIZE_UNCHECKED)
.fontColor(Color.Black)
.textAlign(TextAlign.Center)
.fontWeight(this.index === item.id ? FontWeight.Bold : FontWeight.Regular)
.onClick(() => {
this.index = item.id;
})
}, item => JSON.stringify(item))
}
.margin({ left: CommonConstants.ADS_LEFT })
.width(CommonConstants.FULL_WIDTH)
.height(CommonConstants.TOP_BAR_HEIGHT)
}
}
顶部导航我们使用 TopBar 配合轮播图组建 Swiper 来实现
电视剧页面实现
import { CommonConstants } from '../../common/constants/CommonConstant';
import { PositionDataSource } from '../../viewmodel/PositionDataSource';
@Component
export default struct PageTV {
@Provide positionListData: PositionDataSource = new PositionDataSource();
private startTouchOffsetY: number = 0;
private endTouchOffsetY: number = 0;
private username:String="";
private title:String="";
build() {
Row() {
List({ space: CommonConstants.LIST_ITEM_SPACE }) {
LazyForEach(this.positionListData, (item) => {
ListItem() {
Row() {
Column() {
Image(item?.logo)
.width(CommonConstants.LAYOUT_WIDTH_OR_HEIGHT)
.height(CommonConstants.LAYOUT_WIDTH_OR_HEIGHT)
}
.width(CommonConstants.GOODS_IMAGE_WIDTH)
.height(CommonConstants.LAYOUT_WIDTH_OR_HEIGHT)
Column() {
Row() {
Text(item?.name)
.fontSize(CommonConstants.NORMAL_FONT_SIZE)
.margin({ top: CommonConstants.BIGGER_FONT_SIZE })
}
.justifyContent(FlexAlign.Start)
.width(CommonConstants.GOODS_LIST_WIDTH)
Row() {
Text(item?.cname)
.fontColor($r('app.color.black_text_color'))
.fontSize(CommonConstants.GOODS_EVALUATE_FONT_SIZE)
Text(item?.size).fontSize(CommonConstants.NORMAL_FONT_SIZE).fontColor($r('app.color.login_blue_text_color'))
.margin({ left: 10 })
} .justifyContent(FlexAlign.Start)
.width(CommonConstants.GOODS_LIST_WIDTH)
.margin({ top: 20 })
Row() {
Text('时间').fontSize(CommonConstants.NORMAL_FONT_SIZE).fontColor($r('app.color.red'))
Text(item?.salary).fontSize(CommonConstants.GOODS_EVALUATE_FONT_SIZE).fontColor($r('app.color.login_blue_text_color'))
.margin({ left: 10 })
}
.justifyContent(FlexAlign.Start)
.width(CommonConstants.GOODS_LIST_WIDTH)
.margin({ top: 20 })
}
.padding(CommonConstants.GOODS_LIST_PADDING)
.width(CommonConstants.GOODS_FONT_WIDTH)
.height(CommonConstants.LAYOUT_WIDTH_OR_HEIGHT)
}
.justifyContent(FlexAlign.SpaceBetween)
.height(CommonConstants.GOODS_LIST_HEIGHT)
.width(CommonConstants.LAYOUT_WIDTH_OR_HEIGHT)
}
.onTouch((event: TouchEvent) => {
switch (event.type) {
case TouchType.Down:
this.startTouchOffsetY = event.touches[0].y;
break;
case TouchType.Up:
this.startTouchOffsetY = event.touches[0].y;
break;
case TouchType.Move:
if(this.startTouchOffsetY - this.endTouchOffsetY > 0) {
this.positionListData.pushData();
}
break;
}
})
})
}
.width(CommonConstants.GOODS_LIST_WIDTH).onClick(()=>{
})
}
.justifyContent(FlexAlign.Center)
.width(CommonConstants.LAYOUT_WIDTH_OR_HEIGHT)
}
}
模拟器数据
-
数据模型
export interface PositionListItemType {
logo:Resource;
name: Resource;
cname: Resource;
size: Resource;
salary: Resource;
}
/**
*
* 电视剧
*
*/
export const positionInitialList: PositionListItemType[] = [
{
logo: $r('app.media.fangyandexingxing'),
name: $r('app.string.Entertainmentname1'),
cname: $r('app.string.typesof'),
size: $r('app.string.typesof1'),
salary: $r('app.string.times1'),
},
{
logo: $r('app.media.wohejiangshiyuehui'),
name: $r('app.string.Entertainmentname2'),
cname: $r('app.string.typesof'),
size: $r('app.string.typesof2'),
salary: $r('app.string.times2'),
},
{
logo: $r('app.media.yitiantulongji'),
name: $r('app.string.Entertainmentname3'),
cname: $r('app.string.typesof'),
size: $r('app.string.typesof3'),
salary: $r('app.string.times3'),
},
{
logo: $r('app.media.getoutianwang'),
name: $r('app.string.Entertainmentname4'),
cname: $r('app.string.typesof'),
size: $r('app.string.typesof4'),
salary: $r('app.string.times4'),
},
{
logo: $r('app.media.xianjianqixiazhuna'),
name: $r('app.string.Entertainmentname5'),
cname: $r('app.string.typesof'),
size: $r('app.string.typesof5'),
salary: $r('app.string.times5'),
},
]
懒加载数据处理
import { positionInitialList, PositionListItemType } from './PositionData';
/**
* create a List range
*/
const createListRange = () => {
let result = [];
for (let i = 0; i < 2; i++) {
result = [...result, ...positionInitialList];
}
return result;
}
/**
* LazyLoad Class
*/
class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = []
public totalCount(): number {
return 0;
}
public getData(index: number): PositionListItemType {
return undefined;
}
public getPositionData(index: number): PositionListItemType {
return undefined;
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const position = this.listeners.indexOf(listener);
if (position >= 0) {
this.listeners.splice(position, 1);
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
})
}
}
export class PositionDataSource extends BasicDataSource {
private listData = createListRange();
public totalCount(): number {
return this.listData.length;
}
public getData(index: number): PositionListItemType {
return this.listData[index];
}
public pushData(): void {
if(this.listData.length < 12) {
this.listData = [...this.listData, ...positionInitialList];
this.notifyDataAdd(this.listData.length - 1);
}
}
}
效果图 :
视频播放我们使用的是video组件 之前有写 有兴趣的可以去看我之前的文章
轮播图 也可以去看之前文章
list列表组件也可以去看之前文件
grid网格组件也可以去看之前文章
运行环境 :
-
ArKUI-X
-
OpenHarmony os
-
api 10 版本
打包出的apk 反编译查看
-
反编译之后资源在 Android到处assets 目录下面
-
原工程资源
-
在 LibChecker 中也可以看到该 APP 使用了 ArkUI 的依赖:
最后总结
这个视频播放项目 我也是参考了官网的一些demo 所以有些同学见过也正常 我这里不多赘述 然后也是对比了在OpenHarmony 开发板和主流安卓设备上面的效果 UI 的一致性做得还算不错。 然后就是性能我感觉完成度也算是可以,希望大家能踊跃的参与进来 共同建设国产系统平台的生态 让国产自研技术达到了一个新高度,完成了从零到一的迈步,让自研不再是 PPT ,不再是“套壳”。 有兴趣的朋友可以关注我和我的鸿蒙专栏。我们下一期再见 最后呢 希望我都文章能帮助到各位同学工作和学习 如果你觉得文章还不错麻烦给我三连 关注点赞和转发 谢谢