【每日学点鸿蒙知识】Video播放失败、toggle拖拽、图片裁剪旋转等

1、如何更改TextInput密码输入模式下passwordIcon的大小、颜色、位置?

使用stack容器作为父容器,子容器使用image来实现自定义一个passwordIcon,此时即可对Image组件的位置、大小、颜色做出更改。

@Entry
@Component
struct TextInputExample {
  @State text: string = ''
  @State changeType: InputType = InputType.Password
  @State isVisible: boolean = false
  @State changeState: boolean = false
  controller: TextInputController = new TextInputController()

  build() {
    Column() {
      Flex({ direction: FlexDirection.Row }) {
        Stack() {
          TextInput({ text: this.text, controller: this.controller })
            .type(this.changeType)
            .placeholderFont({ size: 16, weight: 400 })
            .showPasswordIcon(false)
            .width(336)
            .height(56) // 设置内间距让输入内容不超过图标位置
            .padding({
              right: 50
            })
            .onChange((value: string) => {
              this.text = value
            })
          // Image覆盖passwordIcon实现
          Image($r(this.isVisible ? 'app.media.visible' : 'app.media.Invisible'))
            .margin({
              left: 280
              // left: 200
            })
            .backgroundColor('#E7E8EA')
            .width(20)
            .height(20)
            .onClick(() => {
              this.changeState = !this.changeState
              this.isVisible = !this.isVisible
              if (this.changeState) {
                this.changeType = InputType.Normal
              } else {
                this.changeType = InputType.Password
              }
            })
        }
      }
    }.width('100%').height('100%').backgroundColor('#F1F3F5')
  }
}

2、toggle组件设置拖动的同时如何屏蔽其本身的点击手势?

需要手动控制Toggle组件传入的默认值,例如在最上面自定义一个toggleIsOn的boolean变量然后,在onChange回调里面自己手动控制改变其的值。

import { hilog } from '@kit.PerformanceAnalysisKit';

@Entry
@Component
export struct TestDragToggle {
  @State offsetX: number = 0;
  @State offsetY: number = 0;
  @State positionX: number = 0;
  @State positionY: number = 0;
  @State toggleIsOn: boolean = true;
  private isDragging: boolean = false;

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
      Toggle({ type: ToggleType.Button, isOn: this.toggleIsOn }) {
        Text('Toggle')
      }
      .selectedColor(Color.Pink)
      // onchange回调先于onActionEnd
      .onChange((isOn: boolean) => {
        hilog.info(0x0000, 'testTag', 'xxx %{public}s', `onClick Toggle, isOn: ${isOn}`);
        console.info('isDragging======' + this.isDragging)
        if (isOn == this.toggleIsOn) {
          return
        } else {
          this.toggleIsOn = isOn
        }
        if (this.isDragging) {
          this.toggleIsOn = !this.toggleIsOn
        }
      })
      .translate({ x: this.offsetX, y: this.offsetY })
      .gesture(
        PanGesture()
          .onActionStart((event: GestureEvent) => {
            this.isDragging = true;
          })
          .onActionUpdate((event: GestureEvent) => {
            this.offsetX = this.positionX + event.offsetX;
            this.offsetY = this.positionY + event.offsetY;
          })
          .onActionEnd((event: GestureEvent) => {
            this.positionX = this.offsetX;
            this.positionY = this.offsetY;
            this.isDragging = false;
          })
      )
    }
  }
}

3、在使用video组件时,为video添加本地视频播放源后,立刻播放,为什么会播放失败?

从给video加载资源,到video播放,中间必须要加载,这个加载需要耗时。点击按钮添加资源,等资源准备完毕后,才会自动播放,因此需要把播放逻辑写在video的onPrepared回调里。

import { common } from '@kit.AbilityKit';

@Entry
@Component
struct LocalSource {
  @State videoUrl: ResourceStr = '';
  @State currentTime: number = 0;
  private context = getContext(this) as common.UIAbilityContext;
  @State prepared : boolean = false;

  private  videoVC = new VideoController();

  build() {
    Column({space: 20}) {
      Video({
        src: this.videoUrl,
        // src: 'file:///data/storage/el2/base/files/xxxx.mp4',
        controller: this.videoVC
      })
        .onPrepared(()=>{
          this.prepared = true;
          this.videoVC.start()
          console.info('onPrepared')
        })
        .onError(() => {
          console.info('onError')
        })
        .objectFit(ImageFit.Contain)
        .onUpdate((e)=>{
          this.currentTime = e.time;
        })
        .controls(false)
        .width('70%')
        .height(300)

      Text(`当前播放时间: ${this.currentTime}`)
      
      Button('立刻播放')
        .onClick(()=>{
          if(this.videoUrl === '') {
            // 设置url为应用沙箱中的本地视频路径
            this.videoUrl = 'file:///data/storage/el2/base/files/xxxx.mp4';
            // 问题: 首次启动,点击按钮设置播放资源路径后, 立即调用start方法, 无法正常播放视频ex
            if (this.prepared === true) {
              this.videoVC.start();
            }
          }
        })

      Button('延迟100ms播放')
        .onClick(()=>{
          if(this.videoUrl === '') {
            this.videoUrl = 'file:///data/storage/el2/base/files/xxxx.mp4';
            // 但是设置完资源路径后, 稍微延迟下这里延迟100ms, 再调用start方法就可以正常播放
            setTimeout(()=>{
              this.videoVC.start();
            }, 100)
          }
        })


      Button('重置播放')
        .fontSize(10)
        .fontColor(Color.White)
        .onClick(()=>{
          this.videoVC.stop();
          this.videoUrl = '';
        })
    }
    .width('100%')
    .height('100%')
  }
}

4、如何实现图片裁剪、旋转?

使用Canvas与媒体的图片处理结合来实现,Canvas层叠三层绘制,第一层绘制图片后通过OnAreaChange事件获取图片所在容器的坐标,从而确定二层、三层的Canvas画布所在位置。第二层绘制遮罩层实现裁剪区域与非裁剪区域区分显示。第三层绘制裁剪框,结合OnTouch事件实现可拖拽裁剪框,从而确定裁剪区域。

// 绘制背景图
async drawImage() {
  await this.initData('test.jpg')
  if (this.imageInfo != undefined) {
    this.canvasContext.drawImage(this.pixelMap, 0, 0, px2vp(this.imageInfo.size.width),
      px2vp(this.imageInfo.size.height));
    this.canvasContext.save();
  }
}

// 绘制蒙层
drawMask() {
  this.canvasContext3.clearRect(0, 0, this.imageInfo?.size.width, this.imageInfo?.size.height);
  this.canvasContext3.fillStyle = 'rgba(0,0,0,0.7)';
  this.canvasContext3.fillRect(0, 0, px2vp(this.imageInfo?.size.width), px2vp(this.imageInfo?.size.height));
  this.canvasContext3.clearRect(this.clipRect.x - this.initPosition.x, this.clipRect.y - this.initPosition.y,
    this.clipRect.width, this.clipRect.height);
}

// 绘制裁剪框
drawClipImage() {
  this.canvasContext2.clearRect(0, 0, this.clipRect.width, this.clipRect.height);
  this.canvasContext2.lineWidth = 6
  this.canvasContext2.strokeStyle = '#ffffff'
  this.canvasContext2.beginPath()
  this.canvasContext2.moveTo(0, 20)
  this.canvasContext2.lineTo(0, 0);
  this.canvasContext2.lineTo(20, 0);
  this.canvasContext2.moveTo(this.clipRect.width - 20, 0);
  this.canvasContext2.lineTo(this.clipRect.width, 0);
  this.canvasContext2.lineTo(this.clipRect.width, 20);
  this.canvasContext2.moveTo(0, this.clipRect.height - 20);
  this.canvasContext2.lineTo(0, this.clipRect.height);
  this.canvasContext2.lineTo(20, this.clipRect.height);
  this.canvasContext2.moveTo(this.clipRect.width - 20, this.clipRect.height);
  this.canvasContext2.lineTo(this.clipRect.width, this.clipRect.height);
  this.canvasContext2.lineTo(this.clipRect.width, this.clipRect.height - 20);
  this.canvasContext2.stroke()
  this.canvasContext2.beginPath();
  this.canvasContext2.lineWidth = 0.5;
  let height = Math.round(this.clipRect.height / 3);
  for (let index = 0; index <= 3; index++) {
    let y = index === 3 ? this.clipRect.height : height * index;
    this.canvasContext2.moveTo(0, y);
    this.canvasContext2.lineTo(this.clipRect.width, y);
  }
  let width = Math.round(this.clipRect.width / 3);
  for (let index = 0; index <= 3; index++) {
    let x = index === 3 ? this.clipRect.width : width * index;
    this.canvasContext2.moveTo(x, 0);
    this.canvasContext2.lineTo(x, this.clipRect.height);
  }
  this.canvasContext2.stroke();
}

// 裁剪图片
async clipImage() {
  let x = this.clipRect.x - this.initPosition.x;
  let y = this.clipRect.y - this.initPosition.y;
  console.info('x= ' + x + '  y = ' + y + 'height = ' + this.clipRect.height + 'width = ' + this.clipRect.width)
  await this.pixelMap?.crop({
    x: vp2px(x),
    y: vp2px(y),
    size: { height: vp2px(this.clipRect.height), width: vp2px(this.clipRect.width) }
  })
  this.cropImageInfo = await this.pixelMap?.getImageInfo();
  this.isCrop = true
  this.rotateOn = true
}

// 旋转图片
async rotateImage() {
  if (this.rotateOn) {
    await this.pixelMap?.rotate(90)
    const info = await this.pixelMap?.getImageInfo()
    this.cropImageInfo = info
    if (this.pixelMapChange) {
      this.pixelMapChange = false
    } else {
      this.pixelMapChange = true
    }
  }
}

5、如何访问沙盒路径?

Image组件不能直接传入应用沙箱路径,需要传入应用沙箱uri;

拿到文件的沙箱路径后,通过调用调用@ohos.file.fileuri模块的fileuri.getUriFromPath(file.path)将沙箱路径转化为沙箱uri,传入之后即可正常显示。

import { common } from '@kit.AbilityKit';
import { BusinessError, request } from '@kit.BasicServicesKit';
import { fileUri } from '@kit.CoreFileKit';

// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let filesDir = context.filesDir;

@Entry
@Component
export struct Index11 {
  @State message: string = 'Hello World';
  @State urlImage: ResourceStr = ''
  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button('展示图片')
          .onClick(() => {
            try {
              // 文件路径转成沙箱uri
              let filePath = filesDir + '/pic.jpg'
              this.urlImage = fileUri.getUriFromPath(filePath);
            } catch (error) {
              let err: BusinessError = error as BusinessError;
              console.error(`Invoke downloadTask downloadFile failed, code is ${err.code}, message is ${err.message}`);
            }
          })
          .width('100%')
        Image(this.urlImage)
      }
      .height('100%')
    }
  }}
相关推荐
HarmonyOS_SDK30 分钟前
多样化消息通知样式,帮助应用提升日活跃度
harmonyos
塞尔维亚大汉3 小时前
【OpenHarmony】 鸿蒙矢量图形(SVG)之XmlGraphicsBatik
harmonyos·arkui
goodbruce4 小时前
HarmonyOS鸿蒙开发 MVVM模式及状态管理
harmonyos
轻口味7 小时前
【每日学点鸿蒙知识】Hap 安装失败、ArkTS 与C++ 数组转换、渐变遮罩效果等
c++·华为·harmonyos
SuperHeroWu77 小时前
【HarmonyOS】鸿蒙应用实现屏幕录制详解和源码
harmonyos·鸿蒙·视频·录屏·屏幕录制·沙箱·麦克风
轻口味7 小时前
【每日学点鸿蒙知识】一键登录、包资源分析工具、har包版本冲突、系统相册等
华为·harmonyos
法迪9 小时前
华为 Sensor 省电策略调研
华为·功耗
枫叶丹49 小时前
【HarmonyOS之旅】基于ArkTS开发(一) -> Ability开发一
ui·华为·harmonyos
星释10 小时前
鸿蒙Flutter实战:15-Flutter引擎Impeller鸿蒙化、性能优化与未来
flutter·harmonyos·鸿蒙
编程百晓君16 小时前
Harmony OS开发-ArkTS语言速成五
javascript·harmonyos·arkts