回复
#HarmonyOS NEXT体验官# 体验HarmonyOS开发流程,开发一个简单图册展示app(三) 原创
RandomBoolean
发布于 2024-8-21 16:34
浏览
0收藏
接上一篇文章,首页完成后,接着是文章的详情页和分类的相册页面。文章详情页相对简单,先介绍一下文章详情。
文章详情
首页瀑布流的布局代码如下
WaterFlow({ footer: (): void => this.itemFoot() }) {
ForEach(this.ArrData, (item: ESObject) => {
FlowItem() {
FlowItemComponent({ item: item })
.clickEffect({
level: ClickEffectLevel.MIDDLE,
scale: 0.8
}).onClick(() => {
router.pushUrl({
url: 'pages/ArtDetail',
params: {
id: item?._id || ''
}
})
})
}
}, (item: ProductItem) => JSON.stringify(item))
}
.nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.SELF_FIRST })
.layoutWeight(Const.WATER_FLOW_LAYOUT_WEIGHT)
.layoutDirection(FlexDirection.Column)
.columnsTemplate(Const.WATER_FLOW_COLUMNS_TEMPLATE)
.columnsGap($r('app.float.water_flow_columns_gap'))
.rowsGap($r('app.float.water_flow_row_gap'))
监听FlowItemComponent组件的onCLick事件,进行调整并传参文章详情id
文章详情的效果如图所示
顶部是一个Swiper组件,底部是文章详情描述文字。在aboutToAppear生命周期中调用接口,获取数据并展示。
整体页面源码如下
import Constants from '../common/constants/Constants';
import { router } from '@kit.ArkUI';
import ResponseResult from '../viewmodel/ResponseResult';
import { httpRequestGet, httpRequestPost } from '../common/utils/HttpUtil';
import CommonConstants from '../common/constants/CommonConstants';
import { Art } from '../viewmodel/ApiTypeModel';
@Entry
@Component
struct ArtDetail {
private artId: ESObject = router.getParams();
@State artObj: Art | undefined = undefined
aboutToAppear() {
this.artId = router.getParams()
// console.warn(JSON.stringify(this.artId))
httpRequestGet(`${CommonConstants.API_URL}/getArt?id=${this.artId.id}`).then((data: ResponseResult) => {
this.artObj = data.data
})
}
build() {
Navigation() {
Column() {
Swiper() {
ForEach(this.artObj?.images, (item: Resource) => {
Image(item)
.width(Constants.FULL_PERCENT)
.height(Constants.FULL_PERCENT)
.objectFit(ImageFit.Auto)
}, (item: Resource, index?: number) => JSON.stringify(item) + index)
}
.autoPlay(true)
.loop(true)
.clip(true)
.duration(Constants.BANNER_ANIMATE_DURATION)
.indicator(false)
.height('60%')
Text(this.artObj?.title || '')
.fontSize('18vp')
.fontWeight(FontWeight.Medium)
.width(CommonConstants.FULL_PARENT)
.margin({ top: '12vp', left: '12vp' })
Text(this.artObj?.content || '')
.fontSize('14vp')
.width(CommonConstants.FULL_PARENT)
.margin({ top: '12vp', left: '12vp' })
}
.width('100%')
}
.hideBackButton(false)
.titleMode(NavigationTitleMode.Mini)
}
}
全部分类
点击首页全部分类展示的是一个堆叠相册集,效果如图所示。
本文只展示了两个分类。页面的源码如下
import { router } from '@kit.ArkUI';
import CommonConstants from '../common/constants/CommonConstants';
import Constants from '../common/constants/Constants';
import { httpRequestGet } from '../common/utils/HttpUtil';
import PhotoItem from '../view/PhotoItem';
import ResponseResult from '../viewmodel/ResponseResult';
@Entry
@Component
struct IndexPage {
swiperController: SwiperController = new SwiperController();
scroller: Scroller = new Scroller();
@State currentIndex: number = 0;
@State angle: number = 0;
@State ArrData: Resource[][] = [];
aboutToAppear() {
httpRequestGet(`${CommonConstants.API_URL}/getArt`).then((data: ResponseResult) => {
let arr1 = []
let arr2 = []
data.data.forEach((item: ESObject, key: string) => {
if (item.category === 1) {
arr1 = arr1.concat(item.images)
} else if (item.category === 2) {
arr2 = arr2.concat(item.images)
}
})
this.ArrData = [
arr1,
arr2
]
})
}
build() {
Navigation() {
Column() {
Grid() {
ForEach(this.ArrData, (photoArr: Array<Resource>) => {
GridItem() {
PhotoItem({ photoArr })
}
.width(Constants.FULL_PERCENT)
.aspectRatio(Constants.STACK_IMG_RATIO)
.onClick(() => {
router.pushUrl({
url: Constants.URL_LIST_PAGE,
params: { photoArr: photoArr }
});
})
}, (item: Resource, index?: number) => JSON.stringify(item) + index)
}
.scrollBar(BarState.Off)
.columnsTemplate(Constants.INDEX_COLUMNS_TEMPLATE)
.columnsGap('12vp')
.rowsGap('12vp')
.padding({ left: '12vp', right: '12vp' })
.width(Constants.FULL_PERCENT)
.layoutWeight(1)
}
.width(Constants.FULL_PERCENT)
.height(Constants.FULL_PERCENT)
}
.hideBackButton(false)
.titleMode(NavigationTitleMode.Mini)
}
}
PhotoItem组件是实现堆叠的关键,使用Stack进行的实现
import Constants from '../common/constants/Constants';
@Component
export default struct PhotoItem {
photoArr: Array<Resource> = [];
@State currentIndex: number = 0;
private showCount: number = Constants.SHOW_COUNT / Constants.DOUBLE_NUMBER;
@Builder albumPicBuilder(img: Resource, index: number) {
Column() {
Image(img)
.width(Constants.FULL_PERCENT)
.height(Constants.FULL_PERCENT)
.borderRadius('12vp')
.opacity(1 - (this.showCount - index - 1) * Constants.ITEM_OPACITY_OFFSET)
}
.padding((this.showCount - index - 1) * Constants.PHOTO_ITEM_PADDING)
.offset({ y: (this.showCount - index - 1) * Constants.PHOTO_ITEM_OFFSET })
.height(Constants.PHOTO_ITEM_PERCENT)
.width(Constants.FULL_PERCENT)
}
build() {
Stack({ alignContent: Alignment.Top }) {
ForEach(Constants.CACHE_IMG_LIST, (image: string, index?: number) => {
if (index) {
this.albumPicBuilder(this.photoArr[this.showCount - index - 1], index)
}
}, (item: string, index?: number) => JSON.stringify(item) + index)
}
.width(Constants.FULL_PERCENT)
.height(Constants.FULL_PERCENT)
}
}
点击之后平铺展示全部相册,效果如图所示
Grid布局,源码如下
import { router } from '@kit.ArkUI';
import Constants from '../common/constants/Constants';
@Entry
@Component
struct ListPage {
photoArr: Array<Resource> = (router.getParams() as Record<string, Array<Resource>>)[`${Constants.PARAM_PHOTO_ARR_KEY}`];
@StorageLink('selectedIndex') selectedIndex: number = 0;
build() {
Navigation() {
Grid() {
ForEach(this.photoArr, (img: Resource, index?: number) => {
GridItem() {
Image(img)
.height(Constants.FULL_PERCENT)
.width(Constants.FULL_PERCENT)
.objectFit(ImageFit.Cover)
.onClick(() => {
if (!index) {
index = 0;
}
this.selectedIndex = index;
router.pushUrl({
url: Constants.URL_DETAIL_LIST_PAGE,
params: {
photoArr: this.photoArr,
}
});
})
}
.width(Constants.FULL_PERCENT)
.aspectRatio(1)
}, (item: Resource) => JSON.stringify(item))
}
.scrollBar(BarState.Off)
.columnsTemplate(Constants.GRID_COLUMNS_TEMPLATE)
.rowsGap(Constants.LIST_ITEM_SPACE)
.columnsGap(Constants.LIST_ITEM_SPACE)
.layoutWeight(1)
}
.title(Constants.PAGE_TITLE)
.hideBackButton(false)
.titleMode(NavigationTitleMode.Mini)
}
}
再次点击某张图片,进入大图游览模式。效果如下图所示。
分别监听大图滑动和小图滑动的事件。源码如下:
import { router,display } from '@kit.ArkUI';
import Constants from '../common/constants/Constants';
enum scrollTypeEnum {
STOP = 'onScrollStop',
SCROLL = 'onScroll'
};
@Entry
@Component
struct DetailListPage {
private smallScroller: Scroller = new Scroller();
private bigScroller: Scroller = new Scroller();
@State deviceWidth: number = Constants.DEFAULT_WIDTH;
@State smallImgWidth: number = (this.deviceWidth - Constants.LIST_ITEM_SPACE * (Constants.SHOW_COUNT - 1)) /
Constants.SHOW_COUNT;
@State imageWidth: number = this.deviceWidth + this.smallImgWidth;
private photoArr: Array<Resource | string> = (router.getParams() as Record<string, Array<Resource | string>>)[`${Constants.PARAM_PHOTO_ARR_KEY}`];
private smallPhotoArr: Array<Resource | string> = new Array<Resource | string>().concat(Constants.CACHE_IMG_LIST,
(router.getParams() as Record<string, Array<Resource | string>>)[`${Constants.PARAM_PHOTO_ARR_KEY}`],
Constants.CACHE_IMG_LIST)
@StorageLink('selectedIndex') selectedIndex: number = 0;
@Builder SmallImgItemBuilder(img: Resource, index?: number) {
if (index && index > (Constants.CACHE_IMG_SIZE - 1) && index < (this.smallPhotoArr.length - Constants.CACHE_IMG_SIZE)) {
Image(img)
.onClick(() => this.smallImgClickAction(index))
}
}
aboutToAppear() {
let displayClass: display.Display = display.getDefaultDisplaySync();
let width = displayClass?.width / displayClass.densityPixels ?? Constants.DEFAULT_WIDTH;
this.deviceWidth = width;
this.smallImgWidth = (width - Constants.LIST_ITEM_SPACE * (Constants.SHOW_COUNT - 1)) / Constants.SHOW_COUNT;
this.imageWidth = this.deviceWidth + this.smallImgWidth;
}
onPageShow() {
this.smallScroller.scrollToIndex(this.selectedIndex);
this.bigScroller.scrollToIndex(this.selectedIndex);
}
goDetailPage(): void {
router.pushUrl({
url: Constants.URL_DETAIL_PAGE,
params: { photoArr: this.photoArr }
});
}
smallImgClickAction(index: number): void {
this.selectedIndex = index - Constants.CACHE_IMG_SIZE;
this.smallScroller.scrollToIndex(this.selectedIndex);
this.bigScroller.scrollToIndex(this.selectedIndex);
}
smallScrollAction(type: scrollTypeEnum): void {
this.selectedIndex = Math.round(((this.smallScroller.currentOffset().xOffset as number) +
this.smallImgWidth / Constants.DOUBLE_NUMBER) / (this.smallImgWidth + Constants.LIST_ITEM_SPACE));
if (type === scrollTypeEnum.SCROLL) {
this.bigScroller.scrollTo({ xOffset: this.selectedIndex * this.imageWidth, yOffset: 0 });
} else {
this.smallScroller.scrollTo({ xOffset: this.selectedIndex * this.smallImgWidth, yOffset: 0 });
}
}
bigScrollAction(type: scrollTypeEnum): void {
let smallWidth = this.smallImgWidth + Constants.LIST_ITEM_SPACE;
this.selectedIndex = Math.round(((this.bigScroller.currentOffset().xOffset as number) +
smallWidth / Constants.DOUBLE_NUMBER) / this.imageWidth);
if (type === scrollTypeEnum.SCROLL) {
this.smallScroller.scrollTo({ xOffset: this.selectedIndex * smallWidth, yOffset: 0 });
} else {
this.bigScroller.scrollTo({ xOffset: this.selectedIndex * this.imageWidth, yOffset: 0 });
}
}
build() {
Navigation() {
Stack({ alignContent: Alignment.Bottom }) {
List({ scroller: this.bigScroller, initialIndex: this.selectedIndex }) {
ForEach(this.photoArr, (img: Resource) => {
ListItem() {
Image(img)
.height(Constants.FULL_PERCENT)
.width(Constants.FULL_PERCENT)
.objectFit(ImageFit.Contain)
.gesture(PinchGesture({ fingers: Constants.DOUBLE_NUMBER })
.onActionStart(() => this.goDetailPage()))
.onClick(() => this.goDetailPage())
}
.padding({
left: this.smallImgWidth / Constants.DOUBLE_NUMBER,
right: this.smallImgWidth / Constants.DOUBLE_NUMBER
})
.width(this.imageWidth)
}, (item: Resource) => JSON.stringify(item))
}
.onScroll((scrollOffset, scrollState) => {
if (scrollState === ScrollState.Fling) {
this.bigScrollAction(scrollTypeEnum.SCROLL);
}
})
.scrollBar(BarState.Off)
.onScrollStop(() => this.bigScrollAction(scrollTypeEnum.STOP))
.width(Constants.FULL_PERCENT)
.height(Constants.FULL_PERCENT)
.padding({ bottom: this.smallImgWidth * Constants.DOUBLE_NUMBER })
.listDirection(Axis.Horizontal)
List({
scroller: this.smallScroller,
space: Constants.LIST_ITEM_SPACE,
initialIndex: this.selectedIndex
}) {
ForEach(this.smallPhotoArr, (img: Resource, index?: number) => {
ListItem() {
this.SmallImgItemBuilder(img, index)
}
.width(this.smallImgWidth)
.aspectRatio(1)
}, (item: Resource) => JSON.stringify(item))
}
.listDirection(Axis.Horizontal)
.onScroll((scrollOffset, scrollState) => {
if (scrollState === ScrollState.Fling) {
this.smallScrollAction(scrollTypeEnum.SCROLL);
}
})
.scrollBar(BarState.Off)
.onScrollStop(() => this.smallScrollAction(scrollTypeEnum.STOP))
.margin({ top: '20vp', bottom: '20vp' })
.height(this.smallImgWidth)
.width(Constants.FULL_PERCENT)
}
.width(this.imageWidth)
.height(Constants.FULL_PERCENT)
}
.title(Constants.PAGE_TITLE)
.hideBackButton(false)
.titleMode(NavigationTitleMode.Mini)
}
}
到此文章整体便介绍完成,以此简单的案例展示学习一下鸿蒙的开发流程。
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
赞
收藏
回复
相关推荐