DEV Community

Cover image for [Daily HarmonyOS Next Knowledge] HAP Installation Failure, ArkTS and C++ Array Conversion, Gradient Mask Effect, etc.
kouwei qing
kouwei qing

Posted on

[Daily HarmonyOS Next Knowledge] HAP Installation Failure, ArkTS and C++ Array Conversion, Gradient Mask Effect, etc.

[Daily HarmonyOS Next Knowledge] HAP Installation Failure, ArkTS and C++ Array Conversion, Gradient Mask Effect, etc.

1. When starting debugging or running an application/service, an error occurs during HAP installation with the message "error: install failed due to older sdk version in the device".

This is caused by a mismatch between the SDK version used for compilation and packaging and the device image version. Mismatch scenarios include:

Scenario 1: The device image version is lower than the SDK version used for compilation. Update the device image version. Command to query the device image version:

hdc shell param get const.ohos.apiversion
Enter fullscreen mode Exit fullscreen mode

If the API version provided by the image is 10 and the SDK version used for application compilation is also 10, but the error still occurs, it may be because the image version is too low to comply with the new SDK verification rules. Update the image version to the latest.

Scenario 2: For applications that need to run on OpenHarmony devices, confirm that runtimeOS has been changed to OpenHarmony.

2. When starting debugging or running an application/service, an error occurs during HAP installation with the message "error: install sign info inconsistent".

This is due to inconsistent signatures between the installed application on the device and the new application to be installed. If "Keep Application Data" (overwrite installation without uninstalling the application) is checked in Edit Configurations and the signature is renewed, this error will occur.

Solution: Uninstall the installed application on the device, or uncheck "Keep Application Data" and reinstall the new application.

3. How to integrate RichEditor and a Component as a whole?

There is a Component above RichEditor, and the goal is to integrate RichEditor and the Component to enable adaptive scrolling based on the cursor position. Currently, using a Scroller cannot obtain the specific coordinates of the cursor in RichEditor (cursor x and y positions relative to RichEditor), making scrolling impossible.

Solution: Wrap RichEditor and the Component with a Scroll component. Use the onAreaChange callback to call scroller.scrollBy to adjust the height of the scrolling component.

Reference code:

@Entry
@Component
struct Index {
  editorController = new RichEditorController()
  scroller: Scroller = new Scroller()

  build() {
    Column() {
      Scroll(this.scroller) {
        Column() {
          Image($r('app.media.startIcon'))
            .width('100%')
            .height(200)
            .margin({ bottom: 20 })
          RichEditor({ controller: this.editorController })
            .id('RichEditor')
            .width('100%')
            .backgroundColor(Color.Yellow)
            .onReady(() => {
              this.editorController.addImageSpan($r("app.media.startIcon"),
                {
                  imageStyle:
                  {
                    size: ["100px", "100px"]
                  }
                })
              this.editorController.addTextSpan('男生女生向前冲',
                {
                  style:
                  {
                    fontColor: Color.Blue,
                    fontSize: 30
                  }
                })
            })
            .onAreaChange((_, value) => {
              if (_.height !== value.height) {
                this.scroller.scrollBy(0, Number(value.height) - 200)
              }
            })
          Button('getSpans-文字').onClick((event: ClickEvent) => {
            let getSpans = this.editorController.getSpans({ start: 0 })
            // Type casting is required to obtain text or image information
            let span0 = getSpans[0] as RichEditorTextSpanResult
          })
          Button('getSpans-图片').onClick((event: ClickEvent) => {
            let getSpans = this.editorController.getSpans({ start: 0 })
            let span1 = getSpans[1] as RichEditorImageSpanResult
          })
          Button('RichEditor获焦').onClick(() => {
            focusControl.requestFocus('RichEditor')
          })
        }
      }
      .scrollable(ScrollDirection.Vertical) // Vertical scrolling direction
      .scrollBar(BarState.On) // Scrollbar always visible
      .scrollBarColor(Color.Gray) // Scrollbar color
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

4. How to implement array conversion between ArkTS and C/C++?

TS2NAPI

TS-side code:

Button("TS2NAPI")
  .fontSize(50)
  .fontWeight(FontWeight.Bold)
  .onClick(() => {
    let napiArray: Int32Array = new Int32Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    console.info("TS2NAPI: JS   " + napiArray)
    testNapi.TS2NAPI(napiArray)
  })
Enter fullscreen mode Exit fullscreen mode

NAPI-side code:

// Transmit TS array to NAPI layer
static napi_value TS2NAPI(napi_env env, napi_callback_info info) {
  // Get parameters from TS layer
  size_t argc = 1;
  napi_value args;
  napi_get_cb_info(env, info, &argc, &args, NULL, NULL);
  napi_value input_array = args;

  // Get typedarray information to generate input_buffer
  napi_typedarray_type type; // Data type
  napi_value input_buffer;
  size_t byte_offset; // Data offset
  size_t i, length;   // Data byte size
  napi_get_typedarray_info(env, input_array, &type, &length, NULL, &input_buffer, &byte_offset);

  // Get array data
  void *data;
  size_t byte_length;
  napi_get_arraybuffer_info(env, input_buffer, &data, &byte_length);

  if (type == napi_int32_array) {
    int32_t *data_bytes = (int32_t *)(data);
    int32_t num = length / sizeof(int32_t);

    string str = "";
    for (int32_t i = 0; i < num; i++) {
      int32_t tmp = *((int32_t *)(data_bytes) + i);
      str += (to_string(tmp) + ' ');
    }
    OH_LOG_INFO(LOG_APP, "TS2NAPI: C++  %{public}s", str.c_str());
  }

  return NULL;
}
Enter fullscreen mode Exit fullscreen mode

NAPI2TS

TS-side code:

Button("NAPI2TS")
  .fontSize(50)
  .fontWeight(FontWeight.Bold)
  .onClick(() => {
    let napiArray: Int32Array = testNapi.NAPI2TS()
    console.info("NAPI2TS: JS   " + napiArray)
  })
Enter fullscreen mode Exit fullscreen mode

NAPI-side code:

// Transmit NAPI layer array to TS layer
static napi_value NAPI2TS(napi_env env, napi_callback_info info) {
  // Number of data items
  int num = 10;

  // Create output_buffer
  napi_value output_buffer;
  void *output_ptr = NULL;
  napi_create_arraybuffer(env, num * sizeof(int32_t), &output_ptr, &output_buffer);

  // Create output_array
  napi_value output_array;
  napi_create_typedarray(env, napi_int32_array, num, output_buffer, 0, &output_array);

  // Assign values to output_ptr and output_buffer
  int32_t *output_bytes = (int32_t *)output_ptr;
  for (int32_t i = 0; i < num; i++) {
    output_bytes[i] = i;
  }

  string str = "";
  for (int32_t i = 0; i < num; i++) {
    int32_t tmp = *((int32_t *)(output_ptr) + i);
    str += (to_string(tmp) + ' ');
  }
  OH_LOG_INFO(LOG_APP, "NAPI2TS: C++  %{public}s", str.c_str());

  return output_array;
}
Enter fullscreen mode Exit fullscreen mode

Practical scenario:

ArkTS reads a rawfile image, transmits it to the NAPI layer for processing, and then returns it to ArkTS for display:

Button("Test")
  .fontSize(50)
  .fontWeight(FontWeight.Bold)
  .onClick(() => {

    getContext().resourceManager.getRawFileContent('test.png').then(value => {
      hilog.info(0x0000, 'getRawFileContent', '%{public}s', 'OK');
      const fileData: Uint8Array = value;
      // Get the ArrayBuffer of the image
      let buffer = fileData.buffer;

      let input = new Uint8Array(buffer)
      let output = testNapi.TEST(input);

      let decodingOptions: image.DecodingOptions = {
        editable: true,
        desiredPixelFormat: 3,
      }
      let imageSource = image.createImageSource(output.buffer);
      imageSource.createPixelMap(decodingOptions).then(map => {
        this.imageSrc = map;
      })
    }).catch((err: BusinessError) => {
      hilog.error(0x0000, 'getRawFileContent', '%{public}s', err.message);
    })
  })

Image(this.imageSrc)
  .height(100)
  .width(100)
Enter fullscreen mode Exit fullscreen mode

5. How to implement a top gradient mask effect?

How to implement a gradient mask effect for the bullet comment list at the top of a live broadcast room?

Solution: Use overlay with the linearGradient property and blendMode to mix the current overlay with the underlying list for a gradient mask effect.

Effect diagram:

![[【每日学点鸿蒙知识】24.08.18.png]]

Reference code:

@Entry
@Component
struct Index {
  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

  @Builder
  overlayBuilder() {
    Stack()
      .height("100%")
      .width("100%")
      .linearGradient({
        direction: GradientDirection.Bottom, // Gradient direction
        colors: [["#00FFFFFF", 0.0], ["#FFFFFFFF", 0.3]] // When the proportion of the last array element is less than 1, it satisfies the repeated coloring effect
      })
      .blendMode(BlendMode.DST_IN, BlendApplyType.OFFSCREEN)
      .hitTestBehavior(HitTestMode.None)
  }

  build() {
    Column() {
      List({ space: 20, initialIndex: 0 }) {
        ForEach(this.arr, (item: number) => {
          ListItem() {
            Text('' + item)
              .width('100%')
              .height(100)
              .fontSize(16)
              .textAlign(TextAlign.Center)
              .borderRadius(10)
              .backgroundColor(0xFFFFFF)
          }
          .onClick(() => {
            console.log('is click')
          })
        }, (item: string) => item)
      }.width('90%')
      .scrollBar(BarState.Off)
      .overlay(this.overlayBuilder())
      .blendMode(BlendMode.SRC_OVER, BlendApplyType.OFFSCREEN)
    }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)