DEV Community

Cover image for HarmonyOS Yuan Service Project Actual Combat: Memorandum UI Page Development
程序员一鸣
程序员一鸣

Posted on

HarmonyOS Yuan Service Project Actual Combat: Memorandum UI Page Development

Foreword 

this article is based on Api12 

before, I wrote an article about the shelf process of meta-service projects. In order to better understand and develop meta-service, I am going to develop a small project from 0 to 1. I also hope to help students who have just stepped into Hongmeng's development. The specific project is also very simple. It is a small memo project that can edit the content and display the edited content list. The development is very fast, usually half a day to a day can be done. 

The biggest reason for choosing such a project is that there is no need for networking operations, and the data is stored locally, which is convenient for individual developers to develop and subsequently put on shelves. 

Right, at present remember meta-service items have been put on the shelves and can be searched in the application store. Remember can experience. 

Let's look at the final effect: 

home page, no data status 

Image description

home page, with data status 

Image description

edit Page 

Image description

since it is the first article before the project is developed, this article will take everyone to draw the basic UI. 

Home page UI 

the UI on the first page is very simple. The structure from top to bottom is the title bar, search box and Memo List. In page UI layout, we can select the Column component as the root layout, and then arrange it from top to bottom, because there are edit buttons and the default page when there is empty data. It is recommended to use the RelativeContainer component directly here. Of course, this is not the only layout method.

Title bar, you can use the Text component settings, Search using the Search component, List, directly use the List component, as for the default component, need to be dynamically displayed according to whether there is memo data. 

The complete code is as follows:

 

RelativeContainer() {
  Column() {
    ActionBar({ title: "suixinji" })

    Search({ placeholder: "search……" })
      .margin({ left: 10, right: 10, top: 10 })

    List({ space: 10 }) {
      ForEach(this.mListContentBean, (item: ListContentBean, index: number) => {
        ListItem() {
          Column() {
            Text(item.time)
              .width("100%")
              .textAlign(TextAlign.End)
              .margin({ top: 5, right: 5 })

            Text(item.title)
              .fontWeight(FontWeight.Bold)
              .width("100%")

            Text(item.desc)
              .width("100%")
              .margin({ top: 10, bottom: 10 })
          }
          .width("100%")
            .height(100)
            .padding({
              top: 5,
              bottom: 5,
              left: 10,
              right: 10
            })
            .backgroundColor(item.bgColor == undefined ? "#e8e8e8" : item.bgColor)
            .borderRadius(10)
        }.swipeAction({
        end: {
          builder: () => {
            this.swipeDelete(this, item.bgColor == undefined ? "#e8e8e8" : item.bgColor,
                             item.id?.toString(), index)
          },
          actionAreaDistance: 80,
        }
      })
              .onClick(() => {

              })
              })
    }
    .width("100%")
      .layoutWeight(1)
      .padding({ left: 10, right: 10 })
      .margin({ top: 10 })
  }


  Text("+")
    .width(60)
    .height(60)
    .backgroundColor("#FB553C")
    .borderRadius(50)
    .fontColor(Color.White)
    .fontSize(45)
    .textAlign(TextAlign.Center)
    .margin({ right: 20, bottom: 20 })
    .alignRules({
      bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
      right: { anchor: "__container__", align: HorizontalAlign.End }
    })
    .onClick(() => {


    })


  Text("NORMAL")
    .fontWeight(FontWeight.Bold)
    .visibility(this.isContentEmpty ? Visibility.Visible : Visibility.None)
    .alignRules({
      center: { anchor: "__container__", align: VerticalAlign.Center },
      middle: { anchor: "__container__", align: HorizontalAlign.Center }
    })


}.height('100%')
  .width('100%')
Enter fullscreen mode Exit fullscreen mode

edit Page UI 

the editing page is relatively more complicated. In addition to the content editing component, a skin change at the top and a row of style settings at the bottom are added. The same root layout is also the RelativeContainer component. 

There are two points to pay attention to when drawing the editing page UI. One is a row of style buttons at the bottom, which need to be dynamically set according to the height of the soft keyboard. Of course, this article is only UI drawing, and we will talk about it in the following chapters. The other is the style list, such as text color, text size, skin list, etc., which needs to be hidden by dynamic display boxes. 

How to dynamically display the style effect? It's very simple. You can write the style effect first, then hide it first. When you click on it, you can display it. For example, the skin change at the top. You can write the skin change UI first.

 

Column() {
        List() {
          ForEach(this.skinColors, (item: string, index: number) => {
            ListItem() {
              Column() {
                Image($r("app.media.complete"))
                  .width(20)
                  .height(20)
                  .visibility(this.clickSkinPosition == index ? Visibility.Visible : Visibility.None)
                  .border({ width: 1, color: "#666666", radius: 20 })
                  .margin({ bottom: 20 })
              }
              .width(100)
              .height(160)
              .backgroundColor(item)
              .justifyContent(FlexAlign.End)
              .borderRadius(5)
              .margin({ left: 10 })
              .onClick(() => {
                this.clickSkinPosition = index
                this.clickSkinColorValue = item
                this.isClickSkin = !this.isClickSkin
              })
            }
          })
        }
        .width("100%")
        .height(180)
        .backgroundColor("#e8e8e8")
        .listDirection(Axis.Horizontal)
        .padding({ top: 10 })
        .scrollBar(BarState.Off)
      }
      .backgroundColor(Color.Transparent)
      .width("100%")
      .height("100%")
      .onClick(() => {
        this.isClickSkin = !this.isClickSkin
      })
      .visibility(this.isClickSkin ? Visibility.Visible : Visibility.None)
      .alignRules({
        top: { anchor: "bar", align: VerticalAlign.Bottom },
      })
Enter fullscreen mode Exit fullscreen mode

When you click the Skin Resurfacing button, a skin resurfacing list is displayed.

 


 this.isClickSkin = !this.isClickSkin
Enter fullscreen mode Exit fullscreen mode

Basic effect 

Image description

edit all UI codes on the page


 

RelativeContainer() {
      ActionBar({
        title: "EDIT",
        leftIcon: $r("app.media.complete"),
        left2Icon: $r("app.media.skin"),
        leftMenuAttribute: {
          imageWidth: 22,
          imageHeight: 22
        },
        leftMenu2Attribute: {
          imageWidth: 22,
          imageHeight: 22,
          imageMargin: { left: 20 }
        },
        onLeftImageClick: (position) => {
          if (position == 0) {

            router.back()
          } else {

            this.isClickSkin = !this.isClickSkin
          }
        }
      }).id("bar")

      Column() {
        TextInput({ placeholder: "Please enter a note title", text: $$this.title })
          .backgroundColor(Color.Transparent)
          .placeholderFont({ weight: FontWeight.Bold, size: 15 })
          .placeholderColor("#666666")
          .fontSize(18)
          .maxLength(50)
          .fontColor("#222222")
          .fontWeight(FontWeight.Bold)
          .caretColor(Color.Red)//光标的颜色
          .padding(10)
          .borderRadius(0)
          .margin({ top: 10 })

        Text() {
          Span(this.nowTime)
        }
        .width("100%")
        .fontSize(13)
        .fontColor("#666666")
        .padding({ left: 10 })
        .margin({ top: 10 })

        RichEditor(this.options)
          .onReady(() => {

            //获取当前的时间
            this.nowTime = this.getDateTime()
            this.nowInterval = setInterval(() => {
              this.nowTime = this.getDateTime()
            }, 1000)

          })
          .placeholder("Take notes at will, record every little bit", {
            fontColor: "#666666"
          })
          .caretColor(Color.Red)
          .padding(10)
          .margin({ top: 10 })
          .onSelect((value: RichEditorSelection) => {
            this.start = value.selection[0];
            this.end = value.selection[1];
          })
      }
      .alignRules({
        top: { anchor: "bar", align: VerticalAlign.Bottom },
        bottom: { anchor: "bottom_bar", align: VerticalAlign.Top }
      }).margin({ bottom: 80 })

      Column() {
        List({ space: 10 }) {
          ForEach(this.fontColors, (item: ResourceColor) => {
            ListItem() {
              Text()
                .width(20)
                .height(20)
                .backgroundColor(item)
                .borderRadius(20)
                .border({ width: 1, color: "#e8e8e8" })
                .onClick(() => {
                  this.clickStyleColorValue = item
                  this.changeStyle()
                  this.setFontColor()
                })
            }
          })
        }
        .width("100%")
        .height(30)
        .listDirection(Axis.Horizontal)
        .padding({ left: 10, right: 10 })
        .scrollBar(BarState.Off)
        .visibility(this.isClickStyleColor ? Visibility.Visible : Visibility.None)

        List({ space: 10 }) {
          ForEach(this.fontSizes, (item: string, index: number) => {
            ListItem() {
              Text(item)
                .height(20)
                .borderRadius(20)
                .fontColor(Color.Black)
                .fontWeight(FontWeight.Bold)
                .onClick(() => {
                  let fontSize = 15
                  if (index == 0) {
                    fontSize = 21
                  } else if (index == 1) {
                    fontSize = 20
                  } else if (index == 2) {
                    fontSize = 19
                  } else if (index == 3) {
                    fontSize = 18
                  } else if (index == 4) {
                    fontSize = 17
                  } else if (index == 5) {
                    fontSize = 16
                  } else if (index == 6) {
                    fontSize = 15
                  } else {
                    fontSize = Number(item)
                  }
                  this.clickStyleSizeValue = fontSize

                  this.changeStyle()
                  //设置文字大小
                  this.setFontSize()
                })
            }
          })
        }
        .width("100%")
        .height(30)
        .listDirection(Axis.Horizontal)
        .padding({ left: 10, right: 10 })
        .scrollBar(BarState.Off)
        .visibility(this.isClickStyleSize ? Visibility.Visible : Visibility.None)

        Row() {
          Image($r("app.media.font_size"))
            .onClick(() => {
              this.isClickStyleSize = !this.isClickStyleSize
              this.setBold()
            })
            .backgroundColor(this.isClickStyleSize ? "#e8e8e8" : Color.Transparent)
            .width(20)
            .height(20)

          Text("B")
            .onClick(() => {
              this.isClickStyleB = !this.isClickStyleB
              this.changeStyle()
              this.setBold()
            })
            .fontWeight(FontWeight.Bold)
            .fontSize(20)
            .backgroundColor(this.isClickStyleB ? "#e8e8e8" : Color.Transparent)
            .width(30)
            .height(30)
            .textAlign(TextAlign.Center)
            .margin({ left: 20 })
          Text("I")
            .onClick(() => {
              this.isClickStyleI = !this.isClickStyleI
              this.changeStyle()
              this.setStyle()
            })
            .fontWeight(FontWeight.Bold)
            .fontStyle(FontStyle.Italic)
            .backgroundColor(this.isClickStyleI ? "#e8e8e8" : Color.Transparent)
            .fontSize(20)
            .margin({ left: 20 })
            .width(30)
            .height(30)
            .textAlign(TextAlign.Center)

          Image($r("app.media.color_bg"))
            .onClick(() => {
              this.isClickStyleColor = !this.isClickStyleColor
            })
            .backgroundColor(this.isClickStyleColor ? "#e8e8e8" : Color.Transparent)
            .margin({ left: 20 })
            .width(20)
            .height(20)
        }
        .width("100%")
        .height(50)
        .backgroundColor(this.clickSkinColorValue)
        .border({ width: { top: 1 }, color: "#e8e8e8" })
        .padding({ left: 20, right: 20 })

      }.id("bottom_bar")
      .alignRules({
        bottom: { anchor: "__container__", align: VerticalAlign.Bottom }
      })


      Column() {
        List() {
          ForEach(this.skinColors, (item: string, index: number) => {
            ListItem() {
              Column() {
                Image($r("app.media.complete"))
                  .width(20)
                  .height(20)
                  .visibility(this.clickSkinPosition == index ? Visibility.Visible : Visibility.None)
                  .border({ width: 1, color: "#666666", radius: 20 })
                  .margin({ bottom: 20 })
              }
              .width(100)
              .height(160)
              .backgroundColor(item)
              .justifyContent(FlexAlign.End)
              .borderRadius(5)
              .margin({ left: 10 })
              .onClick(() => {
                this.clickSkinPosition = index
                this.clickSkinColorValue = item
                this.isClickSkin = !this.isClickSkin
              })
            }
          })
        }
        .width("100%")
        .height(180)
        .backgroundColor("#e8e8e8")
        .listDirection(Axis.Horizontal)
        .padding({ top: 10 })
        .scrollBar(BarState.Off)
      }
      .backgroundColor(Color.Transparent)
      .width("100%")
      .height("100%")
      .onClick(() => {
        this.isClickSkin = !this.isClickSkin
      })
      .visibility(this.isClickSkin ? Visibility.Visible : Visibility.None)
      .alignRules({
        top: { anchor: "bar", align: VerticalAlign.Bottom },
      })
    }
    .height('100%')
    .width('100%')
    .height(this.screenHeight) 
    .backgroundColor(this.clickSkinColorValue)
    .expandSafeArea([SafeAreaType.SYSTEM, SafeAreaType.KEYBOARD], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
Enter fullscreen mode Exit fullscreen mode

related Summary 

there is nothing to say about UI page drawing, that is, the placement of components and the display logic of components. There are many attributes that are not recorded in the article. You can check them in the warehouse. One of my title bar components is used in the article. If you don't want to use them, you can use what you wrote.

Top comments (0)