推荐车牌号场景
=======

从5.1.0(18)开始，支持智能填充的推荐车牌号场景。

在填写车牌号表单场景时，智能填充可从用户的历史表单输入中提供输入建议，开发者可以参考如下代码进行开发。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/2d/v3/F7QBX4-NTVWazk1e0Oq8zQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073645Z&HW-CC-Expire=86400&HW-CC-Sign=D17A455FA56374B1650A83D74F27FA56684D8D76FB9B6AABCC34E472D2B35233)

只推荐机主本人历史表单信息的车牌号信息（与登录设备的账号信息，实名姓名+手机号或邮箱信息相同）。

#### 效果图

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/47/v3/ZEZkz5AYTZin64lL64C5BA/zh-cn_image_0000002625108973.png?HW-CC-KV=V1&HW-CC-Date=20260612T073645Z&HW-CC-Expire=86400&HW-CC-Sign=7916D50D58A78E67D71143B336BCB6D0392E448F0486D10476D510424FE8574D)

#### 示例代码

import { display } from '@kit.ArkUI';

const NEW\_ENERGY\_TEXT = '新能源';

@Extend(Text)
function extendStyles(value: string, width: number, height: number, active: boolean) {
  .fontSize(value === NEW\_ENERGY\_TEXT ? 10 : 18)
  .fontWeight(FontWeight.Medium)
  .textAlign(TextAlign.Center)
  .width(width)
  .height(height)
  .borderWidth('3px')
  .borderColor(active ? Color.Blue : '#ccc')
  .borderRadius(5)
}

@Entry
@Component
struct LicensePlate {
  // 车牌号输入框的数量。
  private length = 8;
  private licenseItemId = '\_license\_item';
  @State licensePlateVal: string\[\] = \[\];
  @State activeIndex: number = -1;
  @State itemWidth: number = 30;
  @State itemHeight: number = 30;
  @State inputText: string = '';
  // 用户是否已经触发输入。
  @State isUserInput: boolean = false;
  // 匹配历史输入的上一个输入框的值
  private beforeValue: string = '';

  aboutToAppear(): void {
    this.licensePlateVal = new Array(this.length).fill('');
    let displayClass = display.getDefaultDisplaySync();
    let width = displayClass.width;
    // 每个框的宽度根据屏幕宽度计算。
    this.itemWidth = this.getUIContext().px2vp(width) / (this.length + 1) - 4;
    this.itemHeight = this.itemWidth \* 1.2;
  }

  setValue(val: string): void {
    if (!this.isUserInput) {
      // 根据智能填充填写车牌号输入框。
      this.handleAutoFill(val);
      this.beforeValue = val;
      return;
    }
    if (!val || val.length === 0 || this.beforeValue.length > val.length) {
      let licensePlate = this.getLicensePlate();
      if (licensePlate.length > 0) {
        this.inputText = licensePlate;
      }
      this.beforeValue = this.inputText;
      return;
    }
    let inputData = val.substring(this.beforeValue.length);
    if (inputData.length > this.length) {
      inputData = inputData.substring(0, this.length);
    }
    // 用户输入仅替换选定的输入框，而智能填充则替换所有输入框中的所有值。
    this.handleUserInput(inputData);
    this.beforeValue = val;
  }

  getLicensePlate(): string {
    return this.licensePlateVal.join('');
  }

  handleUserInput(val: string): void {
    if (val.length > this.length - this.activeIndex) {
      val = val.substring(0, this.length - this.activeIndex);
    }
    for (let i = 0; i < val.length; i++) {
      this.licensePlateVal\[this.activeIndex\] = val\[i\];
      this.activeIndex = Math.min(this.activeIndex + 1, this.length - 1);
    }
  }

  handleAutoFill(val: string): void {
    let value = val.split('');
    this.licensePlateVal.fill('');
    for (let i = 0; i < this.length; i++) {
      this.licensePlateVal\[i\] = i < value.length ? value\[i\] : this.licensePlateVal\[i\];
    }
    this.activeIndex = Math.min(value.length + 1, this.length - 1);
  }

  handleDelete() {
    if (!this.licensePlateVal\[this.activeIndex\]) {
      this.licensePlateVal\[this.activeIndex - 1\] = '';
    } else {
      this.licensePlateVal\[this.activeIndex\] = '';
    }
    this.activeIndex = Math.max(0, this.activeIndex - 1);
  }

  getValue(index: number): string {
    return (index === this.length - 1 && !this.licensePlateVal\[index\]) ? NEW\_ENERGY\_TEXT : this.licensePlateVal\[index\];
  }

  handleLicenseClick(screenX: number) {
    for(let index = 0; index <= 7; index++) {
      let id = index + this.licenseItemId;
      let position = this.getUIContext().getComponentUtils().getRectangleById(id);
      // 相对于屏幕的位置信息,单位px
      let left = position?.screenOffset?.x ?? 0;
      let right = left + (position?.size?.width ?? 0);
      if (screenX >= left && screenX <= right) {
        this.activeIndex = index;
      }
    }
  }

  @Builder
  displayItem(index: number) {
    Column() {
      Text(this.getValue(index))
        .extendStyles(this.getValue(index), this.itemWidth, this.itemHeight, this.activeIndex === index)
    }
    .id(index + this.licenseItemId)
    .padding({
      left: 2,
      right: 2
    })
  }

  @Builder
  buildLicensePlateNumber() {
    Flex({
      direction: FlexDirection.Row,
      alignItems: ItemAlign.Center,
      justifyContent: FlexAlign.SpaceBetween
    }) {
      Column() {
        Row() {
          Text("车牌号码")
        }.height(30)

        Stack({ alignContent: Alignment.BottomStart }) {
          Row() {
            this.displayItem(0)
            this.displayItem(1)
            Text('·')
              .fontSize(22)
              .fontWeight(600)
              .height(this.itemHeight)
            this.displayItem(2)
            this.displayItem(3)
            this.displayItem(4)
            this.displayItem(5)
            this.displayItem(6)
            this.displayItem(7)
          }

          TextInput({ text: $$this.inputText })
            .width('100%')
            .height('100%')
            .opacity(0)
            .contentType(ContentType.LICENSE\_PLATE)
            .onClick((event) => {
              // 相对于屏幕的X轴坐标,单位px
              let displayX = this.getUIContext().vp2px(event.displayX);
              this.handleLicenseClick(displayX);
              if (this.activeIndex < 0) {
                this.activeIndex = 0;
              }
            })
            .onChange((val: string) => {
              if (val === this.beforeValue) {
                return;
              }
              this.setValue(val);
              this.isUserInput = false;
            })
            .onDidInsert(() => {
              // 当使用输入法输入数据时触发。如果输入法是自定义的，则在用户输入数据时将isUserInput设置为true。
              this.isUserInput = true;
            })
            .onDidDelete((val: DeleteValue) => {
              // 当使用输入方法删除数据时触发。如果输入方法是自定义的，当用户删除数据时，将\`isUserInput\`设置为\`true\`，并调用相应的处理函数。
              if (val?.deleteValue?.length > 0) {
                this.isUserInput = true;
              }
              this.handleDelete();
            })
        }
        .height(this.itemHeight)
        .margin({ top: 20 })
      }
    }
    .backgroundColor(Color.White)
    .height(50)
    .margin({ left: 15, right: 15 })
    .id("customInput")
    .defaultFocus(false)
  }

  build() {
// ...
      Column() {
        this.buildLicensePlateNumber()
      }
// ...
  }
}