HarmonyOS 鸿蒙Next Web 同层组件如何设置自适应高度
HarmonyOS 鸿蒙Next Web 同层组件如何设置自适应高度
背景是在 Web 中基于同层机制渲染了一个 TextArea 的组件,想实现随着 TextArea 随着内容的输入高度自适应,但遇到一个问题:
如果不显示的指定 embed 标签的高度(比如设置 auto),会导致最终计算出来的高度为 0,无法显示;但如果设置了具体的高度,比如 100px, TextArea 无法随着输入自动增加高度。
效果图:
示例 html:
示例 html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>同层渲染测试html</title>
</head>
<body>
<h1>同层渲染测试</h1>
<div class="main">
<p>
ChatGPT,全称聊天生成预训练转换器[2](英语:Chat Generative Pre-trained
Transformer[3]),是OpenAI开发的人工智能聊天机器人程序,于2022年12月推出。该程序使用基于GPT-3.5、GPT-4、GPT-4o架构的大型语言模型并以强化学习训练。ChatGPT目前仍以文字方式交互,而除了可以用人类自然对话方式来交互,还可以用于甚为复杂的语言工作,包括自动生成文本、自动问答、自动摘要等多种任务。如:在自动文本生成方面,ChatGPT可以根据输入的文本自动生成类似的文本(剧本、歌曲、企划等),在自动问答方面,ChatGPT可以根据输入的问题自动生成答案。还有编写和调试计算机程序的能力。[4]在推广期间,所有人可以免费注册,并在登录后免费使用ChatGPT与AI机器人对话[5]。
</p>
<embed id="1" type="native/textarea" style="width: 100%; height: 100px"/>
<p>
ChatGPT是生成型预训练变换模型(GPT),在GPT-3.5之上用基于人类反馈的监督学习和强化学习微调。[16]这两种方法都用人类教练来提高模型性能,以人类干预增强机器学习效果,获得更逼真的结果[17]。在监督学习的情况下为模型提供这样一些对话,在对话中教练充当用户和AI助理两种角色。在强化步骤中,人类教练首先为模型在先前对话中建立的响应评级。这些级别用于建立“奖励模型”,使用近端策略优化(PPO)的多次迭代来微调[18][19]。这种策略优化算法比信任域策略优化(trust
region policy optimization)算法更为高效[20][21]。
</p>
</div>
</body>
</html>
复制
Ark 代码:
@Entry
@Component
struct Index {
private webviewController = new webview.WebviewController();
@State componentIdArr: Array<String> = [];
private componentIdToNodeController: Map<String, ComponentNodeController> = new Map();
build() {
Stack() {
ForEach(this.componentIdArr, (componentId: string) => {
NodeContainer(this.componentIdToNodeController.get(componentId))
}, (componentId: string) => componentId)
Web({ src: $rawfile('test.html'), controller: this.webviewController })
.width('100%')
.enableNativeEmbedMode(true)
.registerNativeEmbedRule('embed', 'native')
.onNativeEmbedLifecycleChange((embed) => {
const componentId = embed.info?.id?.toString() as string
if (embed.status === NativeEmbedStatus.CREATE) {
let nodeController = new ComponentNodeController();
nodeController.setRenderOption({
surfaceId: embed.surfaceId as string,
renderType: NodeRenderType.RENDER_TYPE_TEXTURE,
embedId: embed.embedId as string,
width: px2vp(embed.info?.width),
height: px2vp(embed.info?.height),
});
nodeController.rebuild();
this.componentIdToNodeController.set(componentId, nodeController);
this.componentIdArr.push(componentId);
} else if (embed.status === NativeEmbedStatus.UPDATE) {
let nodeController = this.componentIdToNodeController.get(componentId);
nodeController?.updateNode({
width: px2vp(embed.info?.width),
height: px2vp(embed.info?.height),
} as ESObject);
nodeController?.rebuild();
} else {
let nodeController = this.componentIdToNodeController.get(componentId);
nodeController?.setBuilderNode(null);
nodeController?.rebuild();
}
})
.onNativeEmbedGestureEvent((touch) => { // 获取同层渲染组件触摸事件信息
this.componentIdArr.forEach((componentId) => {
let nodeController = this.componentIdToNodeController.get(componentId);
if (nodeController?.getEmbedId() === touch.embedId) {
nodeController?.postTouchEvent(touch.touchEvent);
}
})
})
}
.height('100%')
.margin({ top: '107px' })
}
}
declare class ComponentNodeControllerParams {
surfaceId: string
renderType: NodeRenderType
embedId: string
width: number
height: number
position?: Position
}
class ComponentNodeController extends NodeController {
private rootNode: BuilderNode<ComponentParams[]> | undefined | null;
private surfaceId: string = "";
private renderType: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
private embedId: string = "";
private componentWidth: number = 0;
private componentHeight: number = 0;
setRenderOption(params: ComponentNodeControllerParams) {
hilog.info(0x0000, TAG, `setRenderOption: ${JSON.stringify(params)}`)
this.surfaceId = params.surfaceId;
this.renderType = params.renderType;
this.embedId = params.embedId;
this.componentWidth = params.width;
this.componentHeight = params.height;
}
makeNode(uiContext: UIContext): FrameNode | null {
if (!this.rootNode) {
this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId, type: this.renderType });
this.rootNode.build(wrapBuilder(TextAreaComponentBuilder), {
width: this.componentWidth,
height: this.componentHeight,
});
}
return this.rootNode.getFrameNode();
}
setBuilderNode(rootNode: BuilderNode<ComponentParams[]> | null): void {
this.rootNode = rootNode;
}
updateNode(arg: Object): void {
this.rootNode?.update(arg);
}
getEmbedId(): string {
return this.embedId;
}
postTouchEvent(event: TouchEvent | undefined): boolean {
return this.rootNode?.postTouchEvent(event) as boolean
}
}
declare class ComponentParams {
width: number
height: number
}
@Builder
function TextAreaComponentBuilder(params: ComponentParams) {
TextAreaComponent({
componentWidth: params.width,
componentHeight: params.height,
})
}
@Component
struct TextAreaComponent {
componentWidth?: number
componentHeight?: number
private controller = new TextAreaController();
build() {
Column() {
TextArea({ placeholder: "TextArea", controller: this.controller })
.width('100%')
.fontSize(18)
}
.width('100%')
}
}
2 回复
TextArea组件不设置height属性时,可自适应最大高度,另外可以用constraintSize({minHeight: 50})设置最小高度,此时最大高度也会自适应
针对HarmonyOS 鸿蒙Next Web同层组件设置自适应高度的问题,以下是一些专业建议:
- CSS布局支持:确保H5页面使用了能够支持高度自适应的CSS布局,如Flexbox或Grid布局。同时,在CSS中设置
html, body { height: 100%; }
,以确保容器可以扩展到视窗高度。 - JavaScript动态调整:在H5页面的body底部动态添加一个div元素,利用该元素的offsetTop属性值来确定Web组件的高度,并据此调整父容器的高度。可以通过JavaScript获取新元素的高度,并手动调整容器高度,但更常见的是使用CSS的min-height或flex-grow等属性来自动调整。
- 监听窗口变化:使用window.onresize事件监听器,确保在窗口大小变化时,动态调整的元素也能相应地调整其大小或位置。
- nestedScroll属性:在Scroll组件中嵌套Web组件时,应使用nestedScroll属性,并设置layoutMode为followSystem或followContent,同时确保Web组件有明确的宽度设置。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html 。