写这篇文章为了以后少踩坑,基于List + scroll 实现了基金列表的横向滚动+竖向滑动, 参考资料:
// 参考 https://blog.csdn.net/HarmonyOSMN/article/details/140107235
//参考 https://ost.51cto.com/answer/11479
该项目拉取的是利得基金的基金列表数据:链接 www.leadfund.com.cn/html5/index… 效果如下图
数据格式
export interface ProductPage{
productPage:ProductPageBean
}
export interface ProductPageBean{
list:ProductPageFundInfo[]
}
export interface ProductPageFundInfo{
follow:boolean
fundCode:string
fundName:string
fundType:string
dayRateValue:number
dayRateValueFormat:string
nav:number
navFormat:string
navDayFormat:string
recentlyMonthRateValue:number
recentlyMonthRateValueFormat:string
recentlySixMonthRateValue:number
recentlySixMonthRateValueFormat:string
recentlyThreeMonthRateValue:number
recentlyThreeMonthRateValueFormat:string
recentlyWeekRateValue:number
recentlyWeekRateValueFormat:string
recentlyYearRateValue:number
recentlyYearRateValueFormat:string
recentlyTwoYearRateValue:number
recentlyTwoYearRateValueFormat:string
recentlyThreeYearRateValue:number
recentlyThreeYearRateValueFormat:string
recentlyFiveYearRateValue:number
recentlyFiveYearRateValueFormat:string
setUpRateValue:number
setUpRateValueFormat:string
thisYearRateValue:number
thisYearRateValueFormat:string
}
export class LabelBean{
value:string
label:string
key:string
}
import http from '@ohos.net.http'
import axios from '@ohos/axios'
import ArrayList from '@ohos.util.ArrayList'
import { FundApi} from '../../api/Fund'
import { ItemStyle, TIME } from '../../common/constants/Constants'
import { LogBus } from '../../utils/LogBus'
import { ZZGTitle } from '../../view/pub/ZZGTitle'
import { BaseResponse } from '../../viewmodel/base/BaseResponse'
import { LabelBean } from '../../viewmodel/lead/LabelBean'
import { ProductTypeData } from '../../viewmodel/lead/ProductTypeData'
import { ProductPage, ProductPageFundInfo } from '../../viewmodel/lead/ProductPage'
import { ProductFundListItem } from '../../view/lead/fund/ProductFundListItem'
import ProductPageParams from '../../viewmodel/lead/ProductPageParams'
const tag: string = 'FundListPages request____'
@Entry
@Component
export struct FundListPages {
//基金类型 全部,股票,债券。。。
@State productTypeList: LabelBean[] = []
productType?: ProductTypeData | undefined
@State productTypePos: number = 0
//排序方式
@State increaseList: LabelBean[] = []
// @State productTypeSize: number = 0
@State fundInfoList: ProductPageFundInfo[] = []
private productPageParams :ProductPageParams = new ProductPageParams("","","","","",1,1)
horizontalScroller: Scroller = new Scroller()
//获取基金类型
async getProductTypeData() {
const res = await FundApi.getProductType()
LogBus.d(tag, "getProductTypeData 完成")
this.productType = res
if (this.productType == null) {
return
}
this.productTypeList = this.productType.productTypeList
LogBus.d(tag, "productTypeList.size = " + this.productTypeList.length)
if (this.productTypeList.length > 0) {
/**
* data.fundType: -1
data.orderBy: 2
data.rateValueType: -1
data.rateValueTypeWrapper: -1
page.limit: 20
page.page: 1
*/
let label: LabelBean = this.productTypeList[0]
this.productPageParams.fundType = label.value
this.productPageParams.orderBy = this.productType.orderList[0].value
this.productPageParams.rateValueType = '-1'
this.productPageParams.rateValueTypeWrapper = '-1'
this.productPageParams.page = 1
this.getProductList()
let labelIncrease: LabelBean = new LabelBean()
labelIncrease.value = '-1'
labelIncrease.label = '最新净值'
labelIncrease.key = 'dayRateValueFormat'
if (this.productType != null) {
this.increaseList = this.productType.increaseList
this.increaseList.unshift(labelIncrease)
}
}
}
/**
* 获取基金列表
*/
async getProductList() {
const res = await FundApi.getProductPage(this.productPageParams)
let productPage: ProductPage = res
this.fundInfoList = productPage.productPage.list
LogBus.d(tag, "getProductList fundInfoList 长度为"+ this.fundInfoList.length)
}
aboutToAppear() {
LogBus.d(tag, 'getProductTypeData() 执行')
this.getProductTypeData()
}
build() {
Column() {
ZZGTitle({ title: '基金' })
Tabs({ barPosition: BarPosition.Start }) {
ForEach(this.productTypeList, (item: LabelBean) => {
TabContent() {
// ProductFundListItem({fundInfoList:this.fundInfoList})
Column() {
}
.alignItems(HorizontalAlign.Start)
}.tabBar(item.label)
.width(120)
.height('100%')
})
}
.barMode(BarMode.Scrollable)
.vertical(false)
.width('100%')
.height(60)
.onChange(index => {
this.productTypePos = index
})
ProductFundListItem({fundInfoList:$fundInfoList,increaseList:$increaseList})
}.width('100%')
.height('100%')
}
@Builder TabBuilder(item: LabelBean) {
Row() {
Text(item.label)
// if (item.key === this.productPageParams.rateValueTypeWrapper) {
// Image($r('app.media.bg_my_topred'))
// .backgroundColor(Color.Red)
// .width(50)
// .height(50)
//
// } else {
// Image($r('app.media.img_setting'))
// // Image($r('app.media.ic_public_back'))
// .width(50)
// .height(50)
// }
}.width(ItemStyle.FUND_TAB_WIDTH)
}
}
ZZGTitle是我写的title,大家换成自己的title就好了。接口数据自己去利得基金网站拿吧,
import { ItemStyle } from '../../../common/constants/Constants'
import { LogBus } from '../../../utils/LogBus';
import { LabelBean } from '../../../viewmodel/lead/LabelBean';
import { ProductPageFundInfo } from '../../../viewmodel/lead/ProductPage'
@Component
export struct ProductFundListItem {
// private fundInfo: ProductPageFundInfo
@Link fundInfoList: ProductPageFundInfo[];
@Link increaseList: LabelBean[]
@State remainOffset: number = 0 // 内容行在横向滚动时回调的offset
private bottomRightScroller: Scroller = new Scroller() //下部分左侧标题List(行标题)
private bottomLeftScroller: Scroller = new Scroller() // 下部分右侧内容List(内容)
private topRightScroller: Scroller = new Scroller() // 上部分右侧类型List(列标题)
private bottomRightHorizontalScroller: Scroller = new Scroller() // 右下部的横向滑动
@State rightHeaderOffset: number = 0
/**
* 是否设置阴影
*/
@State isShowShadow:boolean = false
// hScroller: Scroller = new Scroller()
/**
* 基金列表各项宽度
*/
childItemWidth: number = 70
LeftItemWidth = 150
listItemHeight: number = 60
build() {
Column() {
this.topFixed()
Line().height(0.5).width('100%').backgroundColor('#EEEEEE')
Row() {
this.leftList()
// this.rightList()
this.rightScroll()
}
}
}
getRateShowText(rateFormat: string): string {
if (rateFormat == null || rateFormat == "") {
return '--'
}
return rateFormat
}
@Builder
topFixed() {
Row() {
// 上部分左侧固定信息
Row() {
Text('名称/代码')
.fontColor(ItemStyle.TEXT_BLACK_2)
.fontSize(14)
.width(this.LeftItemWidth)
.textAlign(TextAlign.Start)
Scroll(this.topRightScroller) {
Row() {
ForEach(this.increaseList, (item: LabelBean) => {
Text(item.label)
.fontColor(ItemStyle.TEXT_BLACK_2)
.fontSize(14)
.width(this.childItemWidth)
.textAlign(TextAlign.Start)
})
}
}.shadow( { radius: 10, color: Color.Gray })
.scrollBar(BarState.Off)
.width(this.childItemWidth * 12)
// .position({ x: this.LeftItemWidth, y: 0 })
.onScroll((scrollOffset: number, scrollState: ScrollState) => {
console.log('fxm' + scrollOffset)
this.rightHeaderOffset = this.topRightScroller.currentOffset().xOffset
this.bottomRightHorizontalScroller.scrollTo({ xOffset: this.topRightScroller.currentOffset().xOffset, yOffset: 0 })
})
.edgeEffect(EdgeEffect.None) // 边缘效果设置为Spring
.nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST })
.scrollable(ScrollDirection.Horizontal)
.padding({ top: 0 })
}
}.height(this.listItemHeight).width('100%')
}
@Builder
leftList() {
List({ scroller: this.bottomLeftScroller }) {
ForEach(this.fundInfoList, (item: ProductPageFundInfo) => {
ListItem() {
Column() {
Text(item.fundName)
.fontColor(ItemStyle.TEXT_BLACK_2)
.fontSize(14)
.maxLines(1)
.textAlign(TextAlign.Start)
Text(item.fundCode)
.fontColor(ItemStyle.TEXT_BLACK_2)
.fontSize(14)
.maxLines(1)
.textAlign(TextAlign.Start)
}
.width(this.LeftItemWidth)
.alignItems(HorizontalAlign.Start)
}.height(this.listItemHeight)
})
}
.width(this.LeftItemWidth)
.divider({ color: ItemStyle.TEXT_GRAY_1,
strokeWidth: 0.5 })
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.None)
// .onScroll((scrollOffset: number, scrollState: ScrollState) => {
// this.bottomRightScroller.scrollTo({ xOffset: this.remainOffset, yOffset: scrollOffset })
//
// })
.nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST })
.onScrollFrameBegin((offset: number, state: ScrollState) => {
this.bottomRightScroller.scrollTo({
xOffset: 0,
yOffset: this.bottomLeftScroller.currentOffset().yOffset + offset,
animation: false
})
return { offsetRemain: offset }
})
}
@Builder
rightScroll() {
Scroll(this.bottomRightHorizontalScroller) {
List({ initialIndex: 0, scroller: this.bottomRightScroller }) {
ForEach(this.fundInfoList, (item: ProductPageFundInfo, index: number) => {
// ListItemGroup({ header: this.rightStickyHeader(index) }) {
ListItem() {
Row() {
//最新净值
Text(item.navFormat)
.fontColor(ItemStyle.TEXT_BLACK_2)
.fontSize(14)
.width(this.childItemWidth)
//近一日
Text(this.getRateShowText(item.dayRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.dayRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//近一周
Text(this.getRateShowText(item.recentlyWeekRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.recentlyWeekRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//近一月
Text(this.getRateShowText(item.recentlyMonthRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.recentlyMonthRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//近三月
Text(this.getRateShowText(item.recentlyThreeMonthRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.recentlyThreeMonthRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//近六月
Text(this.getRateShowText(item.recentlySixMonthRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.recentlySixMonthRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//近一年
Text(this.getRateShowText(item.recentlyYearRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.recentlyYearRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//近两年
Text(this.getRateShowText(item.recentlyTwoYearRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.recentlyTwoYearRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//近三年
Text(this.getRateShowText(item.recentlyThreeYearRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.recentlyThreeYearRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//近五年
Text(this.getRateShowText(item.recentlyFiveYearRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.recentlyFiveYearRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//今年以来
Text(this.getRateShowText(item.thisYearRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.thisYearRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
//成立以来
Text(this.getRateShowText(item.setUpRateValueFormat))
.width(this.childItemWidth)
.fontColor(item.setUpRateValue >= 0 ? ItemStyle.RED_RATE : ItemStyle.TEXT_BLACK_2)
}
.height(this.listItemHeight)
}
})
}
.divider({ color: ItemStyle.TEXT_GRAY_1,
strokeWidth: 0.5 })
.sticky(StickyStyle.Header)
.listDirection(Axis.Vertical)
.scrollBar(BarState.Off)
.friction(0.6)
.edgeEffect(EdgeEffect.None)
.width('100%')
.nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST })
.onScrollFrameBegin((offset: number, state: ScrollState) => {
this.bottomLeftScroller.scrollTo({
xOffset: 0,
yOffset: this.bottomRightScroller.currentOffset().yOffset + offset,
animation: false
})
return { offsetRemain: offset }
})
}
.width(this.childItemWidth * 12)
// .position({ x: this.LeftItemWidth, y: 0 })
.onScroll((scrollOffset: number, scrollState: ScrollState) => {
console.log('fxm' + scrollOffset)
this.rightHeaderOffset = this.bottomRightHorizontalScroller.currentOffset().xOffset
LogBus.d("ProductFundListItem","rightHeaderOffset = "+ this.rightHeaderOffset )
if ( this.rightHeaderOffset !=null) {
//添加阴影
}
this.topRightScroller.scrollTo({ xOffset: this.bottomRightHorizontalScroller.currentOffset().xOffset, yOffset: 0 })
})
.edgeEffect(EdgeEffect.None) // 边缘效果设置为Spring
.nestedScroll({ scrollForward: NestedScrollMode.PARENT_FIRST, scrollBackward: NestedScrollMode.PARENT_FIRST })
.scrollable(ScrollDirection.Horizontal)
.padding({ top: 0 })
}
}
核心代码是几个Scroller控制器的关联
其实还是比较简单的