1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { AppInfo } from '../model/AppInfo'; 17import { DataSource } from '../model/DateSource'; 18 19const ICON_NUM_IN_LIST: number = 4; // 示例List中子组件数目 20const LIST_SPACE: number = 30; // 列表默认间隔 21 22/** 23 * 实现List场景,拖拽交换子组件位置: 通过ListItem的onDragStart()方法指定拖拽开始时的行为,通过List的onTouch()指定拖拽释放时的行为。 24 */ 25@Component 26export struct ListSceneView { 27 @State dataSource: DataSource = new DataSource(); 28 @State dragIndex: number = 0; 29 30 aboutToAppear() { 31 for (let index = 0; index < ICON_NUM_IN_LIST; index++) { 32 this.dataSource.pushData(new AppInfo($r(`app.media.drag_and_exchange_ic_public_game${index + 1}`), 33 `Item${index + 1}`, true)); 34 } 35 } 36 37 changeIndex(index1: number, index2: number) { 38 let temp: AppInfo = this.dataSource.getData(index1); 39 this.dataSource.setData(index1, this.dataSource.getData(index2)); 40 this.dataSource.setData(index2, temp); 41 } 42 43 build() { 44 Column() { 45 Text($r('app.string.drag_and_exchange_list_drag_title')) 46 .fontColor(Color.White) 47 .textAlign(TextAlign.Center) 48 .fontSize($r('app.string.drag_and_exchange_opt_title_font_size')) 49 Row() { // 仅靠List实现背景框,padding调整样式后,互换时可能错位 50 List({ space: LIST_SPACE }) { 51 // TODO: 性能知识点:图标一次性完全显示,且禁用滑动,无需懒加载。LazyForEach可以适用在动态添加数据的场景中,参考资料: 52 // https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/performance/lazyforeach_optimization.md/ 53 LazyForEach(this.dataSource, (item: AppInfo, index) => { 54 ListItem() { 55 Column() { 56 IconNoNameView({ app: item }) 57 } 58 } 59 // TODO:知识点:在ListItem层,通过onDragStart实现拖拽开始时的回调行为 60 .onDragStart((event: DragEvent, extraParams: string) => { 61 item.visible = false; // 拖拽时,设置子组件原位置图标不可见 62 // 记录目标位置子组件index值 63 this.dragIndex = (JSON.parse(extraParams) as JsonObjType).selectedIndex; 64 }) 65 .onDragEnd(() => { 66 item.visible = true; 67 }) 68 }, (item: AppInfo) => item.name.toString()) 69 } 70 .scrollBar(BarState.Off) 71 .height($r('app.string.drag_and_exchange_layout_90')) 72 .listDirection(Axis.Horizontal) 73 .alignListItem(ListItemAlign.Center) 74 .onDrop((event: DragEvent, extraParams: string) => { // TODO:知识点:在List层,通过onDrop实现拖拽结束后的回调行为 75 // 通过参数extraParams获取原位置子组件index值 76 let insertIndex: number = (JSON.parse(extraParams) as JsonObjType).insertIndex; 77 if (insertIndex >= this.dataSource.totalCount()) { 78 return; 79 } 80 this.changeIndex(this.dragIndex, insertIndex); // 互换子组件index值 81 this.dataSource.notifyDataReload(); 82 }) 83 .enableScrollInteraction(false) // 禁用滑动 84 .alignListItem(ListItemAlign.Center) 85 .padding({ 86 top: $r('app.string.drag_and_exchange_layout_10'), 87 bottom: $r('app.string.drag_and_exchange_layout_10'), 88 left: $r('app.string.drag_and_exchange_layout_15'), 89 right: $r('app.string.drag_and_exchange_layout_15') 90 }) 91 } 92 .justifyContent(FlexAlign.Center) 93 .height($r('app.string.drag_and_exchange_layout_90')) 94 .width($r('app.string.drag_and_exchange_layout_90_percent')) 95 .borderRadius($r('app.string.drag_and_exchange_layout_20')) 96 .opacity($r('app.string.drag_and_exchange_background_opacity')) 97 .backgroundColor($r('app.color.drag_and_exchange_background_color')) 98 } 99 .margin({ top: $r('app.string.drag_and_exchange_layout_20') }) 100 } 101} 102 103/** 104 * 无名字App自定义组件 105 */ 106@Component 107struct IconNoNameView { 108 @ObjectLink app: AppInfo; 109 110 build() { 111 Column() { 112 Image(this.app.icon) 113 .id(`${this.app.name}`) 114 .width($r('app.string.drag_and_exchange_icon_square_size')) 115 .height($r('app.string.drag_and_exchange_icon_square_size')) 116 .objectFit(ImageFit.Cover) 117 .borderRadius($r('app.string.drag_and_exchange_layout_10')) 118 .draggable(false) // TODO:知识点:保持默认值true时,图片有默认拖拽效果,会影响List子组件拖拽动效,所以修改为false 119 Text(this.app.name) 120 .width($r('app.string.drag_and_exchange_icon_square_size')) 121 .fontColor(Color.White) 122 .textAlign(TextAlign.Center) 123 .margin({ top: $r('app.string.drag_and_exchange_layout_1') }) 124 .fontSize($r('app.string.drag_and_exchange_app_name_font_size')) 125 } 126 // 消失时需要占位,所以使用显隐控制而非条件渲染。(条件渲染与显隐控制区别, 127 // 参考资料: 128 // https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/performance/proper-choice-between-if-and-visibility.md/) 129 .visibility(this.app.visible ? Visibility.Visible : 130 Visibility.Hidden) 131 } 132} 133 134/** 135 * 封装处理处理JSON对象的类 136 */ 137class JsonObjType { 138 public insertIndex: number; 139 public selectedIndex: number; 140 141 constructor(insertIndex: number, selectedIndex: number) { 142 this.insertIndex = insertIndex; 143 this.selectedIndex = selectedIndex; 144 } 145}