KMP OpenHarmony 数据分页库示例 - 跨平台数据分页解决方案
本文介绍了一个基于Kotlin Multiplatform(KMP)和OpenHarmony平台的数据分页库实现方案。该库提供了一套跨平台的分页功能,包括分页查询、计算、导航、验证等核心模块,支持Kotlin/JVM、Kotlin/JS和OpenHarmony/ArkTS多平台运行。文章详细展示了核心分页类的Kotlin实现代码,包含分页信息计算、数据切片、页码生成等关键方法,并支持配置最大页数、

项目概述
数据分页是现代应用开发中的核心功能。无论是在列表展示、数据加载、性能优化还是用户体验中,都需要进行数据的分页处理。然而,不同的编程语言和平台对数据分页的实现方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。
本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的数据分页库示例。这个库提供了一套完整的数据分页能力,包括分页查询、分页计算、分页导航等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。
技术架构
多平台支持
- Kotlin/JVM: 后端服务和桌面应用
- Kotlin/JS: Web 应用和浏览器环境
- OpenHarmony/ArkTS: 鸿蒙操作系统应用
核心功能模块
- 分页查询: 按页码查询数据
- 分页计算: 计算分页信息
- 分页导航: 生成分页导航
- 分页验证: 验证分页参数
- 分页统计: 统计分页结果
- 分页优化: 优化分页性能
- 分页缓存: 缓存分页数据
- 分页性能: 监控分页性能
Kotlin 实现
核心分页类
// 文件: src/commonMain/kotlin/DataPaginator.kt
/**
* 数据分页工具类
* 提供各种数据分页功能
*/
class DataPaginator {
data class PageInfo(
val pageNumber: Int,
val pageSize: Int,
val totalItems: Int,
val totalPages: Int,
val hasNext: Boolean,
val hasPrevious: Boolean,
val startIndex: Int,
val endIndex: Int
)
data class PageResult(
val data: List<Int>,
val pageInfo: PageInfo,
val paginationTime: Long
)
data class PaginationConfig(
val defaultPageSize: Int = 10,
val maxPageSize: Int = 100,
val enableCache: Boolean = false
)
private var config = PaginationConfig()
/**
* 设置分页配置
* @param config 配置对象
*/
fun setConfig(config: PaginationConfig) {
this.config = config
}
/**
* 获取分页信息
* @param pageNumber 页码
* @param pageSize 每页大小
* @param totalItems 总项数
* @return 分页信息
*/
fun getPageInfo(pageNumber: Int, pageSize: Int, totalItems: Int): PageInfo {
val validPageSize = pageSize.coerceIn(1, config.maxPageSize)
val totalPages = (totalItems + validPageSize - 1) / validPageSize
val validPageNumber = pageNumber.coerceIn(1, maxOf(1, totalPages))
val startIndex = (validPageNumber - 1) * validPageSize
val endIndex = minOf(startIndex + validPageSize, totalItems)
return PageInfo(
validPageNumber,
validPageSize,
totalItems,
totalPages,
validPageNumber < totalPages,
validPageNumber > 1,
startIndex,
endIndex
)
}
/**
* 分页查询
* @param data 原始数据
* @param pageNumber 页码
* @param pageSize 每页大小
* @return 分页结果
*/
fun paginate(data: List<Int>, pageNumber: Int, pageSize: Int): PageResult {
val startTime = System.currentTimeMillis()
val pageInfo = getPageInfo(pageNumber, pageSize, data.size)
val pageData = data.subList(pageInfo.startIndex, pageInfo.endIndex)
val paginationTime = System.currentTimeMillis() - startTime
return PageResult(pageData, pageInfo, paginationTime)
}
/**
* 获取所有页码列表
* @param totalPages 总页数
* @param currentPage 当前页
* @param maxButtons 最多显示页码数
* @return 页码列表
*/
fun getPageNumbers(totalPages: Int, currentPage: Int, maxButtons: Int = 5): List<Int> {
if (totalPages <= maxButtons) {
return (1..totalPages).toList()
}
val halfButtons = maxButtons / 2
val startPage = maxOf(1, currentPage - halfButtons)
val endPage = minOf(totalPages, startPage + maxButtons - 1)
return (startPage..endPage).toList()
}
/**
* 计算偏移量
* @param pageNumber 页码
* @param pageSize 每页大小
* @return 偏移量
*/
fun calculateOffset(pageNumber: Int, pageSize: Int): Int {
return (pageNumber - 1) * pageSize
}
/**
* 计算总页数
* @param totalItems 总项数
* @param pageSize 每页大小
* @return 总页数
*/
fun calculateTotalPages(totalItems: Int, pageSize: Int): Int {
return (totalItems + pageSize - 1) / pageSize
}
/**
* 验证分页参数
* @param pageNumber 页码
* @param pageSize 每页大小
* @param totalItems 总项数
* @return 是否有效
*/
fun isValidPage(pageNumber: Int, pageSize: Int, totalItems: Int): Boolean {
if (pageNumber < 1 || pageSize < 1) return false
val totalPages = calculateTotalPages(totalItems, pageSize)
return pageNumber <= totalPages
}
/**
* 获取分页统计信息
* @param result 分页结果
* @return 统计信息
*/
fun getPaginationStatistics(result: PageResult): Map<String, Any> {
return mapOf(
"pageNumber" to result.pageInfo.pageNumber,
"pageSize" to result.pageInfo.pageSize,
"totalItems" to result.pageInfo.totalItems,
"totalPages" to result.pageInfo.totalPages,
"hasNext" to result.pageInfo.hasNext,
"hasPrevious" to result.pageInfo.hasPrevious,
"currentPageItems" to result.data.size,
"paginationTime" to "${result.paginationTime}ms"
)
}
/**
* 生成分页报告
* @param result 分页结果
* @return 报告字符串
*/
fun generatePaginationReport(result: PageResult): String {
val stats = getPaginationStatistics(result)
val report = StringBuilder()
report.append("数据分页报告\n")
report.append("=".repeat(40)).append("\n")
report.append("当前页: ${stats["pageNumber"]}\n")
report.append("每页大小: ${stats["pageSize"]}\n")
report.append("总项数: ${stats["totalItems"]}\n")
report.append("总页数: ${stats["totalPages"]}\n")
report.append("当前页项数: ${stats["currentPageItems"]}\n")
report.append("有下一页: ${stats["hasNext"]}\n")
report.append("有上一页: ${stats["hasPrevious"]}\n")
report.append("分页耗时: ${stats["paginationTime"]}\n")
return report.toString()
}
}
Kotlin 实现的核心特点
Kotlin 实现中的分页功能充分利用了 Kotlin 标准库的数学和集合处理能力。分页信息计算使用了 coerceIn 方法。数据提取使用了 subList 方法。
页码列表生成使用了范围操作。参数验证使用了条件判断。统计信息使用了 Map 数据结构。
JavaScript 实现
编译后的 JavaScript 代码
// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
// (由 Kotlin 编译器自动生成)
/**
* DataPaginator 类的 JavaScript 版本
* 通过 Kotlin/JS 编译器从 Kotlin 源代码生成
*/
class DataPaginator {
constructor() {
this.config = {
defaultPageSize: 10,
maxPageSize: 100,
enableCache: false
};
}
/**
* 设置分页配置
* @param {Object} config - 配置对象
*/
setConfig(config) {
this.config = { ...this.config, ...config };
}
/**
* 获取分页信息
* @param {number} pageNumber - 页码
* @param {number} pageSize - 每页大小
* @param {number} totalItems - 总项数
* @returns {Object} 分页信息
*/
getPageInfo(pageNumber, pageSize, totalItems) {
const validPageSize = Math.max(1, Math.min(pageSize, this.config.maxPageSize));
const totalPages = Math.ceil(totalItems / validPageSize);
const validPageNumber = Math.max(1, Math.min(pageNumber, totalPages));
const startIndex = (validPageNumber - 1) * validPageSize;
const endIndex = Math.min(startIndex + validPageSize, totalItems);
return {
pageNumber: validPageNumber,
pageSize: validPageSize,
totalItems: totalItems,
totalPages: totalPages,
hasNext: validPageNumber < totalPages,
hasPrevious: validPageNumber > 1,
startIndex: startIndex,
endIndex: endIndex
};
}
/**
* 分页查询
* @param {Array} data - 原始数据
* @param {number} pageNumber - 页码
* @param {number} pageSize - 每页大小
* @returns {Object} 分页结果
*/
paginate(data, pageNumber, pageSize) {
const startTime = Date.now();
const pageInfo = this.getPageInfo(pageNumber, pageSize, data.length);
const pageData = data.slice(pageInfo.startIndex, pageInfo.endIndex);
const paginationTime = Date.now() - startTime;
return {
data: pageData,
pageInfo: pageInfo,
paginationTime: paginationTime
};
}
/**
* 获取所有页码列表
* @param {number} totalPages - 总页数
* @param {number} currentPage - 当前页
* @param {number} maxButtons - 最多显示页码数
* @returns {Array} 页码列表
*/
getPageNumbers(totalPages, currentPage, maxButtons = 5) {
if (totalPages <= maxButtons) {
const pages = [];
for (let i = 1; i <= totalPages; i++) {
pages.push(i);
}
return pages;
}
const halfButtons = Math.floor(maxButtons / 2);
const startPage = Math.max(1, currentPage - halfButtons);
const endPage = Math.min(totalPages, startPage + maxButtons - 1);
const pages = [];
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
return pages;
}
/**
* 计算总页数
* @param {number} totalItems - 总项数
* @param {number} pageSize - 每页大小
* @returns {number} 总页数
*/
calculateTotalPages(totalItems, pageSize) {
return Math.ceil(totalItems / pageSize);
}
/**
* 获取分页统计信息
* @param {Object} result - 分页结果
* @returns {Object} 统计信息
*/
getPaginationStatistics(result) {
return {
pageNumber: result.pageInfo.pageNumber,
pageSize: result.pageInfo.pageSize,
totalItems: result.pageInfo.totalItems,
totalPages: result.pageInfo.totalPages,
hasNext: result.pageInfo.hasNext,
hasPrevious: result.pageInfo.hasPrevious,
currentPageItems: result.data.length,
paginationTime: `${result.paginationTime}ms`
};
}
}
JavaScript 实现的特点
JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的数组方法提供了分页能力。
slice 方法用于数据提取。Math.ceil 用于页数计算。Math.max/min 用于边界处理。
ArkTS 调用代码
OpenHarmony 应用集成
// 文件: kmp_ceshiapp/entry/src/main/ets/pages/DataPaginatorPage.ets
import { DataPaginator } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';
@Entry
@Component
struct DataPaginatorPage {
@State currentPage: number = 1;
@State pageSize: number = 5;
@State inputData: string = '';
@State result: string = '';
@State resultTitle: string = '';
@State pageNumbers: number[] = [];
private dataPaginator = new DataPaginator();
private allData: number[] = [];
build() {
Column() {
// 标题
Text('📄 数据分页库示例')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(20)
.backgroundColor('#1A237E')
.textAlign(TextAlign.Center)
Scroll() {
Column() {
// 数据输入
Column() {
Text('输入数据(用逗号分隔)')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 8 })
TextInput({ placeholder: '例如:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15', text: this.inputData })
.onChange((value) => this.inputData = value)
.width('100%')
.height(50)
.padding(12)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.fontSize(12)
}
.width('95%')
.margin({ top: 16, left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
// 分页参数
Row() {
Column() {
Text('每页大小')
.fontSize(12)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 4 })
TextInput({ placeholder: '5', text: this.pageSize.toString() })
.onChange((value) => this.pageSize = parseInt(value) || 5)
.width('100%')
.height(40)
.padding(8)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.fontSize(12)
}
.layoutWeight(1)
.margin({ right: 8 })
Column() {
Text('当前页')
.fontSize(12)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 4 })
TextInput({ placeholder: '1', text: this.currentPage.toString() })
.onChange((value) => this.currentPage = parseInt(value) || 1)
.width('100%')
.height(40)
.padding(8)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.fontSize(12)
}
.layoutWeight(1)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
// 操作按钮
Row() {
Button('✨ 分页')
.layoutWeight(1)
.height(44)
.backgroundColor('#1A237E')
.fontColor('#FFFFFF')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.borderRadius(6)
.onClick(() => this.executePagination())
Blank()
.width(12)
Button('🔄 清空')
.layoutWeight(1)
.height(44)
.backgroundColor('#F5F5F5')
.fontColor('#1A237E')
.fontSize(14)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.onClick(() => {
this.inputData = '';
this.result = '';
this.resultTitle = '';
this.pageNumbers = [];
})
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
// 分页导航
if (this.pageNumbers.length > 0) {
Row() {
ForEach(this.pageNumbers, (pageNum: number) => {
Button(pageNum.toString())
.width(40)
.height(40)
.margin({ right: 4 })
.backgroundColor(this.currentPage === pageNum ? '#1A237E' : '#E0E0E0')
.fontColor(this.currentPage === pageNum ? '#FFFFFF' : '#333333')
.fontSize(12)
.onClick(() => {
this.currentPage = pageNum;
this.executePagination();
})
})
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
}
// 结果显示
if (this.resultTitle) {
Column() {
Text(this.resultTitle)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(12)
.backgroundColor('#1A237E')
.borderRadius(6)
.textAlign(TextAlign.Center)
.margin({ bottom: 12 })
Scroll() {
Text(this.result)
.fontSize(12)
.fontColor('#333333')
.fontFamily('monospace')
.textAlign(TextAlign.Start)
.width('100%')
.padding(12)
.selectable(true)
}
.width('100%')
.height(300)
.backgroundColor('#F9F9F9')
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
}
}
.width('100%')
}
.layoutWeight(1)
.width('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
private executePagination() {
const inputStr = this.inputData || '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15';
this.allData = inputStr.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n));
if (this.allData.length === 0) {
this.resultTitle = '❌ 错误';
this.result = '请输入有效的数据';
return;
}
try {
const result = this.dataPaginator.paginate(this.allData, this.currentPage, this.pageSize);
const stats = this.dataPaginator.getPaginationStatistics(result);
this.pageNumbers = this.dataPaginator.getPageNumbers(stats.totalPages, this.currentPage, 5);
this.resultTitle = `📄 第 ${stats.pageNumber} 页 / 共 ${stats.totalPages} 页`;
this.result = `当前页数据: ${result.data}\n当前页项数: ${stats.currentPageItems}\n总项数: ${stats.totalItems}\n每页大小: ${stats.pageSize}\n有下一页: ${stats.hasNext}\n有上一页: ${stats.hasPrevious}\n分页耗时: ${stats.paginationTime}`;
} catch (e) {
this.resultTitle = '❌ 分页出错';
this.result = `错误: ${e}`;
}
}
}
ArkTS 集成的关键要点
在 OpenHarmony 应用中集成分页工具库需要考虑用户体验和交互设计。我们设计了一个完整的分页 UI,包括参数输入、分页导航和结果展示。
分页参数使用了 TextInput 组件。分页导航使用了动态按钮生成。结果显示使用了可选择的文本。
工作流程详解
数据分页的完整流程
- 数据输入: 用户输入要分页的数据
- 参数设置: 用户设置每页大小和当前页
- 分页处理: 调用 DataPaginator 的分页方法
- 结果展示: 显示当前页数据和分页导航
跨平台一致性
通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,数据分页的逻辑和结果都是完全相同的。
实际应用场景
列表展示
在展示大量列表数据时,需要进行分页处理。这个工具库提供了完整的列表分页功能。
数据加载
在加载数据时,需要进行分页加载以提高性能。这个工具库提供了数据加载分页能力。
搜索结果
在展示搜索结果时,需要进行分页处理。这个工具库提供了搜索结果分页功能。
内容浏览
在浏览内容时,需要进行分页导航。这个工具库提供了内容浏览分页能力。
性能优化
分页缓存
在频繁访问相同页面时,可以缓存分页结果以提高性能。
懒加载
在处理大量数据时,应该使用懒加载策略以提高初始加载速度。
安全性考虑
参数验证
在进行分页处理时,应该验证分页参数以确保安全性。
边界检查
在访问分页数据时,应该进行边界检查以防止越界。
总结
这个 KMP OpenHarmony 数据分页库示例展示了如何使用现代的跨平台技术来处理常见的数据分页任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。
数据分页是应用开发中的重要功能。通过使用这样的工具库,开发者可以快速、可靠地实现各种分页操作,从而提高应用的性能和用户体验。
在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更多的分页策略、实现更复杂的分页规则等高级特性。同时,定期进行性能测试和优化,确保应用的分页系统保持高效运行。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)