
鸿蒙5列表页标准化开发:List+ListItem布局与间距规范
鸿蒙5列表页标准化开发:List+ListItem布局与间距规范
在鸿蒙5应用开发中,列表页是最常见的界面类型之一。本文将详细介绍如何使用List和ListItem组件实现标准化列表页开发,遵循鸿蒙设计规范中的布局与间距要求。
一、鸿蒙5列表设计规范概述
鸿蒙设计系统(HarmonyOS Design)为列表页提供了明确的规范:
列表项高度:
单行列表项:56vp
双行列表项:72vp
三行列表项:88vp
间距规范:
列表项内间距:16vp
列表项间间距:8vp
列表与屏幕边缘间距:24vp
字体规范:
标题:18fp,中等字重
副标题:14fp,常规字重
辅助文本:12fp,常规字重
二、基础列表实现
-
XML布局定义
<!-- resources/base/layout/list_page.xml -->
<?xml version=“1.0” encoding=“utf-8”?>
<DirectionalLayout
xmlns:ohos=“http://schemas.huawei.com/res/ohos”
ohos:width=“match_parent”
ohos:height=“match_parent”
ohos:orientation=“vertical”
ohos:padding=“24vp”><List
ohos:id=“$+id:standard_list”
ohos:width=“match_parent”
ohos:height=“match_content”
ohos:layout_alignment=“center”
ohos:divider=“1vp”
ohos:divider_color=“#20000000”
ohos:item_space=“8vp”/>
</DirectionalLayout>
2. 列表项布局
单行列表项 (56vp):
<!-- resources/base/layout/item_single_line.xml -->
<?xml version=“1.0” encoding=“utf-8”?>
<DirectionalLayout
xmlns:ohos=“http://schemas.huawei.com/res/ohos”
ohos:width=“match_parent”
ohos:height=“56vp”
ohos:orientation=“horizontal”
ohos:padding=“16vp”>
<Image
ohos:id="$+id:item_icon"
ohos:width="24vp"
ohos:height="24vp"
ohos:margin="0vp 16vp 0vp 0vp"
ohos:image_src="$media:ic_list_item"/>
<Text
ohos:id="$+id:item_title"
ohos:width="match_content"
ohos:height="match_content"
ohos:text_size="18fp"
ohos:text_font="sans-serif-medium"
ohos:text_color="$color:text_primary"/>
<Blank
ohos:width="0vp"
ohos:height="0vp"
ohos:layout_weight="1"/>
<Image
ohos:id="$+id:item_arrow"
ohos:width="24vp"
ohos:height="24vp"
ohos:image_src="$media:ic_arrow_right"/>
</DirectionalLayout>
双行列表项 (72vp):
<!-- resources/base/layout/item_double_line.xml -->
<?xml version=“1.0” encoding=“utf-8”?>
<DirectionalLayout
xmlns:ohos=“http://schemas.huawei.com/res/ohos”
ohos:width=“match_parent”
ohos:height=“72vp”
ohos:orientation=“vertical”
ohos:padding=“16vp”>
<DirectionalLayout
ohos:width="match_parent"
ohos:height="match_content"
ohos:orientation="horizontal">
<Image
ohos:id="$+id:item_icon"
ohos:width="24vp"
ohos:height="24vp"
ohos:margin="0vp 16vp 0vp 0vp"
ohos:image_src="$media:ic_list_item"/>
<Text
ohos:id="$+id:item_title"
ohos:width="match_content"
ohos:height="match_content"
ohos:text_size="18fp"
ohos:text_font="sans-serif-medium"
ohos:text_color="$color:text_primary"/>
<Blank
ohos:width="0vp"
ohos:height="0vp"
ohos:layout_weight="1"/>
<Text
ohos:id="$+id:item_time"
ohos:width="match_content"
ohos:height="match_content"
ohos:text_size="14fp"
ohos:text_color="$color:text_secondary"/>
</DirectionalLayout>
<Text
ohos:id="$+id:item_subtitle"
ohos:width="match_parent"
ohos:height="match_content"
ohos:text_size="14fp"
ohos:text_color="$color:text_secondary"
ohos:margin="8vp 0vp 0vp 40vp"/>
</DirectionalLayout>
三、列表适配器实现
-
数据模型定义
public class ListItem {
private int type; // 0:单行, 1:双行, 2:三行
private String title;
private String subtitle;
private String extraInfo;
private int iconResId;// 构造函数
public ListItem(int type, String title, String subtitle, String extraInfo, int iconResId) {
this.type = type;
this.title = title;
this.subtitle = subtitle;
this.extraInfo = extraInfo;
this.iconResId = iconResId;
}// Getter方法
public int getType() { return type; }
public String getTitle() { return title; }
public String getSubtitle() { return subtitle; }
public String getExtraInfo() { return extraInfo; }
public int getIconResId() { return iconResId; }
} -
多类型列表适配器
public class StandardListAdapter extends BaseItemProvider {
private List<ListItem> itemList;
private Context context;public StandardListAdapter(List<ListItem> itemList, Context context) {
this.itemList = itemList;
this.context = context;
}@Override
public int getCount() {
return itemList == null ? 0 : itemList.size();
}@Override
public Object getItem(int position) {
return itemList.get(position);
}@Override
public long getItemId(int position) {
return position;
}@Override
public int getItemViewType(int position) {
return itemList.get(position).getType();
}@Override
public int getViewTypeCount() {
return 3; // 支持三种列表项类型
}@Override
public Component getComponent(int position, Component convertView, ComponentContainer parent) {
ViewHolder holder;
ListItem item = itemList.get(position);
int type = getItemViewType(position);if (convertView == null) { holder = new ViewHolder(); switch (type) { case 0: // 单行 convertView = LayoutScatter.getInstance(context) .parse(ResourceTable.Layout_item_single_line, null, false); holder.title = (Text) convertView.findComponentById(ResourceTable.Id_item_title); holder.icon = (Image) convertView.findComponentById(ResourceTable.Id_item_icon); break; case 1: // 双行 convertView = LayoutScatter.getInstance(context) .parse(ResourceTable.Layout_item_double_line, null, false); holder.title = (Text) convertView.findComponentById(ResourceTable.Id_item_title); holder.subtitle = (Text) convertView.findComponentById(ResourceTable.Id_item_subtitle); holder.icon = (Image) convertView.findComponentById(ResourceTable.Id_item_icon); holder.extra = (Text) convertView.findComponentById(ResourceTable.Id_item_time); break; case 2: // 三行 // 类似实现,省略代码 break; } convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 绑定数据 holder.title.setText(item.getTitle()); holder.icon.setPixelMap(item.getIconResId()); if (type == 1 || type == 2) { holder.subtitle.setText(item.getSubtitle()); holder.extra.setText(item.getExtraInfo()); } return convertView;
}
static class ViewHolder {
Text title;
Text subtitle;
Text extra;
Image icon;
}
}
四、列表页实现与交互 -
AbilitySlice实现
public class ListPageSlice extends AbilitySlice {
private List<Object> dataList = new ArrayList<>();@Override
public void onStart(Intent intent) {
super.onStart(intent);
setUIContent(ResourceTable.Layout_list_page);initData(); initList();
}
private void initData() {
dataList.add(new ListItem(0, “个人设置”, “”, “”, ResourceTable.Media_ic_settings));
dataList.add(new ListItem(0, “账户与安全”, “”, “”, ResourceTable.Media_ic_security));
dataList.add(new ListItem(1, “系统更新”, “已是最新版本”, “今天”, ResourceTable.Media_ic_update));
dataList.add(new ListItem(1, “存储空间”, “64GB中已用32GB”, “昨天”, ResourceTable.Media_ic_storage));
dataList.add(new ListItem(2, “通知管理”, “应用通知设置”, “3个应用有通知”, ResourceTable.Media_ic_notification));
// 更多数据项…
}private void initList() {
List list = (List) findComponentById(ResourceTable.Id_standard_list);
StandardListAdapter adapter = new StandardListAdapter(dataList, this);
list.setItemProvider(adapter);// 列表项点击事件 list.setItemClickedListener((listView, component, position, id) -> { ListItem item = (ListItem) dataList.get(position); showItemDetail(item); }); // 列表项长按事件 list.setItemLongClickedListener((listView, component, position, id) -> { showContextMenu(position); return true; });
}
private void showItemDetail(ListItem item) {
// 跳转到详情页
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withDeviceId(“”)
.withBundleName(getBundleName())
.withAbilityName(DetailAbility.class.getName())
.build();
intent.setOperation(operation);
intent.setParam(“item”, item);
startAbility(intent);
}private void showContextMenu(int position) {
// 显示上下文菜单
ContextMenu contextMenu = new ContextMenu(this);
contextMenu.setTitle(“操作选项”);
contextMenu.addItem(“删除”, () -> deleteItem(position));
contextMenu.addItem(“置顶”, () -> pinItem(position));
contextMenu.show();
}private void deleteItem(int position) {
// 删除逻辑
}private void pinItem(int position) {
// 置顶逻辑
}
}
五、高级列表功能实现 -
分组列表实现
// 分组列表适配器
public class SectionListAdapter extends BaseItemProvider {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;private List<Object> dataList;
@Override
public int getCount() {
return dataList.size();
}@Override
public Object getItem(int position) {
return dataList.get(position);
}@Override
public int getItemViewType(int position) {
return dataList.get(position) instanceof String ? TYPE_HEADER : TYPE_ITEM;
}@Override
public Component getComponent(int position, Component convertView, ComponentContainer parent) {
int type = getItemViewType(position);if (type == TYPE_HEADER) { // 分组标题布局 Text header = new Text(getContext()); header.setText((String) dataList.get(position)); header.setTextSize(16); header.setTextFont(Font.DEFAULT_BOLD); header.setPadding(24, 16, 24, 8); return header; } else { // 普通列表项 // 使用前面的标准列表项实现 }
}
} -
下拉刷新与上拉加载
private void initRefreshLayout() {
RefreshLayout refreshLayout = (RefreshLayout) findComponentById(ResourceTable.Id_refresh_layout);// 下拉刷新
refreshLayout.setRefreshHeader(new DefaultHeader(this));
refreshLayout.setHeaderScrollDistance(70);
refreshLayout.setRefreshListener(new RefreshLayout.RefreshListener() {
@Override
public void onRefresh() {
// 加载新数据
loadNewData();
}@Override public void onLoadMore() { // 加载更多数据 loadMoreData(); }
});
// 自定义刷新动画
refreshLayout.setRefreshHeader(new MaterialHeader(this));
}
private void loadNewData() {
// 模拟网络请求
getUITaskDispatcher().delayDispatch(() -> {
// 添加新数据
refreshLayout.finishRefresh();
adapter.notifyDataChanged();
}, 2000);
}
3. 列表项动画效果
// 在适配器中添加动画
@Override
public Component getComponent(int position, Component convertView, ComponentContainer parent) {
// … 原有代码
// 添加入场动画
if (convertView.getComponentAnimator() == null) {
AnimatorProperty animator = new AnimatorProperty();
animator.moveFromX(parent.getWidth())
.setDuration(300)
.setDelay(position * 30);
convertView.setComponentAnimator(animator);
}
// 点击动画
convertView.setClickedListener(component -> {
AnimatorProperty clickAnim = new AnimatorProperty();
clickAnim.scaleX(0.95f).scaleY(0.95f).setDuration(100)
.setLoopedCount(2)
.setCurveType(Animator.CurveType.SPRING);
component.setComponentAnimator(clickAnim);
});
return convertView;
}
六、性能优化技巧
-
视图回收优化
@Override
public Component getComponent(int position, Component convertView, ComponentContainer parent) {
ViewHolder holder;
if (convertView == null) {
// 创建新视图
} else {
holder = (ViewHolder) convertView.getTag();
// 重置视图状态
holder.icon.setPixelMap(ResourceTable.Media_default_icon);
holder.title.setText(“”);
// … 其他重置操作
}
// … 数据绑定
} -
图片加载优化
// 使用异步图片加载
private void loadImage(Image imageView, int resId) {
AsyncImageLoader loader = new AsyncImageLoader();
loader.load(resId).addCallback(new AsyncImageLoader.ImageCallback() {
@Override
public void onImageLoaded(PixelMap pixelMap) {
getUITaskDispatcher().asyncDispatch(() -> {
imageView.setPixelMap(pixelMap);
});
}
});
} -
分页加载数据
private void loadMoreData() {
if (isLoading || !hasMore) return;isLoading = true;
showLoadingFooter();// 模拟网络请求
getUITaskDispatcher().delayDispatch(() -> {
List<ListItem> newItems = fetchDataFromServer(currentPage);
if (newItems.isEmpty()) {
hasMore = false;
showNoMoreFooter();
} else {
dataList.addAll(newItems);
adapter.notifyDataChanged();
currentPage++;
}
isLoading = false;
refreshLayout.finishLoadMore();
}, 1500);
}
七、深色模式适配
在 resources/dark/layout/ 目录下创建深色模式布局文件,或使用全局颜色变量:
<!-- 在列表项中使用全局颜色 -->
<Text
ohos:id=“$+id:item_title”
ohos:text_color=“$color:text_primary”/>
<Text
ohos:id=“$+id:item_subtitle”
ohos:text_color=“$color:text_secondary”/>
在 color.json 中定义深色模式颜色:
{
“color”: [
{
“name”: “text_primary”,
“value”: “#FFFFFF”
},
{
“name”: “text_secondary”,
“value”: “#AAAAAA”
}
]
}
八、最佳实践总结
遵循设计规范:
严格使用标准高度和间距
保持字体大小和字重一致
组件复用:
创建可复用的列表项组件
使用样式资源统一外观
性能优先:
实现视图回收
使用异步加载
分页加载大数据集
交互体验:
添加合理的动画效果
实现下拉刷新和上拉加载
提供视觉反馈
主题适配:
使用全局颜色变量
为深色模式提供专门资源
通过遵循这些规范和最佳实践,您可以创建出符合鸿蒙设计标准、性能优异且用户体验良好的列表页面。
