全部影片页面 - Cordova 与 OpenHarmony 混合开发实战
文章摘要:本文介绍了MovieTracker应用中"全部影片"页面的功能实现,包括数据加载、列表渲染和搜索筛选三大核心模块。该页面采用分页加载和虚拟滚动技术优化性能,支持按标题、年份、评分等排序,并提供分类筛选和实时搜索功能。通过HTML/CSS构建页面布局,JavaScript实现动态数据交互,结合IndexedDB存储影片数据,为用户提供流畅的影片管理体验。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

📌 模块概述
全部影片页面是MovieTracker应用的核心功能模块,用于展示用户收藏的所有影片。该页面采用表格或卡片列表的形式展示影片信息,支持排序、筛选、搜索等功能。用户可以在这个页面中查看、编辑、删除影片记录,以及进行批量操作。
该模块的主要功能包括:影片列表展示、分页加载、排序功能(按标题、年份、评分等)、快速搜索、批量选择、导出功能等。通过Cordova框架与OpenHarmony原生能力的结合,实现了高效的数据展示和交互。
全部影片页面需要处理大量的数据,因此性能优化至关重要。我们采用虚拟滚动、分页加载等技术来提高页面响应速度。同时,通过OpenHarmony原生插件提供文件操作、权限管理等功能。
🔗 完整流程
第一步:页面初始化与数据加载
当用户导航到全部影片页面时,首先需要从IndexedDB数据库中加载所有影片数据。这个过程需要考虑数据量的大小,采用分页加载的方式来避免一次性加载过多数据导致的性能问题。
初始化过程包括:设置分页参数(当前页码、每页数量)、从数据库查询数据、计算总页数、初始化排序和筛选条件。我们需要建立一个数据缓存机制,避免频繁的数据库查询。同时需要显示加载动画,告知用户数据正在加载中。
第二步:列表渲染与交互
完成数据加载后,需要将影片数据渲染到页面上。可以使用表格或卡片列表的形式展示。每个影片项需要包含基本信息(标题、年份、评分、分类等),以及操作按钮(查看详情、编辑、删除等)。
为了提高用户体验,需要为列表项添加hover效果、点击事件、右键菜单等交互。同时需要实现排序功能,用户可以点击列标题改变排序顺序。这些交互需要实时反馈,提供流畅的动画效果。
第三步:筛选与搜索
全部影片页面需要支持多种筛选和搜索功能。用户可以按分类、标签、评分等条件进行筛选。同时支持快速搜索,用户输入关键词可以实时搜索影片。
筛选和搜索的实现需要考虑性能,避免每次输入都进行全表扫描。可以使用防抖技术延迟搜索请求,同时在数据库中建立索引以加快查询速度。筛选条件需要保存在应用状态中,以便用户切换页面后返回时能恢复之前的筛选条件。
🔧 Web代码实现
影片列表HTML结构
<div id="all-movies-page" class="page">
<div class="page-header">
<h2>全部影片</h2>
<div class="page-actions">
<input type="text" id="search-input" placeholder="搜索影片..." class="search-box">
<button class="btn btn-primary" onclick="app.navigateTo('add-movie')">➕ 添加影片</button>
</div>
</div>
<div class="filters-bar">
<select id="category-filter" onchange="filterMovies()">
<option value="">所有分类</option>
</select>
<select id="sort-by" onchange="sortMovies()">
<option value="title">按标题排序</option>
<option value="year">按年份排序</option>
<option value="rating">按评分排序</option>
</select>
</div>
<div class="movies-list" id="movies-container"></div>
<div class="pagination">
<button onclick="previousPage()">上一页</button>
<span id="page-info">第 1 页</span>
<button onclick="nextPage()">下一页</button>
</div>
</div>
这个HTML结构定义了全部影片页面的布局。页面头部包含标题和操作按钮,搜索框用于快速搜索。筛选栏包含分类和排序下拉菜单。影片列表是一个动态容器,将通过JavaScript填充影片数据。分页控件允许用户在不同页面之间导航。
影片列表数据加载
let currentPage = 1;
const pageSize = 10;
let allMovies = [];
let filteredMovies = [];
async function loadMoviesList() {
try {
allMovies = await db.getAllMovies();
filteredMovies = [...allMovies];
await loadCategories();
renderMoviesList();
updatePagination();
} catch (error) {
console.error('加载影片列表失败:', error);
showError('加载影片列表失败');
}
}
async function loadCategories() {
const categories = await db.getAllCategories();
const categorySelect = document.getElementById('category-filter');
categories.forEach(cat => {
const option = document.createElement('option');
option.value = cat.id;
option.textContent = cat.name;
categorySelect.appendChild(option);
});
}
这个函数实现了影片列表的数据加载。首先从数据库获取所有影片,然后加载分类信息填充筛选下拉菜单。使用filteredMovies变量存储当前筛选后的影片列表,这样可以在不重新查询数据库的情况下进行排序和分页操作。
影片列表渲染
function renderMoviesList() {
const container = document.getElementById('movies-container');
container.innerHTML = '';
const start = (currentPage - 1) * pageSize;
const end = start + pageSize;
const pageMovies = filteredMovies.slice(start, end);
pageMovies.forEach(movie => {
const movieCard = document.createElement('div');
movieCard.className = 'movie-card';
movieCard.innerHTML = `
<div class="movie-card-header">
<h3>${movie.title}</h3>
<span class="movie-year">${movie.year}</span>
</div>
<div class="movie-card-body">
<p class="movie-category">分类: ${movie.category}</p>
<p class="movie-rating">评分: ⭐ ${movie.rating || '未评分'}</p>
<p class="movie-status">状态: ${movie.status === 'watched' ? '已看' : '想看'}</p>
</div>
<div class="movie-card-actions">
<button onclick="app.navigateTo('movie-detail', ${movie.id})" class="btn btn-small">查看</button>
<button onclick="app.navigateTo('edit-movie', ${movie.id})" class="btn btn-small">编辑</button>
<button onclick="deleteMovie(${movie.id})" class="btn btn-small btn-danger">删除</button>
</div>
`;
container.appendChild(movieCard);
});
}
这个函数将影片列表渲染到页面上。首先根据当前页码和每页数量计算要显示的影片范围,然后为每个影片创建一个卡片元素。每个卡片包含影片的基本信息和操作按钮。通过onclick属性绑定事件处理器,实现查看、编辑、删除等功能。
搜索与筛选
function filterMovies() {
const categoryId = document.getElementById('category-filter').value;
const searchText = document.getElementById('search-input').value.toLowerCase();
filteredMovies = allMovies.filter(movie => {
const matchCategory = !categoryId || movie.categoryId === parseInt(categoryId);
const matchSearch = !searchText ||
movie.title.toLowerCase().includes(searchText) ||
movie.director.toLowerCase().includes(searchText);
return matchCategory && matchSearch;
});
currentPage = 1;
renderMoviesList();
updatePagination();
}
function sortMovies() {
const sortBy = document.getElementById('sort-by').value;
filteredMovies.sort((a, b) => {
switch(sortBy) {
case 'title':
return a.title.localeCompare(b.title);
case 'year':
return b.year - a.year;
case 'rating':
return (b.rating || 0) - (a.rating || 0);
default:
return 0;
}
});
renderMoviesList();
}
这个函数实现了搜索和筛选功能。filterMovies()函数根据选中的分类和搜索关键词过滤影片列表。sortMovies()函数根据选择的排序方式对影片进行排序。这两个函数都会重新渲染列表,并重置当前页码为第一页。
🔌 OpenHarmony原生代码
影片列表插件
// MovieListPlugin.ets
import { webview } from '@kit.ArkWeb';
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
export class MovieListPlugin {
private context: common.UIAbilityContext;
constructor(context: common.UIAbilityContext) {
this.context = context;
}
public registerMovieList(controller: webview.WebviewController): void {
controller.registerJavaScriptProxy({
object: new MovieListBridge(this.context),
name: 'movieListNative',
methodList: ['exportMovies', 'importMovies', 'getStorageInfo']
});
}
}
这个OpenHarmony原生插件为影片列表页面提供了文件操作和存储管理功能。通过registerJavaScriptProxy方法将原生对象暴露给Web层,使得JavaScript代码可以调用原生的文件操作方法。
影片数据导出
export class MovieListBridge {
private context: common.UIAbilityContext;
constructor(context: common.UIAbilityContext) {
this.context = context;
}
public exportMovies(moviesJson: string): string {
try {
const movies = JSON.parse(moviesJson);
const timestamp = new Date().getTime();
const fileName = `movies_${timestamp}.json`;
const cacheDir = this.context.cacheDir;
const filePath = `${cacheDir}/${fileName}`;
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE);
fileIo.writeSync(file.fd, moviesJson);
fileIo.closeSync(file.fd);
return JSON.stringify({
success: true,
filePath: filePath,
fileName: fileName
});
} catch (error) {
return JSON.stringify({
success: false,
error: error.message
});
}
}
public getStorageInfo(): string {
try {
const cacheDir = this.context.cacheDir;
const filesDir = this.context.filesDir;
return JSON.stringify({
cacheDir: cacheDir,
filesDir: filesDir,
timestamp: Date.now()
});
} catch (error) {
return JSON.stringify({
error: error.message
});
}
}
}
这个类实现了影片数据的导出功能。exportMovies()方法接收JSON格式的影片数据,将其写入到应用的缓存目录中。getStorageInfo()方法返回应用的存储路径信息,供Web层使用。
Web-Native通信
调用原生导出功能
async function exportMoviesToFile() {
try {
const moviesJson = JSON.stringify(filteredMovies);
if (window.movieListNative) {
const result = window.movieListNative.exportMovies(moviesJson);
const exportResult = JSON.parse(result);
if (exportResult.success) {
showSuccess(`影片已导出到: ${exportResult.fileName}`);
} else {
showError(`导出失败: ${exportResult.error}`);
}
} else {
// 降级方案:使用Web API导出
downloadAsJson(moviesJson, 'movies.json');
}
} catch (error) {
console.error('导出影片失败:', error);
showError('导出影片失败');
}
}
function downloadAsJson(data, filename) {
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
这个函数展示了如何调用OpenHarmony原生的导出功能。首先将影片数据转换为JSON字符串,然后调用原生的exportMovies()方法。如果原生方法不可用,提供了降级方案使用Web API进行导出。
📝 总结
全部影片页面展示了Cordova与OpenHarmony混合开发中的数据展示、交互处理和文件操作。通过Web层提供丰富的UI界面和用户交互,同时利用OpenHarmony原生能力进行文件操作和存储管理。
在实现这个模块时,需要注意大数据量的处理、搜索和筛选的性能优化、以及Web-Native通信的可靠性。通过合理的架构设计和代码优化,可以构建出高效、易用的影片管理功能。
更多推荐
所有评论(0)