玩安卓-鸿蒙版 二 首页横幅、搜索、跳转链接功能

本篇涉及组件TextInput、Image、Menu、Swiper、Web、自定义组件、路由跳转传参、POST请求

1.首页横幅

Swiper组件内部需要放其他内容组件,且需要设置宽高100%。

Image显示网络图片,直接设置图片链接即可。

      Swiper(){
        ForEach(this.bannerData,(data:Datum,index:number)=>{
          Image(data.imagePath)
            .width('100%')
            .height('100%')
            .onClick(()=>{
              console.log("Swiper onClick link "+data.url)
              router.pushUrl({
                url:"pages/WebPage",
                params:{data:data.url}
              })
            })
        })
      }
      .width('100%')
      .height(150)

2.搜索框

placeholder是提示词,text就是输入框已存在的内容,通过onChange监听输入内容。

然后同步bindMenu添加菜单,为了做搜索热词功能

        TextInput({placeholder:"输入搜索关键词",text:this.searchContent})
          .width(270)
          .height(40)
          .margin({top:10,bottom:10})
          .backgroundColor(Color.White)
          .bindMenu(
            this.searchKey()
          )
          .onChange((value: string) => {
            this.searchContent = value;
          })

  @Builder
  searchKey(){
    Menu(){
      ForEach(this.searchKeyData,(data:SearchKeyDataBean,index:number)=>{
        MenuItem({"content":data.name})
          .onClick(()=>{
            this.searchContent = data.name
          })
      })
    }
  }

输入完后,点击按钮确认搜索,跳转到搜索页。

        Button("搜索")
          .margin({left:10,right:10})
          .onClick(()=>{
            console.log("searchContent "+this.searchContent)
            router.pushUrl({
              url:"pages/SearchListPage",
              params:{data:this.searchContent}
            })
          })

3.搜索页

搜索页内容就是一个列表,和首页一样,区别是获取搜索列表是通过POST请求,需要在method指定post请求,且在extraData写入post传参

    let httpRequest = http.createHttp();
    httpRequest.request("https://www.wanandroid.com/article/query/"+this.page+"/json", {method: http.RequestMethod.POST,extraData: "k="+this.searchKey,}, (err, data) => {
      if (!err) {
        console.log("SearchListPage data.result:"+data.result.toString())
        let welcome: Welcome = JSON.parse(data.result.toString());
        if(welcome && welcome.data && welcome.data.datas && welcome.data.datas.length > 0){
          this.dataList = this.dataList.concat(welcome.data.datas)
          this.page++;
        }
      } else {
      }
      this.state = RequestState.END;
    })

4.列表和横幅都能点击跳转到网页

使用Web组件,传网址给src,controller创建一个默认的即可

      Web({
        src: this.url,
        controller: this.webviewController
      })

然后写一个标题栏能返回,能标题栏在搜索页也要使用,所以做了一个自定义组件,只用@Component描述

@Component
export struct TitleBar {
  private title = '';

  build() {
    Column() {
      Row() {
        Image($r('app.media.ic_back'))
          .width(20)
          .height(20)
          .margin({ left: 26 })
          .objectFit(ImageFit.Contain)
          .onClick(() => {
            router.back()
          })
          .backgroundColor(Color.Blue)
          .id('backBtn')
        Text(this.title)
          .fontSize(20)
          .layoutWeight(1)
          .margin({ left: 16 })
          .align(Alignment.Start)
        Blank()
      }
      .height(56)
      .width('100%')
    }
  }
}

5.完整代码如下:

import { http } from "@kit.NetworkKit";
import { DataElement, Welcome } from "../bean/HomePageBean";
import { RequestState } from "../data/RequestState";
import { router } from "@kit.ArkUI";
import { Datum, HomeBannerBean } from "../bean/HomeBannerBean";
import { SearchKeyBean, SearchKeyDataBean } from "../bean/SearchKeyBean";


@Extend(Column)
function ColumnStyle(){
  .width("100%")
  .borderRadius(24)
  .backgroundColor(Color.White)
  .padding({left:12,right:12,bottom:4,top:4})
}


@Component
export struct HomePage{

  @State dataList:DataElement[] = [];
  @State bannerData:Datum[] = [];
  @State searchKeyData:SearchKeyDataBean[] = [];
  @State searchContent:string = ''

  aboutToAppear(): void {
    this.getHomeData();
    this.getHomeBanner();
    this.getSearchKey();
  }

  private page:number = 0;
  private state:RequestState = RequestState.INIT;
  private lastIndex = 0;
  getHomeData(){
    this.state = RequestState.REQUESTING;
    let httpRequest = http.createHttp();
    httpRequest.request("https://www.wanandroid.com/article/list/"+this.page+"/json", {}, (err, data) => {
      if (!err) {
        console.log("HomePage data.result:"+data.result.toString())
        let welcome: Welcome = JSON.parse(data.result.toString());
        if(welcome && welcome.data && welcome.data.datas && welcome.data.datas.length > 0){
          this.dataList = this.dataList.concat(welcome.data.datas)
          this.page++;
        }
      } else {
      }
      this.state = RequestState.END;
    })
  }

  getHomeBanner(){
    let httpRequest = http.createHttp();
    httpRequest.request("https://www.wanandroid.com/banner/json", {}, (err, data) => {
      if (!err) {
        console.log("HomePage data.result:"+data.result.toString())
        let welcome: HomeBannerBean = JSON.parse(data.result.toString());
        this.bannerData = welcome.data;
      } else {
      }
      this.state = RequestState.END;
    })
  }


  getSearchKey(){
    let httpRequest = http.createHttp();
    httpRequest.request("https://www.wanandroid.com/hotkey/json", {}, (err, data) => {
      if (!err) {
        console.log("HomePage data.result:"+data.result.toString())
        let welcome: SearchKeyBean = JSON.parse(data.result.toString());
        this.searchKeyData = welcome.data;
      } else {
      }
      this.state = RequestState.END;
    })
  }

  @Builder
  searchKey(){
    Menu(){
      ForEach(this.searchKeyData,(data:SearchKeyDataBean,index:number)=>{
        MenuItem({"content":data.name})
          .onClick(()=>{
            this.searchContent = data.name
          })
      })
    }
  }

  build() {
    Column(){
      Row(){
        TextInput({placeholder:"输入搜索关键词",text:this.searchContent})
          .width(270)
          .height(40)
          .margin({top:10,bottom:10})
          .backgroundColor(Color.White)
          .bindMenu(
            this.searchKey()
          )
          .onChange((value: string) => {
            this.searchContent = value;
          })
        Button("搜索")
          .margin({left:10,right:10})
          .onClick(()=>{
            console.log("searchContent "+this.searchContent)
            router.pushUrl({
              url:"pages/SearchListPage",
              params:{data:this.searchContent}
            })
          })
      }

      Swiper(){
        ForEach(this.bannerData,(data:Datum,index:number)=>{
          Image(data.imagePath)
            .width('100%')
            .height('100%')
            .onClick(()=>{
              console.log("Swiper onClick link "+data.url)
              router.pushUrl({
                url:"pages/WebPage",
                params:{data:data.url}
              })
            })
        })
      }
      .width('100%')
      .height(150)
      List(){
        if(this.dataList){
          ForEach(this.dataList,(data:DataElement,index:number)=>{
            ListItem(){
              Column(){
                Text(data.title)
                  .height(48)
                  .fontSize(14)
                  .width('100%')
                  .textAlign(TextAlign.Start)
                  .fontColor($r('app.color.font_color_shallow'))
                  .padding({bottom:4,top:4,left:24})
                  .maxLines(1)
                Row(){
                  Text("分享人:"+data.author+"  分类:"+data.chapterName+"/"+data.superChapterName+"  时间:"+data.publishTime)
                    .maxLines(1)
                    .fontSize(8)
                    .padding({bottom:4,left:24})
                }
                .width('100%')
                .justifyContent(FlexAlign.Start)
              }
              .ColumnStyle()
              .margin({top:6,bottom:6})
              .onTouch((event: TouchEvent | undefined) => {
                if (event) {
                  console.log("event.type "+event.type+" event.y "+event.touches[0].y)
                  //判断加载更多,需要当前item展示到最后,并且当前请求结束,且往上拉了一段距离
                  if(event.type == TouchType.Up && event.touches[0].y < -100 && this.state == RequestState.END && this.lastIndex == this.dataList.length-1){
                    this.getHomeData();
                  }
                }
              })
            }.onClick(()=>{
              console.log("onClick link "+data.link)
              router.pushUrl({
                url:"pages/WebPage",
                params:{data:data.link}
              })
            })
          })
        }
      }
      .onScrollIndex((start:number,end:number)=>{
        //可以获取到当前屏幕第一个可见item和最后一个可见item
        console.log("onScrollIndex start "+start+" end "+end)
        this.lastIndex = end;
      })

    }
  }
}

import { http } from "@kit.NetworkKit";
import { DataElement, Welcome } from "../bean/HomePageBean";
import { RequestState } from "../data/RequestState";
import { router } from "@kit.ArkUI";
import { TitleBar } from "../view/TitleBar";


@Extend(Column)
function ColumnStyle(){
  .width("100%")
  .borderRadius(24)
  .backgroundColor(Color.White)
  .padding({left:12,right:12,bottom:4,top:4})
}


@Entry
@Component
export struct SearchListPage{

  @State dataList:DataElement[] = [];
  searchKey:string = ''

  aboutToAppear(): void {
    let params: Record<string, Object> = router.getParams() as Record<string, Object>;
    this.searchKey = params['data'] as string;
    this.getHomeData();
  }

  private page:number = 0;
  private state:RequestState = RequestState.INIT;
  private lastIndex = 0;
  getHomeData(){
    this.state = RequestState.REQUESTING;
    let httpRequest = http.createHttp();
    httpRequest.request("https://www.wanandroid.com/article/query/"+this.page+"/json", {method: http.RequestMethod.POST,extraData: "k="+this.searchKey,}, (err, data) => {
      if (!err) {
        console.log("SearchListPage data.result:"+data.result.toString())
        let welcome: Welcome = JSON.parse(data.result.toString());
        if(welcome && welcome.data && welcome.data.datas && welcome.data.datas.length > 0){
          this.dataList = this.dataList.concat(welcome.data.datas)
          this.page++;
        }
      } else {
      }
      this.state = RequestState.END;
    })
  }


  build() {
    Column(){
      TitleBar({title:this.searchKey})
      List(){
        if(this.dataList){
          ForEach(this.dataList,(data:DataElement,index:number)=>{
            ListItem(){
              Column(){
                Text(data.title)
                  .height(48)
                  .fontSize(14)
                  .width('100%')
                  .textAlign(TextAlign.Start)
                  .fontColor($r('app.color.font_color_shallow'))
                  .padding({bottom:4,top:4,left:24})
                  .maxLines(1)
                Row(){
                  Text("分享人:"+data.author+"  分类:"+data.chapterName+"/"+data.superChapterName+"  时间:"+data.publishTime)
                    .maxLines(1)
                    .fontSize(8)
                    .padding({bottom:4,left:24})
                }
                .width('100%')
                .justifyContent(FlexAlign.Start)
              }
              .ColumnStyle()
              .margin({top:6,bottom:6})
              .onTouch((event: TouchEvent | undefined) => {
                if (event) {
                  console.log("event.type "+event.type+" event.y "+event.touches[0].y)
                  //判断加载更多,需要当前item展示到最后,并且当前请求结束,且往上拉了一段距离
                  if(event.type == TouchType.Up && event.touches[0].y < -100 && this.state == RequestState.END && this.lastIndex == this.dataList.length-1){
                    this.getHomeData();
                  }
                }
              })
            }.onClick(()=>{
              console.log("onClick link "+data.link)
              router.pushUrl({
                url:"pages/WebPage",
                params:{data:data.link}
              })
            })
          })
        }
      }
      .onScrollIndex((start:number,end:number)=>{
        //可以获取到当前屏幕第一个可见item和最后一个可见item
        console.log("onScrollIndex start "+start+" end "+end)
        this.lastIndex = end;
      })

    }
    .backgroundColor($r('app.color.background_shallow_grey'))


  }
}

// renderMode.ets
import { webview } from '@kit.ArkWeb';
import { router } from '@kit.ArkUI';
import { TitleBar } from '../view/TitleBar';

@Entry
@Component
struct WebPage {
  private webviewController: WebviewController = new webview.WebviewController()
  @State url:string = ''
  aboutToAppear() {
    let params: Record<string, Object> = router.getParams() as Record<string, Object>;
    this.url = params['data'] as string;
  }
  build() {
    Column() {
      TitleBar()
      Web({
        src: this.url,
        controller: this.webviewController,
        renderMode: RenderMode.ASYNC_RENDER // 设置渲染模式
      })
        .width('100%')
        .height('100%')
    }
  }
}

/*
 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import router from '@ohos.router'

// Page title bar
@Component
export struct TitleBar {
  private title = '';

  build() {
    Column() {
      Row() {
        Image($r('app.media.ic_back'))
          .width(20)
          .height(20)
          .margin({ left: 26 })
          .objectFit(ImageFit.Contain)
          .onClick(() => {
            router.back()
          })
          .backgroundColor(Color.Blue)
          .id('backBtn')
        Text(this.title)
          .fontSize(20)
          .layoutWeight(1)
          .margin({ left: 16 })
          .align(Alignment.Start)
        Blank()
      }
      .height(56)
      .width('100%')
    }
  }
}

export interface HomeBannerBean {
  data:      Datum[];
  errorCode: number;
  errorMsg:  string;
}

export interface Datum {
  desc:      string;
  id:        number;
  imagePath: string;
  isVisible: number;
  order:     number;
  title:     string;
  type:      number;
  url:       string;
}

export interface SearchKeyBean {
  data:      SearchKeyDataBean[];
  errorCode: number;
  errorMsg:  string;
}

export interface SearchKeyDataBean {
  id:      number;
  link:    string;
  name:    string;
  order:   number;
  visible: number;
}
相关推荐
想一个不重名的名字10 分钟前
web端http请求响应头集合
前端·网络协议·http
右恩14 分钟前
【二维码美化】
服务器·前端·python·学习
蜂鸟视图fengmap1 小时前
蜂鸟云平台2024年1月重大更新:JavaScript SDK v3.1.4 & 微信小程序SDK v0.9.4 亮点解析
开发语言·前端·javascript·微信小程序·ecmascript·主题编辑器·蜂鸟视图
哟哟耶耶1 小时前
component-后端返回图片(数据)前端进行复制到剪切板
前端·javascript·vue.js
小白64021 小时前
浅谈目前我开发的前端项目用到的设计模式
前端·设计模式·状态模式
液态不合群1 小时前
大文件传输与断点续传实现(极简Demo:React+Node.js)
前端·react.js·node.js
儒道易行2 小时前
【bWAPP】XSS跨站脚本攻击实战
前端·web安全·网络安全·xss
陈大鱼头2 小时前
写在裸辞 5 个月之后的年终总结
前端·面试·github
幽兰的天空2 小时前
HTML 面试题全解析
前端·html