如何实现键盘上方自定义工具栏
==============

#### 问题现象

跟随键盘的工具栏如何实现？

#### 背景知识

*   [自定义弹窗（CustomDialog）](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-methods-custom-dialog-box)：通过CustomDialogController类显示自定义弹窗。使用弹窗组件时，优先考虑自定义弹窗，便于弹窗样式与内容的自定义。
*   [defaultFocus](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-focus#defaultfocus9)：设置当前组件是否为当前页面上的默认焦点。
*   [on('keyboardHeightChange')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-window#onkeyboardheightchange7)：开启固定态软键盘高度变化的监听，当软键盘由本窗口唤出并存在重叠区域时通知键盘高度变化。
*   [Stack](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-stack)：堆叠容器，子组件按照顺序依次入栈，后一个子组件覆盖前一个子组件。
*   [alignContent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-stack#aligncontent)：设置子组件在容器内的对齐方式。该属性与通用属性[align](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-location#align)同时设置时，后设置的属性生效。

#### 解决方案

针对跟随键盘的工具栏，可采用以下两种实现方案：

*   方案一：使用CustomDialog组件自定义工具栏。以下示例通过点击文本组件使弹窗自动获焦，设置弹窗从底部弹出，并为弹窗添加弹出与关闭时的过渡动画，以实现跟随键盘的动画效果。
    
    _//_ _自定义对话框组件，用于显示输入框_
    @CustomDialog
    @Component
    export struct Dialog {
      dialogController: CustomDialogController;
    
      build() {
        Column() {
          TextInput({ text: '', placeholder: 'input your word...' })
            .type(InputType.Normal)
            .defaultFocus(true) _//_ _设置为默认聚焦状态_
            .onBlur(() \=\> {
              _//_ _输入框失去焦点时关闭对话框_
              this.dialogController.close();
            })
            .height('50%')
            .margin({ top: 25, left: 16, right: 16 });
        }
        .height(100)
        .backgroundColor(Color.White);
      }
    }
    
    @Entry
    @Component
    struct Index {
      message: string \= 'Hello World';
     _ // 自定义对话框控制器，配置对话框的显示参数_
      customDialogController: CustomDialogController \= new CustomDialogController({
        builder: Dialog(), _// 设置对话框内容组件_
        alignment: DialogAlignment.Bottom, _// 对话框从底部弹出_
        cornerRadius: 0,
        customStyle: true,
        openAnimation: { duration: 0.001, tempo: 0 }, _// 打开弹窗时动画设置_
        closeAnimation: { duration: 0.001, tempo: 0 }, _// 关闭弹窗时动画设置_
      });
    
      build() {
        Column() {
        _  // 点击按钮打开对话框_
          Text(this.message)
            .onClick(() \=\> {
              this.customDialogController.open(); _// 触发对话框显示_
            })
            .id('HelloWorld')
            .fontSize(50)
            .fontWeight(FontWeight.Bold);
        }
        .height('100%')
        .width('100%');
      }
    }
    
    效果预览：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/52/v3/XbU6qhu_RjqgGxwucsbQmw/zh-cn_image_0000002659060593.gif?HW-CC-KV=V1&HW-CC-Date=20260709T134119Z&HW-CC-Expire=86400&HW-CC-Sign=CBF2061DEF5C517C5A07B5AF735F39DC271833A090ABF75F8A98F3D56B277853 "点击放大")
    
*   方案二：自定义工具栏组件，借助堆叠容器Stack进行页面布局，将组件设置为底部横向居中。定义状态变量isVisible以控制自定义工具栏的显示与隐藏：在TextInput组件聚焦时显示，失焦时隐藏。同时，通过on('keyboardHeightChange')方法监听软键盘高度变化，依据键盘高度动态调整自定义工具栏的位置，使其紧贴于键盘上方。
    
    import { window } from '@kit.ArkUI';
    
    @Entry
    @Component
    struct Index {
      message: string \= '图文信息区域';
     _ // 控制自定义工具栏组件可见性_
      @State isVisible: Visibility \= Visibility.Hidden;
      _// 存储键盘高度信息_
      @State keyboardHeight: string \= '0px';
      controller: RichEditorController \= new RichEditorController();
    
      _// 页面加载时监听键盘高度变化_
      aboutToAppear(): void {
        window.getLastWindow(this.getUIContext().getHostContext()).then((win) \=\> {
          win.on('keyboardHeightChange', (height) \=\> {
            console.info('height' \+ height.toString());
            if (height) {
              this.keyboardHeight \= height.toString() \+ 'px'; _// 保存键盘高度_
              this.isVisible \= Visibility.Visible; _// 显示自定义组件_
            } else {
              this.isVisible \= Visibility.Hidden; _// 隐藏自定义组件_
            }
          });
        });
      }
    
      build() {
       _ // 使用Stack布局，底部对齐_
        Stack({ alignContent: Alignment.Bottom }) {
          Column() {
            Text(this.message)
              .fontSize(52)
              .height('90%')
              .width('100%')
              .textAlign(TextAlign.Center)
              .fontColor(Color.Black);
          _  // 操作按钮行_
            Row() {
              Text('点击拉起键盘')
                .fontColor(Color.White)
                .width('90%')
                .height('40')
                .backgroundColor('#ff1a73f8')
                .borderRadius(20)
                .textAlign(TextAlign.Center)
                .margin({ left: 16, right: 16 })
                .onClick(() \=\> {
                  this.isVisible \= Visibility.Visible; _// 显示自定义组件_
                  setTimeout(() \=\> {
                    focusControl.requestFocus('textArea'); _// 焦点切换到输入框_
                  }, 10);
                });
            }.height('10%').width('100%');
          }
          .expandSafeArea(\[SafeAreaType.KEYBOARD\], \[SafeAreaEdge.BOTTOM\]); _//_ _适配键盘区域_
    
       _   // 遮罩层（点击隐藏自定义）_
          Column()
            .width('100%')
            .height('120%')
            .opacity(0.5)
            .backgroundColor('rgba(0,0,0,0.5)')
            .onClick(() \=\> {
              this.isVisible \= Visibility.Hidden; _// 隐藏自定义组件_
            })
            .visibility(this.isVisible)
            .expandSafeArea(\[SafeAreaType.KEYBOARD\], \[SafeAreaEdge.BOTTOM, SafeAreaEdge.TOP\]);
         _ // 动态加载自定义组件_
          CommentComponent({ isVisible: this.isVisible, keyboardHeight: this.keyboardHeight });
        };
      }
    
     _ // 页面卸载时隐藏自定义组件_
      aboutToDisappear(): void {
        this.isVisible \= Visibility.Hidden;
      }
    }
    
    _//_ _自定义工具栏组件_
    @Component
    struct CommentComponent {
      controller: RichEditorController \= new RichEditorController();
      @Link isVisible: Visibility;
      @Link keyboardHeight: string;
    
      build() {
        Flex({ direction: FlexDirection.ColumnReverse, justifyContent: FlexAlign.Start, alignItems: ItemAlign.Stretch }) {
          Row() {
            RichEditor({ controller: this.controller })
              .id('textArea') _//_ _设置输入框ID用于焦点控制_
              .placeholder('喜欢就评论一下吧~', { fontColor: Color.Gray, font: { size: 16, weight: FontWeight.Normal } })
              .width('65%');
            Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.End, alignItems: ItemAlign.Stretch }) {
              Image($r('app.media.startIcon')).height(20).constraintSize({ minWidth: 20, maxWidth: 20 });
            }
            .width(110);
          }
          .margin({
            top: 10,
            bottom: this.keyboardHeight, _// 根据键盘高度设置动态调整底部边距_
            left: 10,
            right: 10
          })
          .border({ width: 1 })
          .borderRadius(50);
        }
        .width('100%')
        .height(380)
        .expandSafeArea(\[SafeAreaType.KEYBOARD\], \[SafeAreaEdge.BOTTOM, SafeAreaEdge.TOP\]) _// 适配键盘区域_
        .backgroundColor(Color.White)
        .visibility(this.isVisible);
      }
    }
    
    效果预览：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/9a/v3/-PGgNgmhTFO70DV2vUsT8Q/zh-cn_image_0000002628821236.gif?HW-CC-KV=V1&HW-CC-Date=20260709T134119Z&HW-CC-Expire=86400&HW-CC-Sign=700EF9A06DD1BD8ABC281E23CACC57C1D98024FAAE407513DE5CFFD626EEE59B "点击放大")
    

#### 常见FAQ

Q：如何实现自定义键盘功能？

A：请参考[实现自定义键盘功能](https://gitee.com/harmonyos_samples/CustomizeKeyboard)文档。

Q：如何实现系统键盘和自定义键盘的切换？

A：请参考[系统键盘和自定义键盘如何切换](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-281)官方文档说明。