如何实现Button可拖动并在应用退出后保存其位置
=========================

#### 问题现象

页面有多个按钮，其中一个按钮用于控制按钮能否拖动，如何实现按钮可通过滑动手势改变位置，并在退出应用时保存按钮的位置。

#### 效果预览

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/84/v3/GMcxye4fQW6J3aoOozRiFg/zh-cn_image_0000002658848487.png?HW-CC-KV=V1&HW-CC-Date=20260709T133955Z&HW-CC-Expire=86400&HW-CC-Sign=23FD2E08D66562343448B8697557DF7FF4A6510CF693762608323493921C823A "点击放大")

#### 背景知识

*   通用属性[translate](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-transformation#translate)用于设置组件的平移，可使组件在以组件左上角为坐标原点的[组件坐标系](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkui-glossary#组件坐标系)中进行移动，如x大于0表示组件右移。
*   [gesture](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-gesture-settings#gesture)可以为组件绑定手势，并根据手势动作触发回调。
*   [PersistentStorage](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-persiststorage)可以持久化存储选定的[AppStorage](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-appstorage)属性，确保应用重新启动时能获取到持久化存储的属性值。

#### 解决方案

可以通过为组件绑定滑动手势[PanGesture](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-gestures-pangesture)，识别滑动动作，通过translate实现改变位置，并通过PersistentStorage持久化保存按钮组件的translate位移的值，使组件位置在应用重新启动时与关闭时一致。

*   EntryAbility.ets文件，onWindowStageCreate中初始化PersistentStorage。
    
    onWindowStageCreate(windowStage: window.WindowStage): void {
      windowStage.loadContent('pages/Index', (err) => {
        if (err.code) {
          hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
          return;
        }
      _  // 初始化PersistentStorage_
        PersistentStorage.persistProp('customerFlag', false);
        PersistentStorage.persistProp('offsetX', 0);
        PersistentStorage.persistProp('offsetY', 0);
        PersistentStorage.persistProp('offset2X', 0);
        PersistentStorage.persistProp('offset2Y', 0);
      });
    }
    
*   Index.ets文件，获取持久化的按钮位置信息，实现滑动手势改变位置功能，并将新的按钮位置信息持久化。
    
    @Entry
    @Component
    struct Index {
      @State flag: boolean = false;
      positionX: number = 0;
      positionY: number = 0;
      @State offsetX: number = 0;
      @State offsetY: number = 0;
      @State offset2X: number = 0;
      @State offset2Y: number = 0;
    
      aboutToAppear(): void {
      _  // AppStorage获取对应值_
        this.flag = AppStorage.get<boolean>('customerFlag') ?? false;
        this.offsetX = AppStorage.get<number>('offsetX') ?? 0;
        this.offsetY = AppStorage.get<number>('offsetY') ?? 0;
        this.offset2X = AppStorage.get<number>('offset2X') ?? 0;
        this.offset2Y = AppStorage.get<number>('offset2Y') ?? 0;
      }
    
      build() {
        Column({ space: 10 }) {
          Row() {
            Button(this.flag ? '完成' : '自定义位置')
              .onClick(() => {
                this.flag = !this.flag;
             _   // AppStorage设置对应属性值_
                AppStorage.setOrCreate('customerFlag', this.flag);
              });
          }
          .width('100%')
          .height(80)
          .justifyContent(FlexAlign.End)
          .margin({ right: 10 });
    
          Button('组件1')
            .zIndex(3)
          _  // 组件平移位置_
            .translate({ x: this.offsetX, y: this.offsetY, z: 0 })
            .gesture(
        _      // 绑定拖动手势_
              PanGesture()
                .onActionStart(() => {
                  this.positionX = this.offsetX;
                  this.positionY = this.offsetY;
                }) _// 当触发拖动手势时，根据回调函数修改组件的布局位置信息_
                .onActionUpdate((event: GestureEvent | undefined) => {
                  if (!this.flag) {
                    return;
                  }
                  if (event) {
                    this.offsetX = this.positionX + event.offsetX;
                    this.offsetY = this.positionY + event.offsetY;
                  }
                })
                .onActionEnd(() => {
              _    // AppStorage设置对应属性值_
                  AppStorage.setOrCreate('offsetX', this.offsetX);
                  AppStorage.setOrCreate('offsetY', this.offsetY);
                })
            );
    
          Button('组件2')
            .zIndex(2)
         _   // 组件平移位置_
            .translate({ x: this.offset2X, y: this.offset2Y, z: 0 })
            .gesture(
            _  // 绑定拖动手势_
              PanGesture()
                .onActionStart(() => {
                  this.positionX = this.offset2X;
                  this.positionY = this.offset2Y;
                }) _// 当触发拖动手势时，根据回调函数修改组件的布局位置信息_
                .onActionUpdate((event: GestureEvent | undefined) => {
                  if (!this.flag) {
                    return;
                  }
                  if (event) {
                    this.offset2X = this.positionX + event.offsetX;
                    this.offset2Y = this.positionY + event.offsetY;
                  }
                })
                .onActionEnd(() => {
              _    // AppStorage设置对应属性值_
                  AppStorage.setOrCreate('offset2X', this.offset2X);
                  AppStorage.setOrCreate('offset2Y', this.offset2Y);
                })
            );
        }
        .height('100%')
        .width('100%');
      }
    }