HarmonyOS鸿蒙Next中2in1设备HdsNavigation的titleBar会侵入WindowDecor

HarmonyOS鸿蒙Next中2in1设备HdsNavigation的titleBar会侵入WindowDecor 在EntryAbility中设置了

win.setWindowLayoutFullScreen(false)

在其他设备上HdsNavigation的titleBar的避让都正常,但在2in1设备中HdsNavigation的titleBar会侵入WindowDecor,如何解决

cke_4941.png

cke_12575.png

cke_13792.png

cke_13105.png


更多关于HarmonyOS鸿蒙Next中2in1设备HdsNavigation的titleBar会侵入WindowDecor的实战教程也可以访问 https://www.itying.com/category-93-b0.html

9 回复

开发者你好,经确认,当前HdsNavigation规格如此,请问您是否要走需求流程,如果走需求麻烦提供以下信息:

"尊敬的开发者,您好!

请问您是在什么样的业务场景中使用该能力,交互流程是怎样的,在哪一个环节遇到了问题?另外请您说明能力不满足可能带来的影响:什么时间用到?是否高频?有无三方库可以做到?若提供该能力,是否会造成大工作量返工?请您注意提供的内容不要包含您或第三方的非公开信息,如给您带来不便,敬请谅解。"

更多关于HarmonyOS鸿蒙Next中2in1设备HdsNavigation的titleBar会侵入WindowDecor的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


当设置win.setWindowDecorVisible(false)后WindowDecor自动下沉到内容区内,目前的规格没有主动避让会导致我在2in1设备上使用HdsNavigation的title时不管什么断点都会入侵到WindowDecor,具体情况可以看我发的截图。因为此问题无法解决,我现在已经采取放弃使用HdsNavigation的title,在每个页面上手搓了一个title出来。但尽管这样,通过手搓方式实现的title要实现HdsNavigation的title中的滚动模糊效果还是有些许麻烦的,且每个页面都需要逐一适配。所以如果可以,我想官方能实现对这个WindowDecor的高度进行避让(28vp)。

尊敬的开发者,您好!该功能正在规划中,还请关注后续版本,感谢您的理解与支持。

尊敬的开发者,您好!您的问题已复现,感谢您的反馈,问题正在加速处理中,还请关注后续版本,感谢您的理解与支持。

//EntryAbility
onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });

    windowStage.getMainWindow((err:BusinessError,win)=>{
      win.setWindowDecorVisible(false)
      win.setWindowLayoutFullScreen(true)
    })
  }
//Index.ets
import { HdsNavigation, HdsNavigationTitleMode, HdsTabs,
  ScrollEffectType } from '@hms.hds.hdsBaseComponent';
import { HomeView } from '../views/HomeView';
import { TestView } from '../views/TestView';
import { MineView } from '../views/MineView';
import { LengthMetrics, SymbolGlyphModifier } from '@kit.ArkUI';
import { HdsSideBar, HdsSideMenu, HdsSideMenuMainItem } from '@kit.UIDesignKit';

@Entry
@ComponentV2
struct Index {
  @Local stack: NavPathStack = new NavPathStack();

  @Local mainTitle: string = "我的设备";

  @Local currentIndex:number=0

  scroller: Scroller = new Scroller();
  @Local sideBarWidth:string='100%'

  listOptionsDefault?: HdsSideMenuMainItem[] = [
    new HdsSideMenuMainItem({
      symbol: new SymbolGlyphModifier($r('sys.symbol.doc_plaintext')),
      label: '主页',
    }),
    new HdsSideMenuMainItem({
      symbol: new SymbolGlyphModifier($r('sys.symbol.star')),
      label: '测试',
    }),
    new HdsSideMenuMainItem({
      symbol: new SymbolGlyphModifier($r('sys.symbol.lock')),
      label: '设置',
    })
  ]

  @Builder
  SideBarPanelBuilder() {
    Column() {
      Text('左侧侧边栏区')
      HdsSideMenu({
        items: this.listOptionsDefault,
        selectedIndex: this.currentIndex,
        $selectedIndex: (selectedIndex: number) => {
          this.currentIndex = selectedIndex;
          this.mainTitle=selectedIndex===0?'我的设备':selectedIndex===1?"测试":"我的"
        },
      })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Transparent)
    .linearGradient({
      direction: GradientDirection.Bottom,
      colors: [['#ff748573', 0.0], [Color.White, 1]]
    })
  }

  //右侧内容区
  @Builder
  ContentPanelBuilder() {
    if (this.currentIndex===0){
      HomeView()
    }else if (this.currentIndex===1) {
      TestView()
    }else if (this.currentIndex===2) {
      MineView()
    }
  }

  @BuilderParam contentBuilder: () => void = this.ContentPanelBuilder
  @BuilderParam sideBarBuilder: () => void = this.SideBarPanelBuilder

  build() {
    HdsNavigation(this.stack){
      HdsSideBar({
        sideBarPanelBuilder: (): void => {
          this.sideBarBuilder()
        },
        contentPanelBuilder: (): void => {
          this.contentBuilder()
        },
        sideBarContainerType: SideBarContainerType.Embed,
        isShowSideBar: true,
      })
        .linearGradient({
          direction: GradientDirection.Bottom,
          colors: [['#ff748573', 0.0], [Color.White, 1]]
        })
    }
    .titleBar({
      style: {
        scrollEffectOpts: {
          enableScrollEffect: true,
          scrollEffectType: ScrollEffectType.GRADUAL_BLUR,
          blurEffectiveStartOffset: LengthMetrics.vp(0),
          blurEffectiveEndOffset: LengthMetrics.vp(20)
        },
        originalStyle: {
          backgroundStyle: {
            backgroundColor: Color.Transparent
          },
          contentStyle: {
            titleStyle: { mainTitleColor: '#ffe2e2e2' }
          }
        },
        scrollEffectStyle: {
          backgroundStyle: {
            backgroundColor: Color.Transparent
          },
          contentStyle: {
            titleStyle: { mainTitleColor: '#ffe2e2e2' }
          }
        },
      },
      content: {
        title: {
          mainTitle: this.mainTitle
        },
        menu:{
          value:[{
            content:{
              label:'设置',
              icon:$r('sys.symbol.gearshape'),
              isEnabled:true,
              action:()=>{
                this.stack.pushPathByName('SettingView',null,true)
              }
            }
          }]
        }
      },
    })
    .systemBarStyle({ statusBarContentColor: '#ffe2e2e2' }, { statusBarContentColor: '#ffe2e2e2' })
    .bindToScrollable([this.scroller])
    .ignoreLayoutSafeArea()
    .hideToolBar(true)
    .titleMode(HdsNavigationTitleMode.MODAL)
    .linearGradient({
      direction: GradientDirection.Bottom,
      colors: [['#ff748573', 0.0], [Color.White, 1]]
    })
    .navBarWidth(this.sideBarWidth)
  }
}

开发者你好,我使用DevEco Studio 6.0.1 Release和对应API21版本的模拟器运行下面的代码未能复现您的问题。麻烦您补充以下信息:

1、复现代码(如最小复现demo); 2、版本信息(如:开发工具、手机系统版本信息);

import { HdsNavigation, HdsNavigationAttribute, HdsNavigationTitleMode, ScrollEffectType } from '@kit.UIDesignKit';

@Entry
@Component
struct Index {
  @State pageIndex: number = 0;
  @State toolItems: Array<ToolbarItem> = [
    {
      value: '首页',
      icon: '',
      action: () => {
        this.pageIndex = 0
      },
      status: this.pageIndex == 0 ? ToolbarItemStatus.ACTIVE : ToolbarItemStatus.NORMAL
    },
    {
      value: '媒体库',
      icon: '',
      action: () => {
        this.pageIndex = 1
      },
      status: this.pageIndex == 1 ? ToolbarItemStatus.ACTIVE : ToolbarItemStatus.NORMAL
    },
    {
      value: '设置',
      icon: '',
      action: () => {
        this.pageIndex = 2
      },
      status: this.pageIndex == 2 ? ToolbarItemStatus.ACTIVE : ToolbarItemStatus.NORMAL
    }
  ]

  build() {
    HdsNavigation() {
    }
    .height('100%')
    .width('100%')
    .hideBackButton(true)
    .titleBar({
      style: {
        scrollEffectOpts: {
          enableScrollEffect: true,
          scrollEffectType: ScrollEffectType.COMMON_BLUR
        }
      },
      content: {
        title: {
          mainTitle: '我的设备'
        },
        menu: {
          value: [
            {
              content: {
                action: () => {
                }
              }
            }
          ]
        }
      }
    })
    .titleMode(HdsNavigationTitleMode.FULL)
    .toolbarConfiguration(this.toolItems, {
      backgroundColor:'#00ffffff',
      backgroundBlurStyle: BlurStyle.BACKGROUND_ULTRA_THICK,
      backgroundBlurStyleOptions: {
        policy: BlurStyleActivePolicy.ALWAYS_ACTIVE
      },
      hideItemValue: false,
      barStyle: BarStyle.STACK
    })
  }
}
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
import numpy as np

# 定义数据集类
class CustomDataset(Dataset):
    def __init__(self, data, labels):
        self.data = data
        self.labels = labels

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return self.data[idx], self.labels[idx]

# 定义简单神经网络模型
class SimpleNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleNN, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x

# 生成模拟数据
def generate_data(num_samples=1000, input_size=10):
    data = np.random.randn(num_samples, input_size).astype(np.float32)
    labels = np.random.randint(0, 2, size=(num_samples,)).astype(np.int64)
    return data, labels

# 训练函数
def train_model(model, dataloader, criterion, optimizer, num_epochs=10):
    model.train()
    for epoch in range(num_epochs):
        running_loss = 0.0
        for inputs, labels in dataloader:
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
        print(f"Epoch {epoch+1}/{num_epochs}, Loss: {running_loss/len(dataloader):.4f}")

# 主函数
def main():
    # 设置随机种子
    torch.manual_seed(42)
    np.random.seed(42)

    # 生成数据
    data, labels = generate_data()
    dataset = CustomDataset(data, labels)
    dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

    # 初始化模型、损失函数和优化器
    input_size = 10
    hidden_size = 20
    output_size = 2
    model = SimpleNN(input_size, hidden_size, output_size)
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)

    # 训练模型
    train_model(model, dataloader, criterion, optimizer)

if __name__ == "__main__":
    main()

在HarmonyOS Next中,2in1设备上HdsNavigation组件的titleBar默认会侵入WindowDecor区域。这是系统为适配折叠屏或平板等大屏设备的默认UI行为,旨在优化屏幕空间利用率。

在HarmonyOS Next的2in1设备上,HdsNavigation的titleBar侵入WindowDecor区域,通常是由于设备形态(如平板模式、桌面模式)切换或窗口管理策略差异导致的。您已正确设置了win.setWindowLayoutFullScreen(false)来退出全屏,但问题可能出在导航栏对系统装饰区域(SystemBar)的适配逻辑上。

针对此问题,您可以尝试以下方案:

  1. 显式设置窗口系统栏可见性:在您的EntryAbility的onWindowStageCreate生命周期中,在设置非全屏布局后,立即显式地设置系统栏的可见性。这可以确保系统装饰区域被正确保留。

    import { window } from '@kit.ArkUI';
    
    onWindowStageCreate(windowStage: window.WindowStage) {
      // ... 其他代码 ...
      let win = windowStage.getMainWindow();
      // 1. 先设置窗口为非全屏布局
      win.setWindowLayoutFullScreen(false);
      // 2. 关键步骤:显式设置系统栏为可见状态
      win.setWindowSystemBarEnable(['status', 'navigation']);
      // ... 其他代码,例如加载页面 ...
    }
    

    setWindowSystemBarEnable(['status', 'navigation']) 会明确告知系统,状态栏和导航栏需要被保留,从而为它们预留出空间。这可以防止HdsNavigation等组件侵入该区域。

  2. 检查并适配窗口安全区域:确保您的UI布局考虑了系统安全区域(Safe Area)。您可以使用getWindowAvoidArea API获取需要避让的区域(包括系统栏区域),并在布局时进行相应偏移。

    import { window } from '@kit.ArkUI';
    import { BusinessError } from '@kit.BasicServicesKit';
    
    let win = windowStage.getMainWindow();
    try {
      let avoidArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
      // avoidArea 是一个 Rect 对象,包含top, bottom, left, right四个属性
      // 您可以根据这些值来调整HdsNavigation或页面内容的布局位置,例如设置paddingTop。
    } catch (error) {
      let err: BusinessError = error as BusinessError;
      console.error(`Failed to get avoid area. Code: ${err.code}, message: ${err.message}`);
    }
    

    在UI组件(例如Column)的样式中,可以使用paddingTop: avoidArea.top来避开顶部的状态栏区域。

  3. 针对2in1设备形态进行条件判断:2in1设备在不同模式(如平板、笔记本)下,系统栏的行为可能不同。您可以通过display.getDefaultDisplay()display.get相关接口获取当前设备的显示信息,并据此调整UI策略。

问题根源分析:在2in1设备上,当应用窗口未设置为全屏时,系统期望应用内容自动避让系统装饰区。HdsNavigation组件可能在某些设备形态下,其内部计算布局的逻辑未能准确获取到最新的系统栏避让区域,导致其titleBar绘制在了预留区域之上。上述方案1通过显式启用系统栏,是最直接有效的解决方式,它强制系统重新计算并应用正确的布局边界。

请优先尝试方案1。如果问题依旧存在,请结合方案2,在获取安全区域后,动态调整包含HdsNavigation的容器布局的paddingmargin

回到顶部