网上的解决方案都是在Dialog组件的根容器中设置偏移量.offset({x:0,y:16}) 大概这种的,这种垃圾解决方式最不可靠,倘若dialog输入框时根据状态变量动态显示的话,即使设置了也没有用
正宗解决方案
首先自定义dialog 三个地方需要注意
1、customStyle 设置为true 2、offset重置为0
3、不启用默认键盘 keyboardAvoidMode: KeyboardAvoidMode.NONE,
private dialogController: CustomDialogController = new CustomDialogController({
builder: CustomBottomDialog({
keywords: this.keywords,
searchType: this.searchType,
}),
alignment: DialogAlignment.Bottom,
offset: { dx: 0, dy: 0 },
customStyle: true,
maskColor: 'rgba(0, 0, 0, 0.5)',
keyboardAvoidMode: KeyboardAvoidMode.NONE,
onWillDismiss: () => {
return false
}
})
然后在@CustomDialog装饰器装饰的组件 aboutToAppear中监听键盘显示与隐藏变化,获取键盘高度

async aboutToAppear() {
this.fetchDistributeCommentData();
window.getLastWindow(getContext(this)).then(currentWindow => {
currentWindow.setWindowLayoutFullScreen(true);
let property = currentWindow.getWindowProperties();
let avoidArea = currentWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_KEYBOARD);
this.scrollHeight = px2vp(property.windowRect.height - avoidArea.bottomRect.height);
currentWindow.on('avoidAreaChange', data => {
if (data.type == window.AvoidAreaType.TYPE_KEYBOARD) {
this.keyHeight = px2vp(data.area.bottomRect.height);
this.scrollHeight =
px2vp(currentWindow.getWindowProperties().windowRect.height - data.area.bottomRect.height);
this.autoHeight = 250;
this.scroller.scrollTo({
xOffset: 0,
yOffset: 250
})
return;
}
this.autoHeight = 'auto';
})
})
}
紧接着设置根容器Colum(){}. margin({ bottom:this.keyHeight})
build() {
Column() {
Flex({
direction: FlexDirection.Row,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceBetween
}) {
Row()
Text(this.moduleName)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor($r('app.color.color_1B1E2E'))
Image($r('app.media.gtui_nav_icon_close'))
.width(24)
.height(24)
.onClick(() => {
this.dialogController?.close();
})
}.padding({
left: 16,
right: 16
})
.margin({
bottom: 30
})
Column() {
Scroll(this.scroller) {
Column() {
Text(this.title)
.font({
size: 16,
weight: FontWeight.Bold,
})
.fontColor($r('app.color.color_1B1E2E'))
.alignSelf(ItemAlign.Start)
.margin({
left: 16
})
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceBetween,
}) {
ForEach(this.commentScoreEmojiVOList, (item: IDistributeCommentDataCommentScoreEmojiVOList) => {
Column() {
Image(this.getEmojiIcon(item.isActive ?? false, item.score))
.width(40)
.height(40)
Text(item.text)
.fontSize(12)
.margin({
top: 16
})
}.onClick(() => {
this.score = item.score;
this.showAdviseInput = true;
let commentScoreEmojiVOList =
this.commentScoreEmojiVOList.map((el: IDistributeCommentDataCommentScoreEmojiVOList) => {
el.isActive = false;
if (item.score === el.score) {
el.isActive = true;
}
return el;
})
this.commentScoreEmojiVOList = [...commentScoreEmojiVOList];
if (Array.isArray(item.labelList) && item.labelList.length > 0) {
this.labelList = item.labelList.map(label => {
return { label, isActive: false } as IDistributeCommentDataLabel
});
this.isDisabled = true;
} else {
this.labelList = [];
this.isDisabled = false;
}
})
})
}.padding({
left: 16,
right: 16
})
.margin({
top: 30
})
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceBetween,
wrap: FlexWrap.Wrap,
}) {
ForEach(this.labelList, (item: IDistributeCommentDataLabel) => {
Button(item.label)
.fontSize(12)
.fontColor(item.isActive ? $r('app.color.brand_500') : $r('app.color.color_1B1E2E'))
.type(ButtonType.Normal)
.borderRadius(3)
.height(28)
.width('31.5%')
.backgroundColor(item.isActive ? $r('app.color.brand_50') : $r('app.color.gray1'))
.onClick(() => {
let labelList = this.labelList.map(el => {
if (el.label.trim().includes(item.label.trim())) {
el.isActive = !item.isActive;
}
return el;
})
this.labelList = [...labelList];
this.isDisabled = false;
this.evaluateName = item.label;
this.selectLabelList = (labelList.filter(item => item.isActive)).map(item => item.label);
})
})
}.visibility(this.labelList.length > 0 ? Visibility.Visible : Visibility.None)
.margin({
top: 24
})
.padding({
left: 16,
right: 16
})
Row() {
TextArea({
placeholder: this.commentTextBoxVO?.tip ?? '',
controller: this.textAreaController,
})
.onChange((text) => {
this.content = text;
})
.onSubmit(() => {
})
.borderRadius(6)
.height(150)
.padding(16)
.enterKeyType(EnterKeyType.NEW_LINE)
.lineHeight(18)
.placeholderColor($r('app.color.fontGy3'))
.placeholderFont({
size: 12
})
.caretColor($r('app.color.brand_500'))
.backgroundColor($r('app.color.gray1'))
.fontSize(12)
.fontColor($r('app.color.fontGy1'))
.layoutWeight(1)
}.margin({
top: 24,
}).padding({
left: 16,
right: 16
})
.visibility(this.showAdviseInput ? Visibility.Visible : Visibility.None)
}
}.scrollBar(BarState.Off)
}.height(this.autoHeight)
Button('确认提交')
.fontSize(16)
.fontWeight(FontWeight.Normal)
.backgroundColor(this.isDisabled ? $r('app.color.brand_200') : $r('app.color.brand_500'))
.width('72%')
.height(44)
.stateEffect(false)
.onClick(() => {
if (this.isDisabled) {
return;
}
this.submitEvaluate();
})
.margin({ top: 44 })
}
.backgroundColor(Color.White)
.borderRadius({
topLeft: 10,
topRight: 10
})
.padding({
top: 24,
bottom: getNaviIndicatorHeightVP()
})
.margin({
bottom: this.keyHeight,
})
}
这样就完美实现当键盘输入时,键盘就会自动定起来弹窗,并且也不会遮盖住底部提交按钮。
并且你也可以基于键盘显示与隐藏监听逻辑判断,使其在键盘弹出时,弹窗部分内容在有限高度里滚动显示,默认可以设置当键盘弹出时,内容直接滚动到底部显示。


完整代码片段
import { showToast, showToastCenter } from '@gaotu/library_ui/src/main/ets/utils/ToastUtils';
import { ApiConstants } from '../constants/ApiConstants';
import {
IDistributeCommentData,
IDistributeCommentDataCommentScoreEmojiVOList,
IDistributeCommentDataCommentTextBoxVO,
IDistributeCommentDataLabel
} from '../interfaces/IDistributeComment';
import { requestDistributeCommentApi } from '../services/SyncRequest';
import { BusinessError } from '@kit.BasicServicesKit';
import { SearchTypeEnum } from '../pages/SearchCoursePage';
import { ISubmitCommentResultData } from '../interfaces/ISubmitCommentResult';
import dayjs from 'dayjs';
import { getNaviIndicatorHeightVP } from '@gaotu/library_ui';
import { window } from '@kit.ArkUI';
@CustomDialog
@Component
export struct CustomBottomDialog {
@Prop searchType: SearchTypeEnum;
@Prop keywords: string;
dialogController?: CustomDialogController
@State moduleName: string = '';
@State title: string = '';
@State commentId: string = '';
@State commentScoreEmojiVOList: IDistributeCommentDataCommentScoreEmojiVOList[] = [];
@State commentTextBoxVO: IDistributeCommentDataCommentTextBoxVO | undefined = undefined;
@State labelList: IDistributeCommentDataLabel[] = []
@State selectLabelList: string[] = [];
@State showAdviseInput: boolean = false;
@State moduleId: string = '';
@State isDisabled: boolean = true;
@State score: number = 0;
@State content: string = '';
@State evaluateName: string = '';
@State currentMillSeconds: number = dayjs().valueOf();
@State scrollHeight: number = 0;
@State isRebuild: boolean = false;
@State keyHeight: number = 0;
@State autoHeight: number | string = 'auto';
submitEvaluate = async () => {
let answerTimes = dayjs().valueOf() - this.currentMillSeconds;
try {
const res = await requestDistributeCommentApi<ISubmitCommentResultData>({
url: ApiConstants.SUBMIT_COMMENT_API,
useAndroidOS: true,
param: {
"answerTimes": answerTimes,
"clazzNumber": "",
"commentId": this.commentId,
"commentType": 5,
"customParams": {
"pageTab": this.searchType === -1 ? "normal" : this.searchType === 1 ? "clazz_open" : 'clazz_system',
"searchWords": this.keywords
} as Record<string, string>,
"evaluateId": this.moduleId,
"evaluateName": this.moduleName,
"evaluateType": 5,
"labelList": this.selectLabelList,
"lessonNumber": "",
"markNumber": "",
"pageSource": 5,
"score": this.score,
"scoreEmojiDTOList": this.commentScoreEmojiVOList,
"textBoxAddCoinsFlag": false,
"textBoxContent": this.content,
"title": this.title,
}
})
if (res && Object.keys(res).length > 0) {
showToastCenter('提交成功')
getContext().eventHub.emit('submitSuccess');
this.dialogController?.close();
}
} catch (e) {
showToast(e.message);
} finally {
this.currentMillSeconds = dayjs().valueOf();
}
}
private scroller: Scroller = new Scroller();
private textAreaController: TextAreaController = new TextAreaController();
async aboutToAppear() {
this.fetchDistributeCommentData();
window.getLastWindow(getContext(this)).then(currentWindow => {
currentWindow.setWindowLayoutFullScreen(true);
let property = currentWindow.getWindowProperties();
let avoidArea = currentWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_KEYBOARD);
this.scrollHeight = px2vp(property.windowRect.height - avoidArea.bottomRect.height);
currentWindow.on('avoidAreaChange', data => {
if (data.type == window.AvoidAreaType.TYPE_KEYBOARD) {
this.keyHeight = px2vp(data.area.bottomRect.height);
this.scrollHeight =
px2vp(currentWindow.getWindowProperties().windowRect.height - data.area.bottomRect.height);
this.autoHeight = 250;
this.scroller.scrollTo({
xOffset: 0,
yOffset: 250
})
return;
}
this.autoHeight = 'auto';
})
})
}
fetchDistributeCommentData() {
requestDistributeCommentApi<IDistributeCommentData>({
url: ApiConstants.DISTRIBUTE_COMMENT_API,
param: {
"commentType": 5,
"autoAction": false,
"customParams": {
"pageTab": this.searchType === -1 ? 'NORMAL' : this.searchType === 1 ? 'CLAZZ_OPEN' : 'CLAZZ_SYSTEM',
"searchWords": ''
} as Record<string, string>,
}
}).then(res => {
this.title = res.title ?? '';
this.moduleName = res.moduleName ?? '';
this.commentId = res.commentId ?? '';
this.moduleId = res.moduleId ?? '';
if (Array.isArray(res.commentScoreEmojiVOList) && res.commentScoreEmojiVOList.length > 0) {
res.commentScoreEmojiVOList.map(item => {
return item;
})
this.commentScoreEmojiVOList = res.commentScoreEmojiVOList;
} else {
this.commentScoreEmojiVOList = [];
}
res.commentTextBoxVO && Object.keys(res.commentTextBoxVO).length > 0 && (this.commentTextBoxVO =
res.commentTextBoxVO);
}).catch((error: BusinessError) => {
showToast(error.message);
})
}
getEmojiIcon(isActive: boolean, score: number) {
switch (score) {
case 1:
return isActive ? $r('app.media.selected_score_first') : $r('app.media.normal_score_first');
case 2:
return isActive ? $r('app.media.selected_score_second') : $r('app.media.normal_score_second');
case 3:
return isActive ? $r('app.media.selected_score_third') : $r('app.media.normal_score_third');
case 4:
return isActive ? $r('app.media.selected_score_four') : $r('app.media.normal_score_four');
case 5:
return isActive ? $r('app.media.selected_score_five') : $r('app.media.normal_score_five');
default:
return '';
}
}
build() {
Column() {
Flex({
direction: FlexDirection.Row,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceBetween
}) {
Row()
Text(this.moduleName)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor($r('app.color.color_1B1E2E'))
Image($r('app.media.gtui_nav_icon_close'))
.width(24)
.height(24)
.onClick(() => {
this.dialogController?.close();
})
}.padding({
left: 16,
right: 16
})
.margin({
bottom: 30
})
Column() {
Scroll(this.scroller) {
Column() {
Text(this.title)
.font({
size: 16,
weight: FontWeight.Bold,
})
.fontColor($r('app.color.color_1B1E2E'))
.alignSelf(ItemAlign.Start)
.margin({
left: 16
})
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceBetween,
}) {
ForEach(this.commentScoreEmojiVOList, (item: IDistributeCommentDataCommentScoreEmojiVOList) => {
Column() {
Image(this.getEmojiIcon(item.isActive ?? false, item.score))
.width(40)
.height(40)
Text(item.text)
.fontSize(12)
.margin({
top: 16
})
}.onClick(() => {
this.score = item.score;
this.showAdviseInput = true;
let commentScoreEmojiVOList =
this.commentScoreEmojiVOList.map((el: IDistributeCommentDataCommentScoreEmojiVOList) => {
el.isActive = false;
if (item.score === el.score) {
el.isActive = true;
}
return el;
})
this.commentScoreEmojiVOList = [...commentScoreEmojiVOList];
if (Array.isArray(item.labelList) && item.labelList.length > 0) {
this.labelList = item.labelList.map(label => {
return { label, isActive: false } as IDistributeCommentDataLabel
});
this.isDisabled = true;
} else {
this.labelList = [];
this.isDisabled = false;
}
})
})
}.padding({
left: 16,
right: 16
})
.margin({
top: 30
})
Flex({
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceBetween,
wrap: FlexWrap.Wrap,
}) {
ForEach(this.labelList, (item: IDistributeCommentDataLabel) => {
Button(item.label)
.fontSize(12)
.fontColor(item.isActive ? $r('app.color.brand_500') : $r('app.color.color_1B1E2E'))
.type(ButtonType.Normal)
.borderRadius(3)
.height(28)
.width('31.5%')
.backgroundColor(item.isActive ? $r('app.color.brand_50') : $r('app.color.gray1'))
.onClick(() => {
let labelList = this.labelList.map(el => {
if (el.label.trim().includes(item.label.trim())) {
el.isActive = !item.isActive;
}
return el;
})
this.labelList = [...labelList];
this.isDisabled = false;
this.evaluateName = item.label;
this.selectLabelList = (labelList.filter(item => item.isActive)).map(item => item.label);
})
})
}.visibility(this.labelList.length > 0 ? Visibility.Visible : Visibility.None)
.margin({
top: 24
})
.padding({
left: 16,
right: 16
})
Row() {
TextArea({
placeholder: this.commentTextBoxVO?.tip ?? '',
controller: this.textAreaController,
})
.onChange((text) => {
this.content = text;
})
.onSubmit(() => {
})
.borderRadius(6)
.height(150)
.padding(16)
.enterKeyType(EnterKeyType.NEW_LINE)
.lineHeight(18)
.placeholderColor($r('app.color.fontGy3'))
.placeholderFont({
size: 12
})
.caretColor($r('app.color.brand_500'))
.backgroundColor($r('app.color.gray1'))
.fontSize(12)
.fontColor($r('app.color.fontGy1'))
.layoutWeight(1)
}.margin({
top: 24,
}).padding({
left: 16,
right: 16
})
.visibility(this.showAdviseInput ? Visibility.Visible : Visibility.None)
}
}.scrollBar(BarState.Off)
}.height(this.autoHeight)
Button('确认提交')
.fontSize(16)
.fontWeight(FontWeight.Normal)
.backgroundColor(this.isDisabled ? $r('app.color.brand_200') : $r('app.color.brand_500'))
.width('72%')
.height(44)
.stateEffect(false)
.onClick(() => {
if (this.isDisabled) {
return;
}
this.submitEvaluate();
})
.margin({ top: 44 })
}
.backgroundColor(Color.White)
.borderRadius({
topLeft: 10,
topRight: 10
})
.padding({
top: 24,
bottom: getNaviIndicatorHeightVP()
})
.margin({
bottom: this.keyHeight,
})
}
}