#2023盲盒+码# 通过商城应用熟悉常用控件用法 原创

droidzxy
发布于 2023-9-1 17:18
浏览
1收藏

【本文正在参加 2023「盲盒」+码有奖征文活动】,活动链接 https://ost.51cto.com/posts/25284

应用简介

本应用是在Huawei的codelabs上的购物类应用的基础上开发的一个程序,只是添加并丰富了一些内容,对改动做一些简要的介绍,主要是通过实践熟悉一下常用控件的用法。

效果预览

#2023盲盒+码# 通过商城应用熟悉常用控件用法-鸿蒙开发者社区

简要介绍

1.点击列表项跳转到对应详情Page页

#2023盲盒+码# 通过商城应用熟悉常用控件用法-鸿蒙开发者社区

通过在ListItem中添加onClick事件,实现点击跳转的目的,在页面跳转时传递want参数,里面包括包名称、页面名称和一些参数。

ListItem() {
  ...
}.onClick(() => {
  let handler = getContext(this) as AppContext.Context;
  viewModel.startDetailsAbility(handler, item?.position);
})

public startDetailsAbility(context, index: number): void {
  const want = {
    bundleName: getContext(context).applicationInfo.name,
    abilityName: DETAILS_ABILITY_NAME,
    parameters: {
      position: index
    }
  };
  try {
    context.startAbility(want);
  } catch (error) {
    hilog.error(HOME_PAGE_DOMAIN, TAG, '%{public}s', error);
  }
}

2.详情Page页可以上下滑动

#2023盲盒+码# 通过商城应用熟悉常用控件用法-鸿蒙开发者社区

页面的上下滑动,是通过scroll组件来完成的,通常把单屏显示不下的,需要滑动显示的内容,先通过column组合在一起,然后再放置到scroll里面。


build() {
  Column() {
    Scroll() {
      Column() {
        Stack({ alignContent: Alignment.Top }) {
          // GoodsPreviewer displays images about goods.
          PreviewerComponent({ goodsImages: this.goodsDetails.goodsImages })
          this.TopBarLayout()
        }
        .height(DetailsPageStyle.TOP_LAYOUT_HEIGHT)
        .width(PERCENTAGE_100)
        .backgroundColor($r('app.color.background1'))
        // the card layout style about goods information.
        this.CardsLayout()
      }.width(PERCENTAGE_100)
    }
    .height(DetailsPageStyle.SCROLL_LAYOUT_WEIGHT)
    .backgroundColor($r('app.color.background'))
    // tool bar in the bottom.
    BottomBarComponent().height(DetailsPageStyle.TOOLBAR_WEIGHT)
  }
  .height(PERCENTAGE_100)
  .width(PERCENTAGE_100)
}

3.点击左上角返回按钮,返回到列表页

#2023盲盒+码# 通过商城应用熟悉常用控件用法-鸿蒙开发者社区

左上角的返回,实际是一个图片组件,点击后关闭当前页面自然就显示之前的页面了。

@Builder BackLayout() {
  Image($rawfile('detail/detail_back.png'))
    .setTopImageStyle()
    .onClick(() => {
      let handler = getContext(this) as AppContext.UIAbilityContext;
      handler.terminateSelf();
    })
}

4.Swiper显示4张图片

#2023盲盒+码# 通过商城应用熟悉常用控件用法-鸿蒙开发者社区

4张图片路径是放置在一个数组中,通过ForEach加载到Swiper容器组件里,

Swiper(this.swiperController) {
  ForEach(this.goodsImages, (item: Resource) => {
    // Text(item).width('90%').height(160).backgroundColor(0xAFEEEE).textAlign(TextAlign.Center).fontSize(30)
    Image(item)
      .objectFit(ImageFit.Fill)
      .height("100%")
      .width("100%")
      .borderRadius(12)
      .align(Alignment.Center)

  }, item => JSON.stringify(item))
}
.cachedCount(2)
.index(0)
.autoPlay(true)
.interval(2000)
.indicator(true)
.loop(true)
.duration(500)
.itemSpace(0)
.curve(Curve.Linear)
.onChange((index: number) => {
  console.info(index.toString())
  this.indicator = index + 1
})

5.商品价格、描述等和点击的列表项一致

#2023盲盒+码# 通过商城应用熟悉常用控件用法-鸿蒙开发者社区

商品价格保持一致,实现方法是靠startAbility时传递参数,打开DetailsAbility 时,调用了DetailsAbility
.ts的onCreate函数,在其中有AppStorage.SetOrCreate(KEY, index)的调用,这样就把position参数保存为应用全局状态,
在DetailPage.ets中通过let position = AppStorage.Get<number>(KEY);获取具体位置,进而viewModel获得对应详细数据
this.goodsDetails = viewModel.loadDetails(position);
价钱和其他的具体信息就都能获得了。


const want = {
  bundleName: getContext(context).applicationInfo.name,
  abilityName: DETAILS_ABILITY_NAME,
  parameters: {
    position: index
  }
};
try {
  context.startAbility(want);
} catch (error) {
  hilog.error(HOME_PAGE_DOMAIN, TAG, '%{public}s', error);
}

export default class DetailsAbility extends UIAbility {
    onCreate(want, launchParam) {
        let index: number = want?.parameters?.position;
        AppStorage.SetOrCreate(KEY, index);
        hilog.info(DETAIL_ABILITY_DOMAIN, TAG, '%{public}s', 'Ability onCreate');
    }
...
const KEY: string = 'GoodsPosition';
let position = AppStorage.Get<number>(KEY);

aboutToAppear() {
  this.goodsDetails = viewModel.loadDetails(position);

  this.commentCount = this.goodsDetails.comment_count;
  this.commentPercent = this.goodsDetails.comment_percent;
}

6.用户评价通过List+懒加载的方式展示

#2023盲盒+码# 通过商城应用熟悉常用控件用法-鸿蒙开发者社区

当列表中数据量较大时,一般推荐使用懒加载模式,


List({ space: CommentList.SPACE }) {
  LazyForEach(this.commentsListData, (item) => {
    ListItem() {
      Column() {
        Row() {
	...
		}
	}

    }
  })
}
.width(PERCENTAGE_100)
.backgroundColor(Color.White)
.edgeEffect(EdgeEffect.None)

第一个参数this.commentsListData是需要进行数据迭代的数据源,

@Component
export default struct CommentsComponent {
  @Provide commentsListData: CommentDataSource = new CommentDataSource();


export class CommentDataSource extends BasicDataSource {
  private listData = createListRange();

  public totalCount(): number {
    return this.listData.length;
  }

  public getData(index: number): CommentItemType {
    return this.listData[index];
  }

}


class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = []

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): CommentItemType {
    return undefined;
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      this.listeners.push(listener);
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const position = this.listeners.indexOf(listener);
    if (position >= 0) {
      this.listeners.splice(position, 1);
    }
  }

  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }

  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    })
  }
}

7.完成ArkTS卡片

#2023盲盒+码# 通过商城应用熟悉常用控件用法-鸿蒙开发者社区

添加卡片,直接在工程中添加一个ServiceWidget就可以了,然后进行页面布局,在根布局中添加以下代码就可以打开主应用了。

.onClick(() => {
  postCardAction(this, {
    "action": this.ACTION_TYPE,
    "abilityName": this.ABILITY_NAME,
    "params": {
      "message": this.MESSAGE
    }
  });
})

代码分析

1.页面跳转

页面间跳转是通过startAbility实现的,同时还可以传递一个want参数,便于携带一些变量的值。

const want = {
  bundleName: getContext(context).applicationInfo.name,
  abilityName: DETAILS_ABILITY_NAME,
  parameters: {
    position: index
  }
};
try {
  context.startAbility(want);
} catch (error) {
  hilog.error(HOME_PAGE_DOMAIN, TAG, '%{public}s', error);
}

此处的context实际是getContext(this) as AppContext.Context;

2.组件间参数传递

参数传递通过在父组件中设置@State装饰器修饰变量,在子组件中设置@prop装饰的变量,然后调用子组件时通过参数传递。

父组件

@Entry
@Component
struct DetailsPage {
  private goodsDetails: GoodsListItemType;
  @State commentCount: string = "";
  @State commentPercent: string = "";

  aboutToAppear() {
    this.goodsDetails = viewModel.loadDetails(position);

    this.commentCount = this.goodsDetails.comment_count;
    this.commentPercent = this.goodsDetails.comment_percent;
  }

子组件

@Component
export default struct CommentsHeaderComponent {
  @Prop commentCount: string;
  @Prop commentPercent: string;

组件调用

CardComponent() {
  CommentsHeaderComponent({ commentCount: this.commentCount, commentPercent: this.commentPercent })
  CommentsComponent().margin({ top: DetailsPageStyle.COMMENT_LIST_MARGIN_TOP })
}

3.扩展样式的选择,@Extend还是@Style?

针对组件属性反复设置,系统给了解决方案,可以通过扩展样式写到一个函数里,实现多次复用,避免了大量代码重复设置。但@Style只支持通用属性的扩展,而@Extend可以扩展原生组件的样式。
例如,以下的扩展只能用Extend,因为里面的样式只是针对Text组件的,和其他组件没有通用性。

@Extend(Text) function commentText () {
  .lineHeight(CommentHeader.LINE_HEIGHT)
  .textOverflow({ overflow: TextOverflow.Ellipsis })
  .textCase(TextCase.UpperCase)
  .fontSize(AppFontSize.MIDDLE)
  .fontColor($r('app.color.text'))
  .fontWeight(AppFontWeight.BOLDER)
  .textAlign(TextAlign.Start)
}

又比如,这里的Extend实际也可以用Style代替。

@Extend(Image) function setTopImageStyle() {
  .width(DetailsPageStyle.TOP_IMAGE_SIZE)
  .height(DetailsPageStyle.TOP_IMAGE_SIZE)
}

@Styles function setTopImageStyle() {
  .width(DetailsPageStyle.TOP_IMAGE_SIZE)
  .height(DetailsPageStyle.TOP_IMAGE_SIZE)
}

总结

通过这次操作实践,不仅复习了常用组件的基本用法,而且还对一些新技能有了深入的了解,例如,页面间携带参数跳转,懒加载方式的实现以及应用全局的状态管理方法等。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
2
收藏 1
回复
举报
回复
    相关推荐