鸿蒙 5 相对布局 RelativeContainer:锚点定位与折叠屏适配

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

鸿蒙 5 相对布局 RelativeContainer:锚点定位与折叠屏适配
在鸿蒙应用开发中,RelativeContainer(相对布局容器)通过锚点定位系统为复杂布局提供了强大支持。本文将深入探讨 RelativeContainer 的核心功能,并重点介绍折叠屏适配的最佳实践。

一、RelativeContainer 锚点定位基础

  1. 九大锚点位置
    // 锚点位置常量定义
    public static final int ALIGN_START = 0;
    public static final int ALIGN_END = 1;
    public static final int ALIGN_TOP = 2;
    public static final int ALIGN_BOTTOM = 3;
    public static final int CENTER_HORIZONTAL = 4;
    public static final int CENTER_VERTICAL = 5;
    public static final int ALIGN_PARENT_START = 6;
    public static final int ALIGN_PARENT_END = 7;
    public static final int ALIGN_PARENT_TOP = 8;
    public static final int ALIGN_PARENT_BOTTOM = 9;
    public static final int CENTER_IN_PARENT = 10;
  2. 基本锚点定位实现
    // 创建相对布局容器
    RelativeContainer relativeContainer = new RelativeContainer(this);
    relativeContainer.setWidth(ComponentContainer.LayoutConfig.MATCH_PARENT);
    relativeContainer.setHeight(400); // 400vp

// 创建三个组件
Component redBox = createColoredBox(Color.RED, 100, 100);
Component blueBox = createColoredBox(Color.BLUE, 100, 100);
Component greenBox = createColoredBox(Color.GREEN, 100, 100);

// 添加定位规则
// 红色盒子 - 左上角
RelativeContainer.LayoutConfig redConfig = new RelativeContainer.LayoutConfig();
redConfig.addRule(RelativeContainer.ALIGN_PARENT_TOP);
redConfig.addRule(RelativeContainer.ALIGN_PARENT_START);
redBox.setLayoutConfig(redConfig);

// 蓝色盒子 - 右下角
RelativeContainer.LayoutConfig blueConfig = new RelativeContainer.LayoutConfig();
blueConfig.addRule(RelativeContainer.ALIGN_PARENT_BOTTOM);
blueConfig.addRule(RelativeContainer.ALIGN_PARENT_END);
blueBox.setLayoutConfig(blueConfig);

// 绿色盒子 - 居中
RelativeContainer.LayoutConfig greenConfig = new RelativeContainer.LayoutConfig();
greenConfig.addRule(RelativeContainer.CENTER_IN_PARENT);
greenBox.setLayoutConfig(greenConfig);

// 添加组件到容器
relativeContainer.addComponent(redBox);
relativeContainer.addComponent(blueBox);
relativeContainer.addComponent(greenBox);
二、组件间相对定位技巧

  1. 组件对齐实现
    // 标题文本在图片下方
    RelativeContainer.LayoutConfig titleConfig = new RelativeContainer.LayoutConfig();
    titleConfig.addRule(RelativeContainer.BELOW, image.getId()); // 在图片下方
    titleConfig.addRule(RelativeContainer.ALIGN_START, image.getId()); // 左侧对齐
    titleConfig.addRule(RelativeContainer.ALIGN_END, image.getId()); // 右侧对齐
    title.setLayoutConfig(titleConfig);

// 按钮在图片右侧且垂直居中
RelativeContainer.LayoutConfig buttonConfig = new RelativeContainer.LayoutConfig();
buttonConfig.addRule(RelativeContainer.RIGHT_OF, image.getId());
buttonConfig.addRule(RelativeContainer.CENTER_VERTICAL, image.getId());
button.setLayoutConfig(buttonConfig);
2. 复杂锚点关系链
// 创建信息卡片布局链
Component avatar = createAvatar(); // 头像
Component name = createNameText(); // 姓名
Component info = createInfoText(); // 信息
Component action = createActionButton(); // 操作按钮

// 头像定位
RelativeContainer.LayoutConfig avatarConfig = new RelativeContainer.LayoutConfig();
avatarConfig.addRule(RelativeContainer.ALIGN_PARENT_START);
avatarConfig.addRule(RelativeContainer.ALIGN_PARENT_TOP);
avatar.setLayoutConfig(avatarConfig);

// 姓名定位(头像右侧)
RelativeContainer.LayoutConfig nameConfig = new RelativeContainer.LayoutConfig();
nameConfig.addRule(RelativeContainer.RIGHT_OF, avatar.getId());
nameConfig.addRule(RelativeContainer.ALIGN_TOP, avatar.getId());
name.setLayoutConfig(nameConfig);

// 信息定位(姓名下方)
RelativeContainer.LayoutConfig infoConfig = new RelativeContainer.LayoutConfig();
infoConfig.addRule(RelativeContainer.BELOW, name.getId());
infoConfig.addRule(RelativeContainer.ALIGN_START, name.getId());
info.setLayoutConfig(infoConfig);

// 按钮定位(头像下方且靠右)
RelativeContainer.LayoutConfig actionConfig = new RelativeContainer.LayoutConfig();
actionConfig.addRule(RelativeContainer.BELOW, avatar.getId());
actionConfig.addRule(RelativeContainer.ALIGN_PARENT_END);
action.setLayoutConfig(actionConfig);
三、折叠屏适配核心技术

  1. 折叠屏状态检测
    // 获取折叠屏状态
    private FoldState getFoldState() {
    WindowManager windowManager = WindowManager.getInstance();
    WindowSize windowSize = windowManager.getTopWindow().get().getWindowSize();

    // 判断是否为折叠态
    if (windowSize.width < windowSize.height * 1.5) {
    return FoldState.FOLDED; // 折叠状态
    } else {
    return FoldState.EXPANDED; // 展开状态
    }
    }

  2. 双布局方案
    // 根据折叠状态构建不同布局
    private ComponentContainer createAdaptiveLayout() {
    if (getFoldState() == FoldState.EXPANDED) {
    return createExpandedLayout(); // 展开布局
    } else {
    return createFoldedLayout(); // 折叠布局
    }
    }

// 展开布局(宽屏模式)
private ComponentContainer createExpandedLayout() {
RelativeContainer container = new RelativeContainer(this);

// 左侧区域(1/3宽度)
Component leftPanel = createLeftPanel();
RelativeContainer.LayoutConfig leftConfig = new RelativeContainer.LayoutConfig();
leftConfig.width = RelativeContainer.LayoutConfig.MATCH_CONTENT;
leftConfig.addRule(RelativeContainer.ALIGN_PARENT_START);
leftConfig.addRule(RelativeContainer.ALIGN_PARENT_TOP);
leftConfig.addRule(RelativeContainer.ALIGN_PARENT_BOTTOM);
leftPanel.setLayoutConfig(leftConfig);

// 分隔线(动态1vp)
Component divider = createDivider();
RelativeContainer.LayoutConfig dividerConfig = new RelativeContainer.LayoutConfig();
dividerConfig.addRule(RelativeContainer.RIGHT_OF, leftPanel.getId());
dividerConfig.addRule(RelativeContainer.ALIGN_TOP, leftPanel.getId());
dividerConfig.addRule(RelativeContainer.ALIGN_BOTTOM, leftPanel.getId());
divider.setLayoutConfig(dividerConfig);

// 右侧区域(剩余空间)
Component rightPanel = createRightPanel();
RelativeContainer.LayoutConfig rightConfig = new RelativeContainer.LayoutConfig();
rightConfig.width = RelativeContainer.LayoutConfig.MATCH_PARENT;
rightConfig.addRule(RelativeContainer.RIGHT_OF, divider.getId());
rightConfig.addRule(RelativeContainer.ALIGN_TOP, divider.getId());
rightConfig.addRule(RelativeContainer.ALIGN_BOTTOM, divider.getId());
rightPanel.setLayoutConfig(rightConfig);

container.addComponent(leftPanel);
container.addComponent(divider);
container.addComponent(rightPanel);
return container;

}
四、动态布局重构技术

  1. 监听屏幕变化
    @Override
    protected void onActive() {
    super.onActive();

    // 注册折叠屏状态监听
    WindowManager.getInstance().registerSystemLayoutChangeListener(layoutChangeListener);
    }

@Override
protected void onInactive() {
super.onInactive();

// 注销监听器
WindowManager.getInstance().unregisterSystemLayoutChangeListener(layoutChangeListener);

}

// 布局变化监听器
private ISystemLayoutChangeListener layoutChangeListener = new ISystemLayoutChangeListener() {
@Override
public void onDisplayRotateChanged(int rotation, int width, int height) {
// 响应显示旋转
rebuildLayout();
}

@Override
public void onDisplaySizeChanged(int width, int height) {
    // 响应显示尺寸变化
    rebuildLayout();
}

};
2. 动态锚点重构
// 动态重布局方法
private void rebuildLayout() {
FoldState currentState = getFoldState();

if (currentState != lastState) {
    // 移除旧组件
    relativeContainer.removeAllComponents();
    
    // 重建布局
    if (currentState == FoldState.EXPANDED) {
        buildWideScreenLayout();
    } else {
        buildCompactLayout();
    }
    
    lastState = currentState;
}

}

// 宽屏布局(双列)
private void buildWideScreenLayout() {
// 创建双列布局(代码同前文createExpandedLayout)
}

// 紧凑布局(单列)
private void buildCompactLayout() {
Component header = createHeader();
RelativeContainer.LayoutConfig headerConfig = new RelativeContainer.LayoutConfig();
headerConfig.addRule(RelativeContainer.ALIGN_PARENT_TOP);
headerConfig.addRule(RelativeContainer.ALIGN_PARENT_START);
headerConfig.addRule(RelativeContainer.ALIGN_PARENT_END);
header.setLayoutConfig(headerConfig);

Component content = createContent();
RelativeContainer.LayoutConfig contentConfig = new RelativeContainer.LayoutConfig();
contentConfig.addRule(RelativeContainer.BELOW, header.getId());
contentConfig.addRule(RelativeContainer.ALIGN_PARENT_START);
contentConfig.addRule(RelativeContainer.ALIGN_PARENT_END);
contentConfig.addRule(RelativeContainer.ALIGN_PARENT_BOTTOM);
content.setLayoutConfig(contentConfig);

relativeContainer.addComponent(header);
relativeContainer.addComponent(content);

}
五、折叠屏悬停区域适配

  1. 悬停区域避让
    // 获取折叠屏折痕区域
    private Rect getFoldCreaseArea() {
    DisplayAttributes attributes = DisplayManager.getInstance()
    .getDefaultDisplay(getContext()).get().getAttributes();

    Rect crease = new Rect();
    if (attributes.hasFoldArea) {
    crease.left = attributes.foldArea.x;
    crease.top = attributes.foldArea.y;
    crease.right = attributes.foldArea.x + attributes.foldArea.width;
    crease.bottom = attributes.foldArea.y + attributes.foldArea.height;
    }
    return crease;
    }

// 避让折痕区域
private void avoidCreaseArea(Component component) {
Rect crease = getFoldCreaseArea();
if (crease.isEmpty()) return;

RelativeContainer.LayoutConfig config = 
    (RelativeContainer.LayoutConfig) component.getLayoutConfig();

// 检测组件是否与折痕区域重叠
Rect compRect = component.getBoundsRect();
if (Rect.intersects(compRect, crease)) {
    // 在折痕左侧添加右边距
    config.setMarginRight(crease.left - compRect.right + 20); // 20vp安全距离
}

}
六、折叠动效与布局过渡

  1. 布局变换动画
    private void animateLayoutChange() {
    // 创建布局动画
    AnimatorProperty animator = new AnimatorProperty();
    animator.setDuration(300);
    animator.setCurve(new CubicCurve(0.4f, 0.0f, 0.2f, 1.0f));

    // 在动画开始前记录初始状态
    for (Component comp : relativeContainer.getAllComponents()) {
    comp.setTag(LAYOUT_KEY_OLD_BOUNDS, comp.getBoundsRect());
    }

    // 先执行布局重建
    rebuildLayout();

    // 重建后立即设置初始状态
    for (Component comp : relativeContainer.getAllComponents()) {
    Rect oldRect = (Rect) comp.getTag(LAYOUT_KEY_OLD_BOUNDS);
    if (oldRect != null) {
    animator.moveFromX(oldRect.left)
    .moveToX(comp.getBoundsRect().left)
    .moveFromY(oldRect.top)
    .moveToY(comp.getBoundsRect().top)
    .scaleXFrom(0.9f).scaleXTo(1.0f)
    .scaleYFrom(0.9f).scaleYTo(1.0f);
    }
    }

    // 执行动画
    animator.start(relativeContainer);
    }
    七、完整示例:折叠屏适配的详情页
    public class DetailPage extends AbilitySlice {
    private RelativeContainer relativeContainer;
    private FoldState lastState = FoldState.UNKNOWN;

    @Override
    public void onStart(Intent intent) {
    super.onStart(intent);

     // 创建相对布局容器
     relativeContainer = new RelativeContainer(this);
     relativeContainer.setWidth(ComponentContainer.LayoutConfig.MATCH_PARENT);
     relativeContainer.setHeight(ComponentContainer.LayoutConfig.MATCH_PARENT);
     
     // 初始布局
     rebuildLayout();
     setUIContent(relativeContainer);
     
     // 注册监听
     WindowManager.getInstance().registerSystemLayoutChangeListener(layoutChangeListener);
    

    }

    private void rebuildLayout() {
    relativeContainer.removeAllComponents();

     if (getFoldState() == FoldState.EXPANDED) {
         createDualPaneLayout();
     } else {
         createSinglePaneLayout();
     }
    

    }

    private void createDualPaneLayout() {
    // 左侧 - 列表
    Component listPane = createListPane();
    RelativeContainer.LayoutConfig listConfig = new RelativeContainer.LayoutConfig();
    listConfig.width = 360; // 固定360vp宽度
    listConfig.addRule(RelativeContainer.ALIGN_PARENT_START);
    listConfig.addRule(RelativeContainer.ALIGN_PARENT_TOP);
    listConfig.addRule(RelativeContainer.ALIGN_PARENT_BOTTOM);
    listPane.setLayoutConfig(listConfig);

     // 右侧 - 详情
     Component detailPane = createDetailPane();
     RelativeContainer.LayoutConfig detailConfig = new RelativeContainer.LayoutConfig();
     detailConfig.width = RelativeContainer.LayoutConfig.MATCH_PARENT;
     detailConfig.addRule(RelativeContainer.RIGHT_OF, listPane.getId());
     detailConfig.addRule(RelativeContainer.ALIGN_PARENT_TOP);
     detailConfig.addRule(RelativeContainer.ALIGN_PARENT_BOTTOM);
     detailPane.setLayoutConfig(detailConfig);
     
     relativeContainer.addComponent(listPane);
     relativeContainer.addComponent(detailPane);
    

    }

    private void createSinglePaneLayout() {
    // 顶部应用栏
    Component appBar = createAppBar();
    RelativeContainer.LayoutConfig appBarConfig = new RelativeContainer.LayoutConfig();
    appBarConfig.width = RelativeContainer.LayoutConfig.MATCH_PARENT;
    appBarConfig.height = 56;
    appBarConfig.addRule(RelativeContainer.ALIGN_PARENT_TOP);
    appBar.setLayoutConfig(appBarConfig);

     // 内容区域
     Component content = createContentPane();
     RelativeContainer.LayoutConfig contentConfig = new RelativeContainer.LayoutConfig();
     contentConfig.width = RelativeContainer.LayoutConfig.MATCH_PARENT;
     contentConfig.addRule(RelativeContainer.BELOW, appBar.getId());
     contentConfig.addRule(RelativeContainer.ALIGN_PARENT_BOTTOM);
     content.setLayoutConfig(contentConfig);
     
     relativeContainer.addComponent(appBar);
     relativeContainer.addComponent(content);
    

    }

    // 其他组件创建方法省略…
    }
    最佳实践总结
    ​​锚点定位要点​​:
    // 精确控制相对位置
    config.addRule(RelativeContainer.BELOW, headerId);
    config.addRule(RelativeContainer.ALIGN_START, parentId);
    ​​折叠屏适配四要素​​:
    // 1. 检测折叠状态
    FoldState state = getFoldState();

// 2. 设计双布局方案
if (state == EXPANDED) createWideLayout();
else createCompactLayout();

// 3. 避让折痕区域
avoidCreaseArea(sensitiveComponent);

// 4. 添加布局变换动画
animateLayoutTransition();
​​性能优化建议​​:
// 复用布局配置对象
RelativeContainer.LayoutConfig reusableConfig = new LayoutConfig();

// 批量更新减少重绘
relativeContainer.postLayout(() -> {
// 批量更新布局规则
updateAllLayoutConfigs();
});
​​响应式设计规则​​:
// 动态调整间距
int margin = isWideScreen ? 24 : 16; // vp单位
config.setMarginLeft(margin);
RelativeContainer 的锚点定位系统结合折叠屏响应式设计,能够为鸿蒙应用提供:

复杂布局的精确控制能力
多形态设备的无缝适配
流畅的用户体验过渡
高效的开发实现流程
通过以上技术和最佳实践,您可以创建出既美观又功能强大的鸿蒙应用界面。

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