鸿蒙5列表页标准化开发:List+ListItem布局与间距规范

暗雨OL
发布于 2025-6-27 21:16
浏览
0收藏

鸿蒙5列表页标准化开发:List+ListItem布局与间距规范
在鸿蒙5应用开发中,列表页是最常见的界面类型之一。本文将详细介绍如何使用List和ListItem组件实现标准化列表页开发,遵循鸿蒙设计规范中的布局与间距要求。

一、鸿蒙5列表设计规范概述
鸿蒙设计系统(HarmonyOS Design)为列表页提供了明确的规范:

​​列表项高度​​:
单行列表项:56vp
双行列表项:72vp
三行列表项:88vp
​​间距规范​​:
列表项内间距:16vp
列表项间间距:8vp
列表与屏幕边缘间距:24vp
​​字体规范​​:
标题:18fp,中等字重
副标题:14fp,常规字重
辅助文本:12fp,常规字重
二、基础列表实现

  1. 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>
三、列表适配器实现

  1. 数据模型定义
    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; }
    }

  2. 多类型列表适配器
    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;
    }
    }
    四、列表页实现与交互

  3. 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) {
    // 置顶逻辑
    }
    }
    五、高级列表功能实现

  4. 分组列表实现
    // 分组列表适配器
    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 {
         // 普通列表项
         // 使用前面的标准列表项实现
     }
    

    }
    }

  5. 下拉刷新与上拉加载
    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;

}
六、性能优化技巧

  1. 视图回收优化
    @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(“”);
    // … 其他重置操作
    }
    // … 数据绑定
    }

  2. 图片加载优化
    // 使用异步图片加载
    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);
    });
    }
    });
    }

  3. 分页加载数据
    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”
}
]
}
八、最佳实践总结
​​遵循设计规范​​:
严格使用标准高度和间距
保持字体大小和字重一致
​​组件复用​​:
创建可复用的列表项组件
使用样式资源统一外观
​​性能优先​​:
实现视图回收
使用异步加载
分页加载大数据集
​​交互体验​​:
添加合理的动画效果
实现下拉刷新和上拉加载
提供视觉反馈
​​主题适配​​:
使用全局颜色变量
为深色模式提供专门资源
通过遵循这些规范和最佳实践,您可以创建出符合鸿蒙设计标准、性能优异且用户体验良好的列表页面。

分类
标签
收藏
回复
举报
回复
    相关推荐