HarmonyOS开发(六):构建简单页面

1、Column&Row组件

1.1、概述

一个页面由很多组件组成,如果需要把这些组件组织起来布局好,需要借助容器组件来实现。

容器组件是一种特殊的组件,它可以包含其他组件,而且按照一定的规律布局,一个容器组件中可以放置基础组件,也可以放置容器组件,通过多层布局的嵌套,可以布局出更丰富的页面。

1.2、组件介绍

1.2.1、布局容器概念

线性布局容器表示按照垂直方向或水平方向排列子组件的容器,ArkTS中使用Column和Row来实现线性布局

Column:垂直方向的布局容器

Row:水平方向的布局容器

1.2.2、主轴与交叉轴

主轴与交叉轴是默认存在的两个轴,它们是相互垂直的。在不同的容器中主轴的方向不一样。

主轴

在Column容器中按从上到下垂直方向布局,主轴是垂直方向

在Row容器中按从左到右水平方向布局,主轴是水平方向

交叉轴

与主轴垂直相交的轴

组件在主轴与交叉轴上的排列方式

Column和Row容器有丙个属性justifyContent,alignItems

justifyContent:在主轴上的对齐方式

其设置的参数是FlexAlign,它有如下几个可选值

  • Start:元素在主轴方向首端对齐,第一个元素与行首对齐,同时后续的元素与前一个对齐。
  • Cener:元素在主轴方向中心对齐,第一个元素与行首的距离以及最后一个元素与行尾距离相同
  • End:元素在主轴方向尾部对齐,最后一个元素与行尾对齐,其他元素与后一个对齐
  • SpaceBetween:元素在主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
  • SpaceAround:元素在主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素到行首的距离和最后一个元素到行尾的距离是相邻元素之间距离的一半
  • SpaceEvenly:元素在主轴方向等间距布局,无论是相邻元素还是边界元素到容器的间距都一样

alignItem:在交叉轴上的对齐方式

Column容器设置的参数类型HorizontalAlign,它有如上几种可选值

  • Start:设置子组件在水平方向上按照起始端对齐
  • Center:默认值,设置子组件在水平方向上居中对齐
  • End:设置子组件在水平方向上按照末端对齐

Row容器设置的参数类型是VerticalAlign,它有如下几种可选值

  • Top:设置子组件在垂直方向上居顶部对齐
  • Center:默认值,设置子组件在竖直方向上居中对齐
  • Bottom:设置子组件在竖直方向上居底部对齐

1.3、Column&Row容器的接口介绍

容器组件接口
ColumnColumn(value?:{space?:string | number})
RowRow(value?:{space?:string | number})

接口中有一个可选参数space表示子组件在主轴上的间距

实现如下页面:

@Entry
@Component
struct Login {


  build() {
    Column(){
      // 图片
      Image($r('app.media.icon'))
        .width(60)
        .height(60)
        .margin({bottom:-45})
      // appname
      Text($r('app.string.app_name'))
      // 提示语
      Text('登录账号获取更多服务')
        .fontColor(Color.Gray)
      // 账号,密码输入区
      TextInput({placeholder: '请输入账号'})
        .width('80%')
      TextInput({placeholder: '请输入密码'})
        .width('80%')
        .type(InputType.Password)
      // 两个链接信息放在一行上
      Row(){
        Text('短信验证登录')
          .fontColor(Color.Blue)
        Text('忘记密码')
          .fontColor(Color.Blue)
      }
      .width('80%')
      .justifyContent(FlexAlign.SpaceBetween)
      // 登录按钮
      Button($r('app.string.login_text'),{type: ButtonType.Capsule, stateEffect: true})
        .width('70%')
      // 注册账号
      Text('注册账号')
        .fontColor(Color.Blue)
      Text('其它方式登录')
        .fontColor(Color.Gray)
      .margin({bottom:-45})
      // 多种方式登录放在一行
      Row(){
        Button('方式一',{type: ButtonType.Circle,stateEffect: true})
          .width('20%')
          .fontColor(Color.White)
          .backgroundColor(Color.Gray)
        Button('方式二',{type: ButtonType.Circle,stateEffect: true})
          .width('20%')
          .fontColor(Color.White)
          .backgroundColor(Color.Gray)
        Button('方式三',{type: ButtonType.Circle,stateEffect: true})
          .width('20%')
          .fontColor(Color.White)
          .backgroundColor(Color.Gray)
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)
    }
    .height('100%')
    .justifyContent(FlexAlign.SpaceEvenly)
    .alignItems(HorizontalAlign.Center) // 默认就是这种对齐方式可以不用配置
  }
}

2、List&Grid组件

2.1、组件介绍

在手机应用中通常会看到一些数据列表,组成这些列表的常用布局就是使用网络布局和列表布局。

2.2、List组件

它是最常用的滚动类容器组件,一般和子组件ListItem一起使用,List列表中的每一个列表项则对应一个ListItem组件。

2.2.1、使用ForEach渲染列表

列表往往由多个列表项组成,在List组件中使用多个ListItem组件来构建列表,但这样操作会导致代码冗余,此时可以使用循环渲染(ForEach)遍历数组的方式来构建。

@Entry
@Component
struct ListTest {

  private arr: number[] = [0,1,2,3,4,5,6,7,8,9]

  build() {
    Column() {
      List({space: 10}){
        ForEach(this.arr, (item: number) => {
          ListItem(){
            Text(`${item}`)
              .width('100%')
              .height(100)
              .fontSize(20)
              .fontColor(Color.White)
              .textAlign(TextAlign.Center)
              .borderRadius(10)
              .backgroundColor(0x007DFF)
          }
        }, item => item)
      }
    }
    .padding(12)
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

List组件子组件ListItem之间默认是不会有分隔线的,如果需要在各子组件之间设置分隔线需要在List组件上使用divider属性,属性包含以下四个参数

  • strokeWidth:分割线的线宽
  • color:分割线的颜色
  • startMargin:分割线距离列表侧边起始端的距离
  • endMargin:分割线距离列表侧边结束端的距离

注意:设置这个之后会在各个ListItem组件之间加上一个分隔线,最后一个ListItem后不会加。

2.2.2、List列表滚动事件监听

List组件提供了一系列事件方法用来监听列表的滚动,可根据需求监听这些事件来做一些操作

  • onScroll:列表滑动时触发,返回值scrollOffset为滑动偏移量,scrollState为当前滑动状态
  • onScrollIndex:列表滑动时触发,返回值分别为滑动起始位置索引值与滑动结束位置索引值
  • onReachStart:列表到达起始位置时触发
  • onReachEnd:列表到达结束位置时触发
  • onScrollStop:列表滑动停止时触发
@Entry
@Component
struct ListTest {

  private arr: number[] = [0,1,2,3,4,5,6,7,8,9];
  // 滑动起始索引与滑动结束过引
  @State firstIndex: number = -1;
  @State lastIndex: number = -1;
  // 滑动偏移量与状态
  @State scrollOffset: number = -1;
  @State scrollState: ScrollState = undefined;

  build() {
    Column() {
      Row(){
        Text(`滑动起始索引:${this.firstIndex === -1? '' : this.firstIndex}`)
        Text(`滑动结束索引:${this.lastIndex === -1? '' : this.lastIndex}`)
      }.width('90%')
      .justifyContent(FlexAlign.SpaceBetween)

      Row(){
        Text(`滑动偏移量:${this.scrollOffset === -1? '' : this.scrollOffset}`)
        Text(`滑动状态:${this.scrollState == undefined? '' : this.scrollState.toString()}`)
      }.width('90%')
      .justifyContent(FlexAlign.SpaceBetween)

      List({space: 10}){  // 这里{space: 10}设置了每个ListItem之间的距离
        ForEach(this.arr, (item: number) => {
          ListItem(){
            Text(`${item}`)
              .width('100%')
              .height(100)
              .fontSize(20)
              .fontColor(Color.White)
              .textAlign(TextAlign.Center)
              .borderRadius(10)
              .backgroundColor(0x007DFF)
          }
        }, item => item)
      }
      .divider({strokeWidth: 2,color: Color.Gray, startMargin: 10, endMargin: 10})
      // 滑动相关事件
      .onScrollIndex((firstIndex: number, lastIndex: number) => {
        this.firstIndex = -1;
        this.lastIndex = -1;
        this.scrollOffset = -1;
        this.scrollState = undefined;
        this.firstIndex = firstIndex;
        this.lastIndex = lastIndex;
      })
      .onScroll((scrollOffset: number, scrollState: ScrollState) =>{
        this.scrollOffset = Number(scrollOffset.toFixed(2))
        this.scrollState = scrollState;
      })
      .onReachStart(() => {
        console.info('触发List组件的onReachStart事件');
      })
      .onReachEnd(() => {
        console.info('触发List组件的onReachEnd事件');
      })
      .onScrollStop(() => {
        console.info('触发List组件的onScrollStop事件');
      })
    }
    .padding(12)
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

2.2.3、设置List排列方向

在List组件中的列表默认是按垂直方向排列的。

如果我们需要改变这个默认的排列方向可以把List组件的listDirection属性设置为Axis.Horizontal

listDirection的参数类型是Axis,它支持两种选项

  • Vertical:默认值,子组件ListItem在List容器组件中呈纵向排列
  • Horizontal:子组件ListItem在List容器组件中呈横向排列

2.3、Grid组件的使用

Grid组件又称为网络容器,是一种网络列表,由“行”与“列”分隔单元格所组成。

Grid组件一般与子组件GridItem一起使用,其中的每一个条目则对应一个GridItem组件。

2.3.1、使用ForEach渲染网格

@Entry
@Component
struct GridTest {

  // 定义一个数组
  private arr: string[] = new Array(16).fill('').map((_,index) => `item_${index}`)

  build() {
    Column(){
      Grid(){
        ForEach(this.arr,(item: string) => {
          GridItem(){
            Text(item)
              .fontSize(16)
              .fontColor(Color.White)
              .backgroundColor(0x007DFF)
              .width('100%')
              .height('100%')
              .textAlign(TextAlign.Center)
          }
        }, item => item)
      }
      .columnsTemplate('1fr 1fr 1fr 1fr')
      .rowsTemplate('1fr 1fr 1fr 1fr')
      .columnsGap(10)
      .rowsGap(10)
      .height(300)
    }
    .width('100%')
    .padding(12)
  }
}

上面创建了16个GridItem列表项。

columnsTemplate的值设置为'1fr 1fr 1fr 1fr',表示网络是一个四列网络

rowsTemplate的值为’1fr 1fr 1fr 1fr‘,表示这个网格为四行网络

columnsGap设置列间距为10vp,rowsGap设置行间距为10vp

上面构建的网格布局就是一个4 * 4的网格布局,固定的是行列数,如果我们内容较多,可以通过滚动方式来显示更多内容,则需要一个可以滚动的网络布局。只需要设置rowsTemplate和columnsTemplate中的一个即可。

3、Tabs组件

3.1、概述

在我们常用的应用中,经常会有视图内容的切换需求。比如底部有一个商城、购物车两个Tab标签页面,分别点击进入商城和购物车页面。

ArkUI开发框架提供了一种页签容器组件Tabs,使用这个组件可以很容易实现内容视图的切换。

页签容器Tabs的形式多种多样,不同的页面设计页签不一样,可以把页签放在底部、顶部或者侧边

@Entry
@Component
struct TabsTest {
  private controller: TabsController = new TabsController();

  build() {
    Column() {
      Tabs({barPosition: BarPosition.Start, controller: this.controller}){
        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Green)
        }
        .tabBar('green')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Blue)
        }
        .tabBar('blue')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
        }
        .tabBar('yellow')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Pink)
        }
        .tabBar('pink')  // 标签名称
      }
      .barWidth('100%') // TabBar宽度
      .barHeight(60)  // TabBar高度
      .width('100%')  // Tabs组件宽度
      .height('100%') // Tabs组件的高度
      .backgroundColor(0xF5F5F5)  // Tabs组件背景颜色
    }
    // .width('100%')
  }
}

Tabs组件包含多个TabContent,通过TabContent的tabBar属性设置TabBar的显示内容。

Tabs组件中使用width和height设置Tabs组件的宽高,而barWidth和barHeight设置TabBar的宽度和高度。

3.2、设置TabBar布局模式

Tabs默认是Fixed的,所以默认情况下Tabs页签是不可滑动的。在这种模式下如果页签比较多则可能会导致页签显示不全。如果把布局模式设置为Scrollable则可以实现页签的滚动。

Tabs的布局模式有如现两种

  • BarMode.Fixed:默认,所有TabBar平均分配barWidth宽度(纵向时平均分配barHeight高度),页签不可滚动
  • BarMode.Scrollable:每一个TabBar均使用实际布局宽度,超过总长度(横向Tabs的barWidth,纵向Tabs的barHeight)后可滑动
@Entry
@Component
struct TabsTest {
  private controller: TabsController = new TabsController();

  build() {
    Column() {
      Tabs({barPosition: BarPosition.Start, controller: this.controller}){
        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Green)
        }
        .tabBar('green')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Blue)
        }
        .tabBar('blue')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
        }
        .tabBar('yellow')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Pink)
        }
        .tabBar('pink')  // 标签名称
      }
      .barMode(BarMode.Scrollable)  // 设置布局模式,这里默认是BarMode.Fixed
      .barWidth('100%') // TabBar宽度
      .barHeight(60)  // TabBar高度
      .width('100%')  // Tabs组件宽度
      .height('100%') // Tabs组件的高度
      .backgroundColor(0xF5F5F5)  // Tabs组件背景颜色
    }
    // .width('100%')
  }
}

3.3、设置TabBar位置和排列方向

Tabs组件页签默认显示在顶部,可以使用Tabs组件接口中参数barPosititon设置标签位置。

页签显示位置还与vertical属性相关联,vertical属性用于设置页面的排列方向,当vertical的属性值为false(默认值)时页签横向排列,当为true时表示纵向排列

@Entry
@Component
struct TabsTest {
  private controller: TabsController = new TabsController();

  build() {
    Column() {
      Tabs({barPosition: /*BarPosition.Start*/ BarPosition.End, controller: this.controller}){
        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Green)
        }
        .tabBar('green')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Blue)
        }
        .tabBar('blue')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Yellow)
        }
        .tabBar('yellow')  // 标签名称

        TabContent(){
          // 内容部分
          Column().width('100%').height('100%').backgroundColor(Color.Pink)
        }
        .tabBar('pink')  // 标签名称
      }
      .vertical(true)
      // .barMode(BarMode.Scrollable)  // 设置布局模式,这里默认是BarMode.Fixed
      .barWidth(100) // TabBar宽度
      .barHeight(200)  // TabBar高度
      .width('100%')  // Tabs组件宽度
      .height('100%') // Tabs组件的高度
      .backgroundColor(0xF5F5F5)  // Tabs组件背景颜色
    }
    // .width('100%')
  }
}

Tabs组件接口中参数barPosititon加上他的属性vertical组合则可以让Tabs定位到顶部 底部 左侧 右侧。

@Entry
@Component
struct TabsTest2 {

  @State currentIndex: number = 0;
  private tabsController: TabsController = new TabsController();

  @Builder TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource){
    Column() {
      Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
        .size({width: 32,height:32})
      Text(title)
        .fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
    }
    .width('100%')
    .height(50)
    .justifyContent(FlexAlign.Center)
    .onClick(() => {
      this.currentIndex = targetIndex;
      this.tabsController.changeIndex(this.currentIndex);
    })
  }

  build() {
    Tabs({barPosition:BarPosition.End, controller: this.tabsController}){
      TabContent(){
        Column().width('100%').height('100%').backgroundColor('#00CB87')
      }
      .tabBar(this.TabBuilder('首页',0,$r('app.media.index2'),$r('app.media.index1')))

      TabContent(){
        Column().width('100%').height('100%').backgroundColor('#007DFF')
      }
      .tabBar(this.TabBuilder('我的',1,$r('app.media.my2'),$r('app.media.my1')))
    }
    .barWidth('100%')
    .barHeight(50)
    .onChange((index: number) => {
      this.currentIndex = index;
    })
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/181653.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

idea 2023 安装配置 Gradle8.4

官网:https://gradle.org 下载 Gradle8.4 https://gradle.org/releases/ 解压到本地,到 gradle-8.4\init.d 目录下新建文件:init.gradle 这里有个坑,编译报http协议安全的问题,解决办法,加入&#xff1…

便携式心电图机方案_基于MT6735平台的手持心电图机

便携式心电图机具备体积小、易携带、兼容12导模式的特点,通过工频滤波、基线滤波和肌电滤波等处理,能够获得更精准的心电图谱。该设备可以与医院信息系统(HIS)相连接,实现患者信息的共享。采集的心电数据可以通过无线方式发送到心电判读平台&…

2024年的云趋势:云计算的前景如何?

本文讨论了2024年云计算的发展趋势。 适应复杂的生态系统、提供实时功能、优先考虑安全性和确保可持续性的需求正在引领云计算之船。多样化的工作负载允许探索通用的公共云基础设施范例之外的选项。由于需要降低成本、提高灵活性和降低风险,混合云和多云系统越来越受…

vue项目引入中国地图

先安装有china.js的版本 npm install echarts4.8 --save //以前的版本有china.js <template><div class"mapMain"><div id"map" style"width: 30vw; height: 30vw;" /></div> </template><script>//引入文…

openGauss学习笔记-130 openGauss 数据库管理-参数设置-重设参数

文章目录 openGauss学习笔记-130 openGauss 数据库管理-参数设置-重设参数130.1 背景信息130.2 GUC参数设置130.3 操作步骤130.4 示例 openGauss学习笔记-130 openGauss 数据库管理-参数设置-重设参数 130.1 背景信息 openGauss提供了多种修改GUC参数的方法&#xff0c;用户可…

js实现获取原生form表单的数据序列化表单以及将数组转化为一个对象obj,将数组中的内容作为对象的key转化为对象,对应的值转换为对象对应的值

1.需求场景 哈喽 大家好啊&#xff0c;今天遇到一个场景&#xff0c; js实现获取原生form表单的数据序列化表单以及将数组转化为一个对象obj&#xff0c;将数组中的内容作为对象的key转化为对象&#xff0c;对应的值转换为对象对应的值 数组对象中某个属性的值&#xff0c;转…

行情分析 - - 加密货币市场大盘走势(11.23)

大饼昨日又开始了回调&#xff0c;因为FTF消息&#xff0c;而实际还是要下跌的&#xff0c;耐心等待即可。 空单策略&#xff1a;入场37300 止盈34000-33000 止损39000 以太昨日上涨也很激励&#xff0c;目前上涨打了止损&#xff0c;现在入场是好的机会&#xff0c;等待即可。…

经营现金流转正,宝尊电商解构内容电商3.0时代长期价值

在过去的“黄金十年”里&#xff0c;货架电商、直播电商鱼贯而出&#xff0c;接力式地推动品牌增长。彼时&#xff0c;价格换市场、“以快打快”的打法足以满足品牌发展所需。 然而&#xff0c;随着流量红利消退&#xff0c;消费者愈发理性&#xff0c;品牌增长集体“失速”。…

《图解Java数据结构与算法:微课视频版》简介

本书系统、全面地介绍数据结构的基础理论与算法设计&#xff0c;精选数据结构考研习题和各类典型例题进行讲解&#xff0c;案例和课后习题丰富&#xff0c;突出对数据结构算法实践能力的培养。本书算法均采用Java语言实现&#xff0c;示例代码可直接上机运行。 本书配套资源丰…

自由飞翔之小鸟

一、创建文件、包、类、插入图片文件 二、app包 1、Gameapp类&#xff08;运行游戏&#xff09; package app;import main.GameFrame;public class Gameapp {public static void main(String[] args) {//游戏的入口new GameFrame();} } 三、main包 1、Barrier&#xff08;障…

Spring第一课,了解IDEA里面的文件,回顾Cookie和Session,获取Session,Cookie,Header的方式

目录 IDEA第一课&#xff08;熟悉里面内容&#xff09; 建立连接 -RequestMapping 路由映射 请求 1.传递单个参数​编辑 2.多个参数​编辑 3.传递数组 4.传递一个集合&#xff0c;但是这里我们传递的时候发生了500的错误 简单介绍JSON 回顾Cookie和S…

Docker 中OpenResty下载与使用

1Panel安装OpenResty 查看到就说明安装成功 部署项目 在http中添加&#xff1a; server { listen 8001; //端口号 server_name localhost; location / { root /admin; //项目路径 index index.html index.htm; …

python appiumn 自动化测试 入门

资源下载 链接&#xff1a;https://pan.baidu.com/s/1zl1yXYna73RAL-V0PQ9xHA 提取码&#xff1a;syjg 安装JDK 不详细说了 配置Android SDK 新建 ANDROID_HOME配置对应的SDK路径 新增PATH 安装python库 pip install Appium-Python-Client报如图错误的话可以使用 pytho…

【SpringCloud】为什么选择微服务?

一般的平台会遇到的问题&#xff1a; 服务配置复杂。基础服务多&#xff0c;服务的资源配置复杂&#xff0c;传统方式管理服务复杂 服务之间调用复杂。检索服务、用户中心服务等&#xff0c;服务之间的调用复杂&#xff0c;依赖多 服务监控难度大。服务比较多&#xff0c;…

前端实现菜单快速检索的功能

前端CSS <style type"text/css">.btn-box {color: #fff;width: auto;border-radius: 25px;min-width: 40px;height: 40px;margin: 9px;line-height: 40px;display: inline-block;position: relative;overflow: hidden;background-image: linear-gradient(315de…

Spark-06:共享变量

目录 1.广播变量&#xff08;broadcast variables&#xff09; 2.累加器&#xff08;accumulators&#xff09; 在分布式计算中&#xff0c;当在集群的多个节点上并行运行函数时&#xff0c;默认情况下&#xff0c;每个任务都会获得函数中使用到的变量的一个副本。如果变量很…

为何越来越多的程序员纷纷转行网络安全?

目前&#xff0c;我国IT行业的人才结构不断升级&#xff0c;公司对程序员的要求越来越高&#xff0c;出现了大量的裁员现象&#xff0c;导致很多的程序员纷纷想转行的想法。 可能对于早期的程序员而言&#xff0c;学好编程语言就能找到比较好的工作。而现在伴随着互联网的不断发…

vue - - - - - vue-qr插件生成二维码

vue-qr插件生成二维码 1. 安装插件2. 组件使用示例图&#xff1a;扫码结果 1. 安装插件 【vue-qr 官网地址】 npm install vue-qr --save // or yarn add vue-qr --save2. 组件使用 <template><vue-qr :logo-src"logoSrc":size"237":margin&qu…

03-详细介绍Stream及其常用API

Stream API Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,可以极大地提高程序员生产力&#xff0c;让程序员写出高效、简洁的代码 实际开发中项目中多数数据源都是来自MySQL、Oracle等关系型数据库,还有部分来自MongDB、Redis等非关系型数据库 从关系型…

Exception in thread “消费者“ java.lang.IllegalMonitorStateException

这两天学习生产者消费者模型的时候&#xff0c;使用Java线程来实现&#xff0c;出现了一个问题“Exception in thread "消费者" java.lang.IllegalMonitorStateException”&#xff0c;并且&#xff0c;线程不结束。报错图片如下&#xff1a; 那我们怎么解决呢&…