网上的解决方案都是在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,
} )
}
}