天天看點

#DAYU200體驗官# 起司播客App主題

(目錄)

主題

本帖使用Dayu200為開發闆,展示一個線上播音App - 起司播客。

預覽效果圖

#DAYU200體驗官# 起司播客App主題
#DAYU200體驗官# 起司播客App主題
#DAYU200體驗官# 起司播客App主題

Dayu200的預覽配置

為了大幅提高UI的開發效率,降低Dayu200的使用門檻,在開發過程中,強烈建議使用DevEco Studio 3.0 Beta3(OpenHarmony)的MatePadPro作為預覽配置,并調整到豎屏模式,最終與Dayu200上的效果近似一緻。

資源導入

本案例為了簡單起見,文字與顔色直接寫在代碼中,僅圖檔資源需要導入,将全部所需圖檔拖到pages的建立img子目錄中:

#DAYU200體驗官# 起司播客App主題

首頁結構

使用預設的index.ets入口頁作為啟動頁,分析頁面的結構,可以一個Column,從上至下依次是導航欄、分類标題、分類卡片清單、篩選欄、播客作品清單。

導航欄

導航欄的左側是一個按列布局的兩行文字,右側是一個頭像:

Row {

        Column {

          Text('起司播客')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)

          Text('愛情,生活,舒緩')
            .fontSize(15)
            .fontColor('#A3A1AF')

        }
	.alignItems(HorizontalAlign.Start)

        Blank()

        Image($r("app.media.profile"))
          .objectFit(ImageFit.Contain)
          .width(49)
          .height(49)

      }
      .width('100%')
      .padding({left:20,top:20,bottom:10,right:20})
           
#DAYU200體驗官# 起司播客App主題

分類标題

标題是一行文字,左對齊:

Row {

        Text('分類')
          .fontSize(20)
          .fontColor('#1E3354')

      }
      .width('100%')
      .padding({left:20,top:10,bottom:10,right:20})
           

分類卡片清單

定義分類清單的資料:

var cate = [
    {
      title: '音樂 & 娛樂',
      img: '/pages/img/cate1.png',
      total: '84',
      album: ['', '', '', '', '', '', ''],
    },
    {
      title: '生活 & 舒緩',
      img: '/pages/img/cate2.png',
      total: '96',
      album: ['', '', '', '', '', '', ''],
    },
    {
      title: '教育 & 學習',
      img: '/pages/img/cate3.png',
      total: '72',
      album: ['', '', '', '', '', '', ''],
    },
  ]
           

專輯資料:

var albums = [
    {
      title: 'Ngobam',
      cate: '音樂 & 娛樂',
      tag: 'pop',
      img: '/pages/img/item1.png',
      eps: '84',
      artist: 'Gofar Hilman'
    },
    {
      title: 'Semprod',
      cate: '生活 & 舒緩',
      tag: 'pop',
      img: '/pages/img/item2.png',
      eps: '44',
      artist: 'Kugo娛樂'
    },
    {
      title: 'Sruput Nendang',
      cate: '教育 & 學習',
      tag: 'pop',
      img: '/pages/img/item3.png',
      eps: '46',
      artist: 'Macro & Marlo'
    },
  ]
           

卡片本身由背景層和卡片文字組成。

背景層:

Column {

                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .borderRadius(20)

        }
              .width('100%')
              .height('100%')
           

卡片文字又上下排列的Column組成:

Column {

                Blank()

                Column {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                    .fontWeight(FontWeight.Bold)

                  Text(item.total + "個播客   ")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .borderRadius(20)
                .alignItems(HorizontalAlign.Start)
                .backgroundColor(Color.White)
                .opacity(0.6)
                .backdropBlur(8)
                .padding(20)
                .width('100%')
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
              .height('100%')
           
#DAYU200體驗官# 起司播客App主題

将背景層和卡片文字組合起來,加上使用者點選的互動:

Stack {

              Column {

                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .borderRadius(20)

              }
              .width('100%')
              .height('100%')

              Column {

                Blank()

                Column {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                    .fontWeight(FontWeight.Bold)

                  Text(item.total + "個播客   ")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .borderRadius(20)
                .alignItems(HorizontalAlign.Start)
                .backgroundColor(Color.White)
                .opacity(0.6)
                .backdropBlur(8)
                .padding(20)
                .width('100%')
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
              .height('100%')
            }
		.padding( left: 20 )
            .width(224)
            .height(296)
            .onClick( {() =>
              router.push(
                uri: 'pages/channel',
                params: {
                  place: item
                }
              )
            })
           
#DAYU200體驗官# 起司播客App主題

使用橫向滑動的List元件和ForEach對卡片資料進行循環:

List {

        ForEach(this.cate, item => {

          ListItem {

            Stack {

              Column {

                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .borderRadius(20)

              }
              .width('100%')
              .height('100%')

              Column {

                Blank()

                Column {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                    .fontWeight(FontWeight.Bold)

                  Text(item.total + "個播客   ")
                    .fontSize(15)
                    .opacity(0.4)
                }
                .borderRadius(20)
                .alignItems(HorizontalAlign.Start)
                .backgroundColor(Color.White)
                .opacity(0.6)
                .backdropBlur(8)
                .padding(20)
                .width('100%')
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
              .height('100%')
            }.padding( left: 20 )
            .width(224)
            .height(296)
            .onClick( () =>{
              router.push(
                uri: 'pages/channel',
                params: {
                  place: item
                }
              )
            })

          }
        })
      }
      .listDirection(Axis.Horizontal)
      .width('100%')
      .height(296)
           
#DAYU200體驗官# 起司播客App主題

篩選欄

定義篩選欄的資料數組和對應的索引數組:

var filters: string[] = [
    "流行", "最近", "音樂", "舒緩", "R&B"
  ]
  var filterIndices: number[] = this.filters.map{(_, index) => index}
           

定義使用者選中的篩選欄狀态變量:

@State var selected: number = 0
           

篩選欄是一系列的自定義按鈕。單個按鈕由圖示加文字組成,第一個流行按鈕有帶火的圖示。篩選欄中如果任何一個按鈕被使用者點選,則顯示按鈕的背景以及文字變粗體:

Button {

              Row {

                if (index == 0) {
                  Image($r("app.media.fire"))
                    .objectFit(ImageFit.Contain)
                    .width(16).height(16)
                    .margin({ right: 10 })
                }

                Text(this.filters[index])
                  .fontSize(17)
                  .fontWeight(this.selected == index ? FontWeight.Bold : FontWeight.Lighter)
                  .fontColor(this.selected == index ? '#413E50' : '#A3A1AF')
              }.padding(15)

            }
            .type(ButtonType.Normal)
            .backgroundColor(this.selected == index ? '#EDF0FC' : Color.White)
            .borderRadius(10)
            .height(50)
            .onClick( () =>{
              this.selected = index
            })
           
#DAYU200體驗官# 起司播客App主題

将篩選按鈕資料使用橫向滑動的List和ForEach進行循環渲染,即可得到按鈕組:

List( {space: 10} ) {

        ForEach(this.filterIndices,  index =>{

          ListItem {

            Button {

              Row {

                if (index == 0) {
                  Image($r("app.media.fire"))
                    .objectFit(ImageFit.Contain)
                    .width(16).height(16)
                    .margin({ right: 10 })
                }

                Text(this.filters[index])
                  .fontSize(17)
                  .fontWeight(this.selected == index ? FontWeight.Bold : FontWeight.Lighter)
                  .fontColor(this.selected == index ? '#413E50' : '#A3A1AF')
              }.padding(15)

            }
            .type(ButtonType.Normal)
            .backgroundColor(this.selected == index ? '#EDF0FC' : Color.White)
            .borderRadius(10)
            .height(50)
            .onClick( () =>{
              this.selected = index
            })
          }
        })
      }
      .listDirection(Axis.Horizontal)
      .width('100%')
      .height(90)
      .padding(20)
           
#DAYU200體驗官# 起司播客App主題
#DAYU200體驗官# 起司播客App主題

音樂清單

單個的音樂條目由專輯圖示、音樂名和作者、所屬分類和專輯數目組合在一行之内。

圓角的專輯圖示:

Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .width(56)
                  .height(56)
                  .borderRadius(20)
           
#DAYU200體驗官# 起司播客App主題

音樂名和作者:

Row {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding(left:10,right:10)

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')
           
#DAYU200體驗官# 起司播客App主題

所屬分類和專輯數目:

Row {

                  Text(item.cate)
                    .fontSize(16)
                    .opacity(0.4)

                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding(left:5,right:5)

                  Text(item.eps + "個Ep")
                    .fontSize(15)
                    .opacity(0.4)

   }
                .width('90%')
           
#DAYU200體驗官# 起司播客App主題

将三個部分依次組合起來:

Row {

                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .width(56)
                  .height(56)
                  .borderRadius(20)

              Column {

                Blank()

                Row {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding(left:10,right:10)

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)
                }
                .width('90%')

                Row {

                  Text(item.cate)
                    .fontSize(16)
                    .opacity(0.4)

                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding(left:5,right:5)

                  Text(item.eps + "個Ep")
                    .fontSize(15)
                    .opacity(0.4)
                }
                .width('90%')

                Blank()
              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin({left:20,top:10,bottom: 10, right:20})
            .padding(10)
            .width('90%')
            .height(72)
           
#DAYU200體驗官# 起司播客App主題

有了單個音樂條目的實作,就可以使用一個縱向滑動的List和ForEach循環渲染,再加上使用者點選跳轉到音樂播放頁的互動:

List {

        ForEach(this.albums, item =>{

          ListItem {

            Row {

              Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .width(56)
                  .height(56)
                  .borderRadius(20)

              Column {

                Blank()

                Row {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding(left:10,right:10)

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

                Row {

                  Text(item.cate)
                    .fontSize(16)
                    .opacity(0.4)

                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding(left:5,right:5)

                  Text(item.eps + "個Ep")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')
                Blank()
              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin({left:20,top:10,bottom: 10, right:20})
            .padding(10)
            .width('90%')
            .height(72)
            .onClick( () =>{
              router.push(
                uri: 'pages/detail',
                params: {
                  place: item
                }
              )
            })

          }
        })
      }
      .width('100%')
      .height('35%')
           
#DAYU200體驗官# 起司播客App主題

完整代碼和效果

将上述代碼從上到下依次放入Column中,組成首頁index.ets的完整代碼:

import router from '@system.router';

@Entry
@Component
struct Index {
  @State selected: number = 0
  filters: string[] = [
    "流行", "最近", "音樂", "舒緩", "R&B"
  ]
  filterIndices: number[] = this.filters.map((_, index) => index)
  cate = [
    {
      title: '音樂 & 娛樂',
      img: '/pages/img/cate1.png',
      total: '84',
      album: ['', '', '', '', '', '', ''],
    },
    {
      title: '生活 & 舒緩',
      img: '/pages/img/cate2.png',
      total: '96',
      album: ['', '', '', '', '', '', ''],
    },
    {
      title: '教育 & 學習',
      img: '/pages/img/cate3.png',
      total: '72',
      album: ['', '', '', '', '', '', ''],
    },
  ]
  albums = [
    {
      title: 'Ngobam',
      cate: '音樂 & 娛樂',
      tag: 'pop',
      img: '/pages/img/item1.png',
      eps: '84',
      artist: 'Gofar Hilman'
    },
    {
      title: 'Semprod',
      cate: '生活 & 舒緩',
      tag: 'pop',
      img: '/pages/img/item2.png',
      eps: '44',
      artist: 'Kugo娛樂'
    },
    {
      title: 'Sruput Nendang',
      cate: '教育 & 學習',
      tag: 'pop',
      img: '/pages/img/item3.png',
      eps: '46',
      artist: 'Macro & Marlo'
    },
  ]

  build() {
    Column() {
      Row() {

        Column() {
          Text('起司播客')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('愛情,生活,舒緩')
            .fontSize(15)
            .fontColor('#A3A1AF')
        }.alignItems(HorizontalAlign.Start)

        Blank()
        Image($r("app.media.profile"))
          .objectFit(ImageFit.Contain)
          .width(49)
          .height(49)
      }
      .width('100%')
      .padding({left:20,top:20,bottom:10,right:20})

      Row() {
        Text('分類')
          .fontSize(20)
          .fontColor('#1E3354')
      }
      .width('100%')
      .padding({left:20,top:10,bottom:10,right:20})

      List() {
        ForEach(this.cate, item => {
          ListItem() {
            Stack() {
              Column() {
                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .borderRadius(20)
              }
              .width('100%')
              .height('100%')

              Column() {
                Blank()
                Column() {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                    .fontWeight(FontWeight.Bold)
                  Text(item.total + "個播客   ").fontSize(15).opacity(0.4)
                }
                .borderRadius(20)
                .alignItems(HorizontalAlign.Start)
                .backgroundColor(Color.White)
                .opacity(0.6)
                .backdropBlur(8)
                .padding(20)
                .width('100%')
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
              .height('100%')
            }.padding({ left: 20 })
            .width(224)
            .height(296)
            .onClick(() => {
              router.push({
                uri: 'pages/channel',
                params: {
                  place: item
                }
              })
            })

          }
        })
      }
      .listDirection(Axis.Horizontal)
      .width('100%')
      .height(296)

      List({ space: 10 }) {
        ForEach(this.filterIndices, index => {
          ListItem() {
            Button() {
              Row() {
                if (index == 0) {
                  Image($r("app.media.fire"))
                    .objectFit(ImageFit.Contain)
                    .width(16).height(16)
                    .margin({ right: 10 })
                }
                Text(this.filters[index])
                  .fontSize(17)
                  .fontWeight(this.selected == index ? FontWeight.Bold : FontWeight.Lighter)
                  .fontColor(this.selected == index ? '#413E50' : '#A3A1AF')
              }.padding(15)

            }
            .type(ButtonType.Normal)
            .backgroundColor(this.selected == index ? '#EDF0FC' : Color.White)
            .borderRadius(10)
            .height(50)
            .onClick(() => {
              this.selected = index
            })
          }
        })
      }
      .listDirection(Axis.Horizontal)
      .width('100%')
      .height(90)
      .padding(20)

      List() {
        ForEach(this.albums, item => {
          ListItem() {
            Row() {
              Image(item.img)
                .objectFit(ImageFit.Cover)
                .width(56).height(56)
                .borderRadius(20)

              Column() {
                Blank()
                Row() {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding({left:10,right:10})
                  Text(item.artist).fontSize(15).opacity(0.4)
                }
                .width('90%')

                Row() {
                  Text(item.cate)
                    .fontSize(16)
                    .opacity(0.4)
                  Text("·").fontSize(15).opacity(0.2).padding({left:5,right:5})
                  Text(item.eps + "個Ep").fontSize(15).opacity(0.4)
                }
                .width('90%')
                Blank()
              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin({left:20,top:10,bottom: 10, right:20})
            .padding(10)
            .width('90%')
            .height(72)
            .onClick(() => {
              router.push({
                uri: 'pages/detail',
                params: {
                  place: item
                }
              })
            })

          }
        })
      }
      .width('100%')
      .height('35%')
    }
    .width('100%')

  }
}
           
#DAYU200體驗官# 起司播客App主題

頻道頁

頻道頁是一個播客作者的詳細介紹,結構的上半部分是播客資訊區域(導航欄、頭像、昵稱、播客描述、作品數和昵稱),下半部分是播客作品頁。在pages下建立一個channel.ets源檔案。

播客作品資料

播客個人資訊:

var author = {
    desc: '聽我用音樂娓娓道來',
    albums: 256,
    name: '奧珍妮博士',
    avatar: '/pages/img/uper_avatar1.png'
  }
           

播客作品資訊:

var albums = [
    {
      title: '工作和生活之間',
      img: '/pages/img/ep1.png',
      duration: '56:38',
      eps: '56',
    },
    {
      title: '前進的力量',
      img: '/pages/img/ep2.png',
      duration: '28:01',
      eps: '35',
    }, {
      title: '讓我驚喜的小猴',
      img: '/pages/img/ep3.png',
      duration: '1:40:20',
      eps: '42',
    }, {
      title: '我的愛情被疫情阻隔',
      img: '/pages/img/ep4.png',
      duration: '1:05:13',
      eps: '51',
    }, {
      title: '你為什麼要振作起來?',
      img: '/pages/img/ep5.png',
      duration: '45:28',
      eps: '77',
    },
  ]
           

導航欄

導航欄左側有一個傳回按鈕,中間是标題,右側留白:

Row {

        Image($r("app.media.back"))
          .objectFit(ImageFit.Contain)
          .width(20)
          .height(20)
          .onClick(()=>{
            router.back()
          })

        Blank()

        Text('播客')
          .fontSize(20)

        Blank()

      }
	.width('100%')
      .padding( {left: 20, top: 20, bottom: 10, right: 20} )
           
#DAYU200體驗官# 起司播客App主題

播客個人資訊區域

頭像、昵稱、播客描述、作品數和昵稱都組合在一個Column中:

Column({space:15}) {

        Image(this.author.avatar)
          .objectFit(ImageFit.Contain)
          .width(84)
          .height(84)
          .borderRadius(21)

        Text(this.author.desc)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)

        Row {

          Text(this.author.albums + "個Ep")
            .fontSize(15)
            .opacity(0.4)

          Text("|")
            .fontSize(15)
            .opacity(0.2)
            .padding( left: 5, right: 5 )

          Text(this.author.name)
            .fontSize(15)
            .fontColor('#A3A1AF')

        }

        Button {

          Row {

            Image($r("app.media.follow"))
              .objectFit(ImageFit.Contain)
              .width(24)
              .height(24)
              .margin({right:10})

            Text('關注')
              .fontSize(18)
              .fontColor(Color.White)
          }
        }
        .type(ButtonType.Normal)
        .backgroundColor('#2882F1')
        .width(200)
        .height(48)
        .borderRadius(8)
      }
	.height('30%')
           
#DAYU200體驗官# 起司播客App主題

播客作品清單

清單有一個标題區:

Row {

        Text('全部EP')
          .fontSize(20)
          .fontColor('#1E3354')

      }
      .width('100%')
      .padding({left:20})
           
#DAYU200體驗官# 起司播客App主題

單個播客作品項目,由作品封面、作品名、時長和作品數,組合在一個Column中:

Row {

              Image(item.img)
                .objectFit(ImageFit.Cover)
                .width(56).height(56)
                .borderRadius(20)

              Column {

                Blank()

                Row {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding( {left: 10, right: 10} )

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)
                }
                .width('90%')

                Row {

                  Text(item.duration)
                    .fontSize(16)
                    .opacity(0.4)
                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding( {left: 5, right: 5 })

                  Text(item.eps + "個Ep")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

                Blank()

              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin( {left: 20, top: 10, bottom: 10, right: 20} )
            .padding(10)
            .width('90%')
            .height(72)
           

對于播客作品資料,使用List和ForEach循環渲染,加上使用者互動跳轉到播放頁:

List {

        ForEach(this.albums, item =>{

          ListItem {

            Row {

              Image(item.img)
                .objectFit(ImageFit.Cover)
                .width(56).height(56)
                .borderRadius(20)

              Column {

                Blank()

                Row {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding( {left: 10, right: 10} )

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

                Row {

                  Text(item.duration)
                    .fontSize(16)
                    .opacity(0.4)

                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding( left: 5, right: 5 )

                  Text(item.eps + "個Ep")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

                Blank()

              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin( {left: 20, top: 10, bottom: 10, right: 20} )
            .padding(10)
            .width('90%')
            .height(72)
            .onClick(() =>{
              router.push(
                uri: 'pages/detail',
                params: {
                  place: item
                }
              )
            })

          }
        })
      }
      .width('100%')
      .height('55%')
           
#DAYU200體驗官# 起司播客App主題

完整頁面代碼和預覽效果

将以上各個子元件和資料組合起來,channel.ets源檔案整個頁面的代碼:

import router from '@system.router';

@Entry
@Component
struct Channel {
  author = {
    desc: '聽我用音樂娓娓道來',
    albums: 256,
    name: '奧珍妮博士',
    avatar: '/pages/img/uper_avatar1.png'
  }
  albums = [
    {
      title: '工作和生活之間',
      img: '/pages/img/ep1.png',
      duration: '56:38',
      eps: '56',
    },
    {
      title: '前進的力量',
      img: '/pages/img/ep2.png',
      duration: '28:01',
      eps: '35',
    }, {
      title: '讓我驚喜的小猴',
      img: '/pages/img/ep3.png',
      duration: '1:40:20',
      eps: '42',
    }, {
      title: '我的愛情被疫情阻隔',
      img: '/pages/img/ep4.png',
      duration: '1:05:13',
      eps: '51',
    }, {
      title: '你為什麼要振作起來?',
      img: '/pages/img/ep5.png',
      duration: '45:28',
      eps: '77',
    },
  ]

  build() {
    Column() {
      Row() {
        Image($r("app.media.back"))
          .objectFit(ImageFit.Contain)
          .width(20).height(20)
          .onClick(()=>{
            router.back()
          })
        Blank()
        Text('播客')
          .fontSize(20)

        Blank()
      }.width('100%')
      .padding({ left: 20, top: 20, bottom: 10, right: 20 })

      Column({space:15}) {
        Image(this.author.avatar)
          .objectFit(ImageFit.Contain)
          .width(84).height(84).borderRadius(21)

        Text(this.author.desc)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)

        Row() {
          Text(this.author.albums + "個Ep").fontSize(15).opacity(0.4)

          Text("|").fontSize(15).opacity(0.2).padding({ left: 5, right: 5 })
          Text(this.author.name)
            .fontSize(15)
            .fontColor('#A3A1AF')
        }

        Button(){
          Row(){

            Image($r("app.media.follow"))
              .objectFit(ImageFit.Contain)
              .width(24).height(24).margin({right:10})
            Text('關注').fontSize(18).fontColor(Color.White)
          }
        }.type(ButtonType.Normal)
        .backgroundColor('#2882F1')
        .width(200).height(48).borderRadius(8)
      }.height('30%')


      Row() {
        Text('全部EP')
          .fontSize(20)
          .fontColor('#1E3354')
      }
      .width('100%')
      .padding({left:20})

      List() {
        ForEach(this.albums, item => {
          ListItem() {
            Row() {
              Image(item.img)
                .objectFit(ImageFit.Cover)
                .width(56).height(56)
                .borderRadius(20)

              Column() {
                Blank()
                Row() {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding({ left: 10, right: 10 })
                  Text(item.artist).fontSize(15).opacity(0.4)
                }
                .width('90%')

                Row() {
                  Text(item.duration)
                    .fontSize(16)
                    .opacity(0.4)
                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding({ left: 5, right: 5 })
                  Text(item.eps + "個Ep").fontSize(15).opacity(0.4)
                }
                .width('90%')

                Blank()
              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin({ left: 20, top: 10, bottom: 10, right: 20 })
            .padding(10)
            .width('90%')
            .height(72)
            .onClick(() => {
              router.push({
                uri: 'pages/detail',
                params: {
                  place: item
                }
              })
            })

          }
        })
      }
      .width('100%')
      .height('55%')
    }
    .width('100%')
  }
}
           
#DAYU200體驗官# 起司播客App主題

播放頁

播放頁用于播放上一頁的頻道頁的播客作品,包含導航欄、作品大圖、作品名和作者、播放控制按鈕。在pages目錄下建立源檔案detail.ets。

狀态變量

播放的作品來自上一頁,這裡使用固定的資料。播放狀态是使用者可控的,使用@State變量,定義如下:

@State playing: boolean = false
  var music = {
    img: 'pages/img/ep1.png',
    author: '奧珍妮博士',
    title: '工作和生活之間',
    duration: '56:38',
  }
           

導航欄

導航欄左側用于傳回上一頁,右側可以将作品添加到播放清單:

Row {

        Image($r("app.media.back"))
          .objectFit(ImageFit.Contain)
          .width(20).height(20)
          .onClick({()=>
            router.back()
          })

        Blank()

        Text(' ')

          .fontSize(20)

        Blank()

        Image($r("app.media.playlist"))
          .objectFit(ImageFit.Contain)
          .width(20)
          .height(20)

      }
      .width('100%')
      .padding(30)
           
#DAYU200體驗官# 起司播客App主題

作品大圖

作品大圖帶有圓角和陰影,将Image放入Column中再修飾屬性即可:

Column {

        Image(this.music.img)
          .objectFit(ImageFit.Cover)
          .width(279)
          .height(326)
          .borderRadius(16)

      }
      .borderRadius(16)
      .shadow({radius: 50, color: '#cfcfcf',
        offsetX:5, offsetY: 15})
      .margin(30)
           
#DAYU200體驗官# 起司播客App主題

作品名和作者

作品名和作者按列布局,其中作者的文字略帶不透明效果:

Column {

        Text(this.music.title)
          .fontSize(20)

        Text(this.music.author)
          .fontSize(15)
          .opacity(0.4)

      }
      .padding(30)
           
#DAYU200體驗官# 起司播客App主題

播放控制按鈕

播放按鈕可以根據作品的播放狀态來切換圖示,使用者點選按鈕可以在播放或暫停兩種狀态進行切換:

Row {

        Image(this.playing ? $r("app.media.pause") : $r("app.media.play"))
          .objectFit(ImageFit.Cover)
          .width(64)
          .height(64)
          .onClick(()=>{
          	this.playing = !this.playing
          })
      }
	.padding(30)
           
#DAYU200體驗官# 起司播客App主題
#DAYU200體驗官# 起司播客App主題

完整頁面源碼和預覽效果

将以上子元件和資料組合起來,detail.ets源檔案整個頁面的代碼:

import router from '@system.router';

@Entry
@Component
struct Detail {
  @State playing: boolean = false
  music = {
    img: 'pages/img/ep1.png',
    author: '奧珍妮博士',
    title: '工作和生活之間',
    duration: '56:38',
  }

  build() {
    Column() {
      Row() {
        Image($r("app.media.back"))
          .objectFit(ImageFit.Contain)
          .width(20).height(20)
          .onClick(()=>{
            router.back()
          })
        Blank()
        Text(' ')
          .fontSize(20)
        Blank()
        Image($r("app.media.playlist"))
          .objectFit(ImageFit.Contain)
          .width(20).height(20)
      }.width('100%')
      .padding(30)

      Column() {
        Image(this.music.img)
          .objectFit(ImageFit.Cover)
          .width(279).height(326)
          .borderRadius(16)
      }
      .borderRadius(16)
      .shadow({radius: 50, color: '#cfcfcf',
        offsetX:5, offsetY: 15})
      .margin(30)

      Column() {
        Text(this.music.title)
          .fontSize(20)
        Text(this.music.author)
          .fontSize(15).opacity(0.4)
      }.padding(30)

      Row() {
        Image(this.playing ? $r("app.media.pause") : $r("app.media.play"))
          .objectFit(ImageFit.Cover)
          .width(64).height(64)
        .onClick(()=>{
          this.playing = !this.playing
        })
      }.padding(30)


    }
    .width('100%')
    .height('100%')
  }
}
           
#DAYU200體驗官# 起司播客App主題

總結

Dayu200不僅适合裝置開發,更适合App開發,配合最新的DevEco Studio 3.0,即使您手頭沒有裝置,也可以進行相對完善的UI開發大部分工作。

起司播客源碼(https://ost.51cto.com/resource/2158)

繼續閱讀