从零开始纯血鸿蒙天气预报-城市管理页面(1)

易得天气

目前做了城市列表的展示、添加城市、删除城市、批量删除城市。城市列表的拖动排序还未做

由于代码量上来了,只贴关键代码,等做完了会开源出来

CityManagerViewModel类管理城市管理页面的状态

less 复制代码
// 管理页面是否是编辑模式
@Trace isEditMode: boolean = false
// 编辑模式下被选中的城市
@Trace selectedList: Array<CityData> = []
// 城市列表滑动时标题栏的透明度
@Trace titleOpacity: number = 0

标题的显示根据isEditMode和selectedList共同决定

kotlin 复制代码
@Computed
get title() {
  return this.cityManagerVm.isEditMode ?
    (ArrayUtil.isEmpty(this.cityManagerVm.selectedList) ? '请选择项目' :
      '已选择' + this.cityManagerVm.selectedList.length + '项') : '城市管理'
}

中间的标题

scss 复制代码
@Builder
centerTitle() {
  Text(this.title)
    .fontSize(18)
    .fontColor($r('app.color.black'))
    .opacity(this.cityManagerVm.titleOpacity)
}

右侧的按钮显示逻辑、点击事件

kotlin 复制代码
@Builder
rightIcon() {
  Image(this.cityManagerVm.isEditMode ? $r('app.media.ic_select_all_icon') : $r('app.media.ic_search_icon'))
    .width(20)
    .height(20)
    .colorFilter(this.cityManagerVm.isEditMode ?
    ColorUtils.translateColor(this.cityManagerVm.isSelectedAll(this.addedCityData) ? $r('app.color.app_main') :
    $r('app.color.special_black')) : undefined)
    .onClick(() => {
      if (this.cityManagerVm.isEditMode) {
        if (this.cityManagerVm.isSelectedAll(this.addedCityData)) {
          this.cityManagerVm.clearSelected()
        } else {
          this.cityManagerVm.selectedAll(this.addedCityData)
        }
      } else {
        ZRouter.getInstance()
          .push(RouterConstants.SELECT_CITY_PAGE)
      }
    })
}

内容区域

scss 复制代码
@Builder
contentView() {
  Stack({ alignContent: Alignment.Bottom }) {
    // 城市列表
    this.cityList()
    // 底部删除按钮
    this.bottomOperateButton()
  }
  .width('100%')
  .layoutWeight(1)
}

城市列表,其中标题的透明度变化在onDidScroll回调函数进行,swipeAction是侧滑按钮,transition是会执行List删除动画

kotlin 复制代码
@Builder
cityList() {
  List({ scroller: this.listScroller }) {
    ListItem() {
      Text(this.title)
        .fontSize(28)
        .fontColor($r('app.color.black'))
        .opacity(1 - this.cityManagerVm.titleOpacity)
        .padding({ left: 16, top: 12 })
        .height(60)
    }

    ForEach(this.addedCityData, (cityData: CityData) => {
      ListItem() {
        CityManagerItem({
          cityData: cityData,
          isEditMode: this.cityManagerVm.isEditMode,
          isSelected: this.cityManagerVm.isSelected(cityData),
          toEditMode: (cityData) => {
            this.cityManagerVm.toEditMode(this.addedCityData?.length ?? 0, cityData)
          },
          onItemClick: (cityData) => {
            if (this.cityManagerVm.isEditMode) {
              this.cityManagerVm.selected(cityData)
            }
          }
        })
      }
      .swipeAction({
        end: !cityData.isLocationCity && !this.cityManagerVm.isEditMode ? () => {
          this.itemEnd(cityData)
        } : undefined,
      })
      .transition(TransitionEffect.SLIDE)
    }, (cityData: CityData): string => {
      return cityData.cityid ?? ''
    })
    ListItem() {
      Divider()
        .width('100%')
        .height(px2vp(AppUtil.getNavigationIndicatorHeight()) + (this.cityManagerVm.isEditMode ? 54 : 0))
        .color($r('app.color.transparent'))
    }
  }
  .width('100%')
  .height('100%')
  .divider({ strokeWidth: 12, color: $r('app.color.transparent') })
  .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
  .onDidScroll(() => {
    const yOffset = this.listScroller.currentOffset().yOffset
    const percent = fixPercent(yOffset / 72)
    this.cityManagerVm.changeTitleOpacity(percent)
  })
}

CityManagerItem是城市列表每一项item

kotlin 复制代码
// 定位的城市不支持编辑
if (!this.isLocationCity) {
  this.editItem()
}
this.cityItem()

editItem,其中animatableEditItem让item支持动画

scss 复制代码
@Builder
editItem() {
  Row() {
    Image($r('app.media.ic_menu_icon'))
      .width(24)
      .height(24)
      .colorFilter(ColorUtils.translateColor($r('app.color.special_white')))
    Image(this.isSelected ? $r('app.media.ic_checked_icon') : $r('app.media.ic_check_icon'))
      .width(22)
      .height(22)
      .colorFilter(ColorUtils.translateColor(this.isSelected ? $r('app.color.app_main') :
      $r('app.color.special_white')))
      .backgroundColor(this.isEditMode && this.isSelected ? $r('app.color.special_white') :
      $r('app.color.transparent'))
      .borderRadius(100)
  }
  .width('100%')
  .padding({ left: 16, right: 16 })
  .animatableEditItem(this.isEditMode ? 1 : 0)
  .animation({ duration: 200, curve: Curve.Linear })
  .justifyContent(FlexAlign.SpaceBetween)
}
css 复制代码
@AnimatableExtend(Row)
function animatableEditItem(visibility: number) {
  .opacity(visibility)
  .visibility(visibility <= 0 ? Visibility.Hidden : Visibility.Visible)
}

cityItem编辑模式下的动画

kotlin 复制代码
.animatableCityItemPadding(this.isEditMode && !this.isLocationCity ? 52 : 16)
.animation({ duration: 200, curve: Curve.Linear })
less 复制代码
@AnimatableExtend(Row)
function animatableCityItemPadding(padding: number) {
  .padding({ left: padding, right: padding })
}

添加长按手势进入编辑模式

javascript 复制代码
.gesture(
  LongPressGesture()
    .onAction((event: GestureEvent | undefined) => {
      if (event) {
        if (this.toEditMode) {
          this.toEditMode(this.cityData)
        }
      }
    })
)

添加城市:

侧滑删除

批量删除

相关推荐
nashane1 天前
HarmonyOS 6学习:CapsLock键失效诊断与长截图完整实现指南
学习·华为·harmonyos
richard_yuu1 天前
鸿蒙心理测评模块实战|PHQ-9/GAD7双量表答题、实时计分与结果本地化存储
华为·harmonyos
不爱吃糖的程序媛1 天前
2026年Electron 鸿蒙PC环境搭建指南
人工智能·华为·harmonyos
nashane1 天前
HarmonyOS 6学习:长截图功能开发中的滚动拼接与权限处理实战
人工智能·华为·harmonyos
大师兄66681 天前
从零开发一个 HarmonyOS 输入法——KikaInputMethod 完整拆解
harmonyos·服务卡片·harmonyos6·formkit
Python私教1 天前
鸿蒙 NEXT 也能接 MCP?用 ArkTS 跑通 AI Agent 工具链
人工智能·华为·harmonyos
Swift社区2 天前
分布式能力在鸿蒙 PC 上到底怎么用?
分布式·华为·harmonyos
nashane2 天前
HarmonyOS 6学习:外接键盘CapsLock与长截图功能的实战调试与完整解决方案
学习·华为·计算机外设·harmonyos
aqi002 天前
一文理清 HarmonyOS 6.0.2 涵盖的十个升级点
android·华为·harmonyos·鸿蒙·harmony