HarmonyOS--提升动画运行流畅度

2024-08-29 08:36

本文主要是介绍HarmonyOS--提升动画运行流畅度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、概述

        动画在应用开发中扮演着重要的角色,能够提升用户体验,传达信息,引导用户操作,提升应用品质和增加视觉吸引力。而动画的性能表现也至关重要,优化可以从属性更新和布局等几个方面考虑,尽可能减少冗余刷新。本文将介绍如下4种优化手段,通过这些优化手段的单个使用或组合使用,可以对动画帧率、应用卡顿等方面带来优化,提升性能和用户体验:

  • 使用系统提供的动画接口:系统接口经过精心设计和优化,能够在不同设备上提供流畅的动画效果,最大程度地减少丢帧率和卡顿现象。
  • 使用图形变换属性变化组件布局:通过对组件的图形变换属性进行调整,而不是直接修改组件的布局属性,可以减少不必要的布局计算和重绘操作,从而降低丢帧率,提升动画的流畅度和响应速度。
  • 参数相同时使用同一个animateTo:当多个动画的参数相同时,合并它们并使用同一个animateTo方法进行处理能够有效减少不必要的计算和渲染开销。
  • 多次animateTo时统一更新状态变量:在进行多次动画操作时,统一更新状态变量可以避免不必要的状态更新和重复渲染,从而减少性能开销。

        测试的关键指标: 

  • 丢帧率(Janky Frames):表示一个时间周期内的丢帧比率,指一个时间周期内有问题的帧比例。HarmonyOS系统要求每一帧都要在11.1ms(90Hz刷新率)内绘制完成,如果页面没有在11.1ms内完成这一帧的绘制,就会出现丢帧。部分丢帧一般用户肉眼是感知不到的,只有出现连续丢帧用户才有明显感知。
  • 最大连续丢帧数(maximum successive frame dropping count):表示从页面开始有响应变化到页面结束刷新的过程中,由于显示器画面刷新频率低于预设的画面帧率而未能正常呈现的最大连续帧数。一般而言,当连续值超过3时,用户可以明显感知到卡顿掉帧,数值越大卡顿时间越长。
  • 平均1s大卡顿次数:表示程序运行过程中平均1s出现连续丢3帧以上的卡顿的次数,数值越大用户体验感越差。

二、使用系统提供的动画接口

         实现效果:同一界面多个按钮同时缩放的场景

        

 

         1、自定义动画

        自定义动画是指通过编写自定义的动画逻辑和计算过程来实现特定的动画效果。开发人员可以根据应用的需求和设计要求,使用自定义的动画算法和逻辑来创建独特的动画效果。自定义动画的优势在于可以实现非常个性化的动画效果,并且能够完全控制动画的每一个细节,但需要开发人员具备一定的动画算法和计算能力。

// 自定义动画代码
@Entry
@Component
struct CustomAnimationExample {@State widthSize: number = 80@State heightSize: number = 40@State flag: boolean = true// 自定义动画函数computeSize() {let duration = 2000// 设定循环周期let period = 1let widthSizeEnd = 0let heightSizeEnd = 0if (this.flag) {widthSizeEnd = 50heightSizeEnd = 25} else {widthSizeEnd = 80heightSizeEnd = 40}// 计算循环次数let doTimes = duration / period// 计算每次循环变化量let deltaHeight = (heightSizeEnd - this.heightSize) / doTimeslet deltaWeight = (widthSizeEnd - this.widthSize) / doTimes// 循环,组件每个周期增加一部分宽高for (let i = 1; i <= doTimes; i++) {let t = period * (i);setTimeout(() => {this.heightSize = this.heightSize + deltaHeightthis.widthSize = this.widthSize + deltaWeight}, t)}this.flag = !this.flag}build() {Column() {Button().width(this.widthSize).height(this.heightSize)// 其他相似布局…Button('click me').onClick(() => {let delay = 500// 调用自定义函数setTimeout(() => {this.computeSize()}, delay)}).width('50%').height('15%').backgroundColor(0x317aff)}.width('100%').margin({ top: 5 })}
}

        2、系统动画接口

        系统动画接口是指通过使用系统提供的动画框架和接口来实现动画效果。在移动应用开发中,通常会使用属性动画来实现各种动画效果。通过可动画属性改变引起UI上产生的连续视觉效果,即为属性动画。属性动画是最基础易懂的动画,ArkUI提供两种属性动画接口animateTo和animation驱动组件属性按照动画曲线等动画参数进行连续的变化,产生属性动画。使用系统提供的动画接口可以简化动画的实现过程,并且能够充分利用系统优化的动画计算和渲染能力,从而提高动画的性能和流畅度。

// 系统动画
@Entry
@Component
struct PropertyAnimateToExample {@State widthSize: number = 80;@State heightSize: number = 40;@State flag: boolean = true;build() {Column() {Button().width(this.widthSize).height(this.heightSize)// 对Button组件的宽高属性进行动画配置.animation({duration: 2000, // 动画时长curve: Curve.Linear, // 动画曲线delay: 500, // 动画延迟iterations: 1, // 播放次数playMode: PlayMode.Normal // 动画模式}) // 其他相似布局…Button('click me').onClick((event?: ClickEvent | undefined) => {if (this.flag) {this.widthSize = 50this.heightSize = 25} else {this.widthSize = 80this.heightSize = 40}this.flag = !this.flag}).width('50%').height('15%').backgroundColor(0x317aff)}.width('100%').margin({ top: 5 })}
}// 或
@Entry
@Component
struct ExplicitAnimateToExample {@State widthSize: number = 80;@State heightSize: number = 40;@State flag: boolean = true;build() {Column() {Button().width(this.widthSize).height(this.heightSize)// 其他相似布局…Button('click me').onClick((event?: ClickEvent | undefined) => {// 对Button组件的宽高属性进行动画配置if (this.flag) {animateTo({duration: 2000, // 动画时长curve: Curve.Linear, // 动画曲线delay: 500, // 动画延迟iterations: 1, // 播放次数playMode: PlayMode.Normal // 动画模式}, () => {this.widthSize = 50;this.heightSize = 25;})} else {animateTo({duration: 2000, // 动画时长curve: Curve.Linear, // 动画曲线delay: 500, // 动画延迟iterations: 1, // 播放次数playMode: PlayMode.Normal // 动画模式}, () => {this.widthSize = 80;this.heightSize = 40;})}this.flag = !this.flag;}).width('50%').height('15%').backgroundColor(0x317aff)}.width('100%').margin({ top: 5 })}
}

        3、结论

        系统动画接口内部实现对开发者是透明的,已经进行了尽可能的性能优化,避免开发者使用自定义动画时产生不必要的性能劣化。自定义动画适合实现个性化的、复杂的动画效果,而系统提供的动画接口则适合实现常见的动画效果并且能够获得更好的性能表现。因此,在动画能够使用系统接口实现的情况下,开发者应尽量使用系统接口实现,保持动画的流畅性和稳定性,提升应用的性能表现。 

三、使用图形变换属性变化组件

        实现效果:针对同一界面多个图片同时缩放并位移的场景(如下图所示),分别通过改变布局属性、改变图形变换属性实现

         

        1、改动布局属性

·常见的布局属性包括位置、大小、内边距、外边距、对齐方式、权重等。当这些布局属性发生改变时,界面将重新布局以适应新的属性值。

@Entry
@Component
struct Index {@State imageWidth: number = 60;@State imageHeight: number = 60;@State xPosition: number = 0;@State yPosition: number = 0;build() {Column(){Image($r('app.media.sample')).width(this.imageWidth).height(this.imageHeight).position({x: this.xPosition, y: this.yPosition})// 其他相似布局…Button("布局属性")// 按键属性设置….onClick(() => {let doTimes = 10;// 按播放次数循环播放动画for (let i = 0; i < doTimes; i++) {// 间隔播放位置、宽高变化if (i % 2 == 0){setTimeout(() => {animateTo({ duration:1000 }, () => {this.imageWidth = 120;this.imageHeight = 120;this.xPosition = 15;this.yPosition = 15;})}, 1000 * i)} else {setTimeout(() => {animateTo({ duration: 1000 }, () => {this.imageWidth = 60;this.imageHeight = 60;this.xPosition = 0;this.yPosition = 0;})}, 1000 * i)}}})}.width('100%').margin({ top: 5 })}
}

        2、改动图形变换属性

·图形变换属性是指对组件布局结果的变换操作,如平移、旋转、缩放等操作。通过改变这些图形变换属性,可以实现对组件布局完成后,在界面上的位置和形态进行动态变换。

@Entry
@Component
struct Index {@State imageScaleX: number = 1;@State imageScaleY: number = 1;@State imageTranslateX: number = 0;@State imageTranslateY: number = 0;build() {Column() {Image($r('app.media.like')).scale({ x: this.imageScaleX, y: this.imageScaleY, centerX: 0, centerY: 0 }).translate({ x: this.imageTranslateX, y: this.imageTranslateY })// 其他相似布局…Button("图形变换属性")// 按键属性设置….onClick(() => {let doTimes = 10;// 按播放次数循环播放动画for (let i = 0; i < doTimes; i++) {if (i % 2 == 0) {setTimeout(() => {animateTo({ duration: 1000 }, () => {this.imageScaleX = 2;this.imageScaleY = 2;this.imageTranslateX = 15;this.imageTranslateY = 15;})}, 1000 * i)} else {setTimeout(() => {animateTo({ duration: 1000 }, () => {this.imageScaleX = 1;this.imageScaleY = 1;this.imageTranslateX = 0;this.imageTranslateY = 0;})}, 1000 * i)}}})}.width('100%').margin({ top: 5 })}
}

图形变换属性

布局属性

rotate

/

translate

position、offset

scale

width、height、Size

transform

/

        3、结论 

         使用图形变化属性改变图片大小和位置时,能够显著降低丢帧率和大卡顿的发生频率。界面布局是非常耗时的操作,因此频繁地改动布局属性会导致界面性能下降,出现卡顿现象,影响用户体验。因此,在动画能够使用图形变化属性实现的情况下,开发者应尽量使用图形变化属性实现,保持动画的流畅性和稳定性,提升应用的性能表现。

四、参数相同时使用同一个animateTo

        每次调用animateTo方法,都会触发一次属性变化,这意味着在每次动画执行时都需要进行动画前后的对比,以确定属性的变化情况。当多次连续调用animateTo时,会增加额外的布局计算和绘制开销,从而降低性能表现。特别是当这些animateTo操作针对同一个组件的属性时,会导致该组件更新的次数增加,进一步影响性能。

        在实际开发中,如果多个属性需要以相同的动画参数进行变化,推荐将它们放到同一个动画闭包中执行。通过将多个属性的动画操作合并到同一个动画闭包中,可以减少对组件的多次更新,避免重复的布局计算和绘制操作,提升动画效果的性能。

        除了性能方面的优势,将多个属性的动画操作合并到同一个动画闭包中还有助于提高代码的可读性和维护性。通过集中管理相关联的属性变化,可以使代码结构更加清晰,便于后续的维护和修改。

        实现效果:针对多个相同组件同时修改多个属性的场景

        

        1、多个animateTo闭包

@Entry
@Component
struct MyComponent {@State w:number = 150@State h:number = 2@State brightNum:number = 1.5@State color:Color = Color.Red// 动画闭包1,设置宽度变化func1() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.w = (this.w === 80 ? 150 : 80);});}// 动画闭包2,设置颜色变化func2() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);});}// 动画闭包3,设置高度变化func3() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.h = (this.h === 2 ? 5 : 2);});}// 动画闭包4,设置高光变化func4() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.brightNum= (this.brightNum=== 1.5 ? 1 : 1.5);});}build() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h).brightness(this.brightNum)// 其他相似布局…Button("click nFunc")// 按键属性设置….onClick(() => {let doTimes = 10;// 按播放次数循环播放动画for (let i = 0; i < doTimes; i++) {setTimeout(() => {this.func1();this.func2();this.func3();this.func4();}, 1000 * i)}})}}
}

        2、一个animateTo闭包

@Entry
@Component
struct MyComponent {@State w:number = 150@State h:number = 2@State brightNum:number = 1.5@State color:Color = Color.Red// 统一动画闭包,同时设置四个属性变化func() {animateTo({curve: Curve.Sharp, duration: 1000}, () => {this.w = (this.w === 80 ? 150 : 80);this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);this.h = (this.h === 2 ? 5 : 2);this.brightNum= (this.brightNum=== 1.5 ? 1 : 1.5);});}build() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h).brightness(this.brightNum)// 其他相似布局…Button("click oneFunc")// 按键属性设置….onClick(() => {let doTimes = 10;// 按播放次数循环播放动画for (let i = 0; i < doTimes; i++) {setTimeout(() => {this.func();}, 1000 * i)}})}}
}

        3、结论

        将多个属性变化动画合并到同一个animateTo动画闭包中能够显著降低丢帧率和大卡顿的发生频率。合并动画操作可以减少不必要的布局计算和绘制开销,从而提升动画的流畅性和性能表现,有助于优化动画效果的性能。

五、多次animateTo时统一更新状态变量

        使用animateTo方法执行动画时,会对执行动画闭包前后的状态进行对比,然后只对差异部分进行动画处理。

        在动画执行过程中,脏节点是指在界面上需要进行重新绘制的区域。如果状态发生了变化,ArkUI会跟踪这些变化,并在动画闭包执行前进行状态对比,相关的脏节点会被标记为需要刷新,以便在动画闭包执行前对这些脏节点进行重新绘制。这样,只有发生变化的部分才会被纳入动画处理,而不需要重新绘制整个界面。

        这种差异对比的方式能够显著减少不必要的绘制操作,提高动画的性能和流畅度。

         实现效果:针对多个相同组件同时修改多个属性的场景

        

 

        1、在多个animateTo之间更新状态变量

         

@Entry
@Component
struct MyComponent {@State w: number = 100@State h: number = 2@State color: Color = Color.Redbuild() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h)// 其他相似布局…Button("click1")// 按键属性设置….onClick(() => {let doTimes = 5;for (let i = 0; i < doTimes; i++) {setTimeout(() => {this.w = 80// h是非动画属性this.h = 4animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {this.w = (this.w === 80 ? 150 : 80);});// 在两个animateTo之间更新状态变量this.color = Color.YellowanimateTo({ curve: Curve.Linear, duration: 2000 }, () => {this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);});}, 2000 * i)}})}}
}

        2、在animateTo之前显式指定属性初值

         

@Entry
@Component
struct MyComponent {@State w: number = 100@State h: number = 2@State color: Color = Color.Redbuild() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h)// 其他相似布局…Button("click2")// 按键属性设置….onClick(() => {let doTimes = 5;for (let i = 0; i < doTimes; i++) {setTimeout(() => {// 在动画之前显式的指定所有需要动画的属性初值this.w = 80this.color = Color.Yellow// 动画1,修改宽度animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {this.w = (this.w === 80 ? 150 : 80);});// 动画2,修改颜色animateTo({ curve: Curve.Linear, duration: 2000 }, () => {this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);});// 动画完成后刷新非动画属性this.h = 5}, 2000 * i)}})}}
}

        3、在animateTo之前使用原始状态

        

@Entry
@Component
struct MyComponent {//原始状态@State w: number = 80@State h: number = 5@State color: Color = Color.Yellowbuild() {Column() {Row().width(this.w).backgroundColor(this.color).height(this.h)// 其他相似布局…Button("click3")// 按键属性设置….onClick(() => {let doTimes = 5;for (let i = 0; i < doTimes; i++) {// 直接使用原始状态实现动画setTimeout(() => {animateTo({ curve: Curve.Sharp, duration: 1000 }, () => {this.w = (this.w === 80 ? 150 : 80);});animateTo({ curve: Curve.Linear, duration: 2000 }, () => {this.color = (this.color === Color.Yellow ? Color.Red : Color.Yellow);});}, 2000 * i)}})}}
}

        4、结论

        在进行属性变化动画时,显式指定属性初值或者使用原始状态作为动画的起始状态能够显著降低丢帧率和大卡顿的发生频率。因此,在进行动画操作时,合理管理状态变量的更新和初始值设定对于优化动画效果的性能至关重要,开发者应根据需要,尽可能地避免在多个animateTo之间更新状态变量,从而提升动画的流畅性和性能表现。

六、使用renderGroup

        renderGroup是组件通用方法,它代表了渲染绘制的一个组合。其核心功能就是标记组件,在绘制阶段将组件和其子组件的绘制结果进行合并并缓存,以达到复用的效果,从而降低绘制负载。

        组件渲染流程图如下所示:

        

        在进行缓存更新时,需要满足以下三个条件:

  • 组件在当前组件树上。
  • 组件renderGroup被标记为true。
  • 组件内容被标脏。

        在进行缓存清理时,需要满足以下任意条件:

  • 组件不存在于组件树上。
  • 组件renderGroup被标记为false。

具体缓存管理流程图如下所示:

         

        实现效果: 在同一个页面下使用了固定的图片和文本内容,并且每个组件统一使用旋转和缩放的动效

        

// IconItem.ets
@Component
export  struct IconItem {@StorageLink('renderGroupFlag') renderGroupFlag: boolean = false;image: string | Resource = '';text: string | Resource = '';build() {Flex({direction: FlexDirection.Column,justifyContent: FlexAlign.Center,alignContent: FlexAlign.Center}) {Image(this.image).height(20).width(20).objectFit(ImageFit.Contain).margin({ left: 15 })Text(this.text).fontSize(10).fontColor("#182431").margin({ top: 5 }).width(50).opacity(0.8).textAlign(TextAlign.Center)}.backgroundColor('#e3e3e3').width(50).height(50).borderRadius(25)// 在IconItem内调用renderGroup,false为关闭,true为开启.renderGroup(this.renderGroupFlag)}
}// Index.ets
import { IconItem } from './IconItem'// IconItem相关数据
class IconItemSource {image: string | Resource = ''text: string | Resource = ''constructor(image: string | Resource = '', text: string | Resource = '') {this.image = image;this.text = text;}
}@Entry
@Component
struct Index {// renderGroup接口是否开启@State renderGroupFlag: boolean = false;private iconItemSourceList: IconItemSource[] = [];aboutToAppear() {// 遍历添加60个IconItem的数据for (let index = 0; index < 20; index++) {const numStart: number = index * 3;// 此处循环使用三张图片资源this.iconItemSourceList.push(new IconItemSource($r('app.media.album'), `item${numStart + 1}`),new IconItemSource($r('app.media.applet'), `item${numStart + 2}`),new IconItemSource($r('app.media.cards'), `item${numStart + 3}`),);}}build() {Column() {Row() {Row() {Text('场景示例').fontSize(24).lineHeight(24).fontColor(Color.Black).fontWeight(FontWeight.Bold).margin({ left: 30 })}// 动态切换renderGroup功能Stack({ alignContent: Alignment.End }) {Button(this.renderGroupFlag ? 'renderGroup已开启' : 'renderGroup已关闭', {type: ButtonType.Normal,stateEffect: true}).fontSize(12).borderRadius(8).backgroundColor(0x317aff).width(150).height(30).margin({ right: 30 }).onClick(() => {this.renderGroupFlag = !this.renderGroupFlag;AppStorage.setOrCreate('renderGroupFlag', this.renderGroupFlag)})}}.height(56).width('100%').backgroundColor(Color.White).justifyContent(FlexAlign.SpaceBetween)// IconItem放置在grid内GridRow({columns: 6,gutter: { x: 0, y: 0 },breakpoints: { value: ["400vp", "600vp", "800vp"],reference: BreakpointsReference.WindowSize },direction: GridRowDirection.Row}) {ForEach(this.iconItemSourceList, (item: IconItemSource) => {GridCol() {IconItem({ image: item.image, text: item.text }).transition(TransitionEffect.scale({ x: 0.5, y: 0.5 }).animation({duration: 3000, curve: Curve.FastOutSlowIn, iterations: -1 }).combine(TransitionEffect.rotate({ z: 1, angle: 360 }).animation({ duration: 3000, curve: Curve.Linear, iterations: -1 })))}.height(70).width('25%')})}.width("100%").height("100%")}.width('100%').height('100%').alignItems(HorizontalAlign.Center)}
}

        1、结论

        在在单一页面上存在大量应用动效的组件,且组件和其子组件各属性保持固定、组件统一应用动效时,开发者可以使用renderGroup来提升应用的性能,保证应用及动画的流畅性。

七、总结

  • 用户体验:流畅的动画能够提升用户体验,使用户感到界面更加生动和直观。相反,卡顿或者延迟的动画会带来不良的体验。
  • 设备资源消耗:动画的性能表现会直接影响设备的资源消耗,尤其是CPU和GPU的占用。低性能的动画可能导致设备发热、耗电量增加,甚至影响其他应用的运行。
  • 应用稳定性:性能较差的动画可能会导致应用崩溃或者卡顿,影响应用的稳定性和可靠性。
  • 节省流量:高性能的动画可以减少数据传输量,对于移动应用来说,这意味着可以节省用户的流量消耗。
  • 适应不同设备:不同设备的性能差异很大,提升动画性能可以使应用在各种设备上都能够流畅运行。

 

这篇关于HarmonyOS--提升动画运行流畅度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1117373

相关文章

Java终止正在运行的线程的三种方法

《Java终止正在运行的线程的三种方法》停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作,停止一个线程可以用Thread.stop()方法,但最好不要用它,本文给大家介绍了... 目录前言1. 停止不了的线程2. 判断线程是否停止状态3. 能停止的线程–异常法4. 在沉睡中停止5

在VSCode中本地运行DeepSeek的流程步骤

《在VSCode中本地运行DeepSeek的流程步骤》本文详细介绍了如何在本地VSCode中安装和配置Ollama和CodeGPT,以使用DeepSeek进行AI编码辅助,无需依赖云服务,需要的朋友可... 目录步骤 1:在 VSCode 中安装 Ollama 和 CodeGPT安装Ollama下载Olla

解读docker运行时-itd参数是什么意思

《解读docker运行时-itd参数是什么意思》在Docker中,-itd参数组合用于在后台运行一个交互式容器,同时保持标准输入和分配伪终端,这种方式适合需要在后台运行容器并保持交互能力的场景... 目录docker运行时-itd参数是什么意思1. -i(或 --interactive)2. -t(或 --

pycharm远程连接服务器运行pytorch的过程详解

《pycharm远程连接服务器运行pytorch的过程详解》:本文主要介绍在Linux环境下使用Anaconda管理不同版本的Python环境,并通过PyCharm远程连接服务器来运行PyTorc... 目录linux部署pytorch背景介绍Anaconda安装Linux安装pytorch虚拟环境安装cu

通过prometheus监控Tomcat运行状态的操作流程

《通过prometheus监控Tomcat运行状态的操作流程》文章介绍了如何安装和配置Tomcat,并使用Prometheus和TomcatExporter来监控Tomcat的运行状态,文章详细讲解了... 目录Tomcat安装配置以及prometheus监控Tomcat一. 安装并配置tomcat1、安装

mysqld_multi在Linux服务器上运行多个MySQL实例

《mysqld_multi在Linux服务器上运行多个MySQL实例》在Linux系统上使用mysqld_multi来启动和管理多个MySQL实例是一种常见的做法,这种方式允许你在同一台机器上运行多个... 目录1. 安装mysql2. 配置文件示例配置文件3. 创建数据目录4. 启动和管理实例启动所有实例

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.

IDEA运行spring项目时,控制台未出现的解决方案

《IDEA运行spring项目时,控制台未出现的解决方案》文章总结了在使用IDEA运行代码时,控制台未出现的问题和解决方案,问题可能是由于点击图标或重启IDEA后控制台仍未显示,解决方案提供了解决方法... 目录问题分析解决方案总结问题js使用IDEA,点击运行按钮,运行结束,但控制台未出现http://

解决Spring运行时报错:Consider defining a bean of type ‘xxx.xxx.xxx.Xxx‘ in your configuration

《解决Spring运行时报错:Considerdefiningabeanoftype‘xxx.xxx.xxx.Xxx‘inyourconfiguration》该文章主要讲述了在使用S... 目录问题分析解决方案总结问题Description:Parameter 0 of constructor in x

解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题

《解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题》文章详细描述了在使用lombok的@Data注解标注实体类时遇到编译无误但运行时报错的问题,分析... 目录问题分析问题解决方案步骤一步骤二步骤三总结问题使用lombok注解@Data标注实体类,编译时