音频播放器浮窗+通知栏播放器控制

2023-11-24 01:20

本文主要是介绍音频播放器浮窗+通知栏播放器控制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、最终效果如图

二、音频播放器浮窗实现

原理:

1、创建单例类FloatPlayer,内部创建浮窗播放器的布局,通过MediaPlayer去实现音频的播放。并暴露出开启、显示、隐藏、关闭浮窗播放器的方法供外部调用。

2、因为是通过Window.addView()、removeView()方法在每个页面去显示、隐藏浮窗播放器(这种方法优点:不用申请系统弹窗权限。缺点:每个页面都要处理。),需要在页面的基类BaseActivity里边的onResume()、onPause()方法里调用FloatPlayer的显示(判断是否启动了播放器,若启动则显示,否则不显示)、隐藏方法。

0、布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/csRootFloatPlayer"android:layout_width="168dp"android:layout_height="48dp"><com.example.floatplayer.PlayerBgViewandroid:id="@+id/bgViewPlayer"android:layout_width="168dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"android:layout_height="48dp"/><com.google.android.material.imageview.ShapeableImageViewandroid:id="@+id/sivPlayerCover"android:layout_width="@dimen/player_icon_width"android:layout_height="0dp"android:layout_marginStart="8dp"android:scaleType="centerCrop"android:src="@mipmap/ic_player_cover"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintDimensionRatio="1"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:shapeAppearance="@style/imgStyleCircle" /><ImageViewandroid:id="@+id/ivPlayerControl"android:layout_width="@dimen/player_icon_width"android:layout_height="0dp"android:layout_marginStart="@dimen/player_icon_margin_start"android:contentDescription="@null"android:src="@drawable/ic_baseline_play_arrow_24"app:layout_constraintBottom_toBottomOf="@+id/sivPlayerCover"app:layout_constraintDimensionRatio="1"app:layout_constraintStart_toEndOf="@+id/sivPlayerCover"app:layout_constraintTop_toTopOf="@+id/sivPlayerCover"app:tint="@color/float_play_icon" /><ImageViewandroid:id="@+id/ivPlayerNext"android:layout_width="@dimen/player_icon_width"android:layout_height="0dp"android:layout_marginStart="@dimen/player_icon_margin_start"android:contentDescription="@null"android:src="@drawable/ic_baseline_skip_next_24"app:layout_constraintBottom_toBottomOf="@+id/sivPlayerCover"app:layout_constraintDimensionRatio="1"app:layout_constraintStart_toEndOf="@+id/ivPlayerControl"app:layout_constraintTop_toTopOf="@+id/sivPlayerCover"app:tint="@color/float_play_icon" /><ImageViewandroid:id="@+id/ivPlayerClose"android:layout_width="@dimen/player_icon_width"android:layout_height="@dimen/player_icon_width"android:layout_marginStart="@dimen/player_icon_margin_start"android:contentDescription="@null"android:src="@drawable/ic_baseline_close_24"app:layout_constraintBottom_toBottomOf="@+id/sivPlayerCover"app:layout_constraintStart_toEndOf="@+id/ivPlayerNext"app:layout_constraintTop_toTopOf="@+id/sivPlayerCover"app:tint="@color/float_play_icon" /></androidx.constraintlayout.widget.ConstraintLayout>

圆形封面图样式style:imgStyleCircle

<?xml version="1.0" encoding="utf-8"?>
<resources><!--ShapeImageView圆形图--><style name="imgStyleCircle"><item name="cornerFamily">rounded</item><item name="cornerSize">50%</item></style>
</resources>

1、FloatPlayer

import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.media.MediaPlayer
import android.os.Build
import android.transition.TransitionManager
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.view.animation.LinearInterpolator
import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.app.NotificationCompat
import com.example.floatplayer.databinding.FloatPlayerViewBindingclass FloatPlayer private constructor() {//播放器是否活动private var isPlayerActive = false//悬浮窗是否正在显示private var isShowing = falseprivate lateinit var bindingFloatPlayer: FloatPlayerViewBindingprivate val mCsApply = ConstraintSet()private val mCsReset = ConstraintSet()private var mContext: Context? = null//播放状态(默认不播放)private var isPlaying = false//控件展开状态(默认展开)private var isExpansion = trueprivate lateinit var animCoverRotation: ObjectAnimatorprivate var mediaPlayer: MediaPlayer? = null//音乐列表private val mMusicList = arrayListOf(R.raw.shanghai, R.raw.withoutyou)private var mMusicPosition = 0var mPlayControlReceiver: PlayerActionBroadCastReceiver = PlayerActionBroadCastReceiver()private lateinit var mNotificationManager: NotificationManagercompanion object {const val notificationMediaId = 10010const val notificationChannelMedia = "MediaNotification"@Volatileprivate var instance: FloatPlayer? = nullfun getInstance() = instance ?: synchronized(this) {instance ?: FloatPlayer().also { instance = it }}}init {initNotificationManager()initView()}private fun initNotificationManager() {mNotificationManager = FloatApp.appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagerif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {mNotificationManager.createNotificationChannel(NotificationChannel(notificationChannelMedia,"播放器", NotificationManager.IMPORTANCE_DEFAULT))}}//显示播放控件fun show(context: Context) {if (!isPlayerActive) returnmContext = contextinitMediaPlayer()bindingFloatPlayer.root.visibility = View.VISIBLEval windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManagerwindowManager.addView(bindingFloatPlayer.root, createLayoutParam(context))isShowing = true}fun dismiss() {if (!isShowing || mContext == null) returnbindingFloatPlayer.root.visibility = View.INVISIBLEval windowManger = mContext!!.getSystemService(Context.WINDOW_SERVICE) as WindowManagerwindowManger.removeView(bindingFloatPlayer.root)isShowing = falsemContext = null}//开启展示播放控件fun open(context: Context) {if (!isPlayerActive) {isPlayerActive = trueshow(context)}}//是否正在播放fun playing(): Boolean {return mediaPlayer?.isPlaying == true}//播放/暂停切换fun playSwitch() {if (mediaPlayer == null) returnbindingFloatPlayer.ivPlayerControl.performClick()}//切换下一首fun playNext() {if (hasNext()) {bindingFloatPlayer.ivPlayerNext.performClick()} else {showToast("没有更多了")}}//关闭播放控件fun close() {if (!isPlayerActive || mContext == null) returndestroyMediaPlayer()dismiss()isPlayerActive = false}//创建LayoutParamprivate fun createLayoutParam(context: Context): WindowManager.LayoutParams {val layoutParam = WindowManager.LayoutParams()layoutParam.width = WindowManager.LayoutParams.WRAP_CONTENTlayoutParam.height = WindowManager.LayoutParams.WRAP_CONTENT//弹窗层级layoutParam.type = WindowManager.LayoutParams.TYPE_APPLICATIONlayoutParam.gravity = Gravity.START or Gravity.BOTTOM//背景透明layoutParam.format = PixelFormat.TRANSPARENTlayoutParam.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLElayoutParam.x =TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24f,context.resources.displayMetrics).toInt()layoutParam.y =TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80f,context.resources.displayMetrics).toInt()return layoutParam}//初始化控件private fun initView() {bindingFloatPlayer =FloatPlayerViewBinding.inflate(LayoutInflater.from(FloatApp.appContext))mCsApply.clone(bindingFloatPlayer.root)mCsReset.clone(bindingFloatPlayer.root)bindingFloatPlayer.sivPlayerCover.setOnClickListener {playExpansionStatusSwitch(!isExpansion)bindingFloatPlayer.bgViewPlayer.doAnimation()}bindingFloatPlayer.ivPlayerControl.setOnClickListener {playControlStatusSwitch(!isPlaying)updateNotification()}bindingFloatPlayer.ivPlayerNext.setOnClickListener {mediaPlayNext()}bindingFloatPlayer.ivPlayerClose.setOnClickListener {playControlStatusSwitch(false)cancelNotificationMedia()close()}initRotationAnimator(bindingFloatPlayer.sivPlayerCover)}//初始化音频播放器private fun initMediaPlayer() {if (mediaPlayer != null) returnmediaPlayer = MediaPlayer.create(FloatApp.appContext, mMusicList[0])mediaPlayer!!.setOnCompletionListener {mediaPlayNext()}mediaPlayer!!.setOnErrorListener { _, _, _ ->mediaPlayError()true}}//销毁MediaPlayerprivate fun destroyMediaPlayer() {mediaPlayer?.stop()mediaPlayer?.release()mediaPlayer = null}//是否还有下一首private fun hasNext(): Boolean {return mMusicList.isNotEmpty() && mMusicPosition < mMusicList.size - 1}//创建音频播放器private fun mediaPlayNext() {if (!hasNext()) {showToast("没有更多了")} else {mediaPlayer?.stop()mediaPlayer?.release()mMusicPosition++mediaPlayer = MediaPlayer.create(FloatApp.appContext,mMusicList[mMusicPosition])mediaPlayer!!.start()playControlStatusSwitch(true)updateNotification()}}private fun showToast(message: String) {if (mContext == null) returnToast.makeText(mContext, message, Toast.LENGTH_SHORT).show()}//开始播放音频private fun mediaPlayStart() {if (mediaPlayer?.isPlaying == true) returnmediaPlayer?.start()}//暂停播放音频private fun mediaPlayPause() {if (mediaPlayer?.isPlaying == true) {mediaPlayer?.pause()}}//播放出错private fun mediaPlayError() {showToast("播放出错")mediaPlayer?.reset()}//播放按钮状态控制private fun playControlStatusSwitch(startPlay: Boolean) {if (startPlay) {startCoverAnim()mediaPlayStart()} else {stopCoverAnim()mediaPlayPause()}bindingFloatPlayer.ivPlayerControl.setImageResource(if (startPlay) R.drawable.ic_baseline_pause_24else R.drawable.ic_baseline_play_arrow_24)isPlaying = startPlay}//初始化旋转动画private fun initRotationAnimator(target: View) {//顺时针animCoverRotation = ObjectAnimator.ofFloat(target, "rotation", 0f, 360f)//3s一圈animCoverRotation.duration = 6000animCoverRotation.repeatMode = ValueAnimator.RESTARTanimCoverRotation.repeatCount = ValueAnimator.INFINITEanimCoverRotation.interpolator = LinearInterpolator()}//开始播放private fun startCoverAnim() {if (animCoverRotation.isPaused) {animCoverRotation.resume()} else {animCoverRotation.start()}}//取消播放private fun stopCoverAnim() {animCoverRotation.pause()}/*** 封面点击切换状态* @param expansion 展开* */private fun playExpansionStatusSwitch(expansion: Boolean) {if (expansion == isExpansion) returnif (expansion) playViewExpansion() else playViewShrink()isExpansion = expansion}//展开播放控件private fun playViewExpansion() {TransitionManager.beginDelayedTransition(bindingFloatPlayer.root)mCsReset.applyTo(bindingFloatPlayer.root)}//收缩播放控件private fun playViewShrink() {TransitionManager.beginDelayedTransition(bindingFloatPlayer.root)mCsApply.setVisibility(R.id.ivPlayerClose, View.GONE)mCsApply.setVisibility(R.id.ivPlayerNext, View.GONE)mCsApply.setVisibility(R.id.ivPlayerControl, View.GONE)mCsApply.applyTo(bindingFloatPlayer.root)}//更新播放器通知UIprivate fun updateNotification() {val notificationCompatAction = NotificationCompat.Action.Builder(if (playing()) R.drawable.ic_baseline_pause_24else R.drawable.ic_baseline_play_arrow_24,"switch",PendingIntent.getBroadcast(FloatApp.appContext,111,Intent(PlayerActionBroadCastReceiver.actionSwitch),if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENTelse PendingIntent.FLAG_UPDATE_CURRENT)).build()val nextPendingIntent = PendingIntent.getBroadcast(FloatApp.appContext, 222,Intent(PlayerActionBroadCastReceiver.actionNext),if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENTelse PendingIntent.FLAG_UPDATE_CURRENT)val notification =NotificationCompat.Builder(FloatApp.appContext, notificationChannelMedia).setVisibility(NotificationCompat.VISIBILITY_PUBLIC).setSmallIcon(R.mipmap.ic_launcher).addAction(notificationCompatAction).addAction(R.drawable.ic_baseline_skip_next_24, "next", nextPendingIntent).setStyle(androidx.media.app.NotificationCompat.MediaStyle()).setContentTitle("这是标题").setContentText("这是内容这是内容").build()mNotificationManager.notify(notificationMediaId, notification)}//取消掉通知栏播放器private fun cancelNotificationMedia() {mNotificationManager.cancel(notificationMediaId)}
}

2、BaseActivity

import androidx.appcompat.app.AppCompatActivityopen class BaseActivity: AppCompatActivity() {override fun onResume() {super.onResume()FloatPlayer.getInstance().show(this)}override fun onPause() {super.onPause()FloatPlayer.getInstance().dismiss()}
}

三、通知栏播放器实现

原理:创建 NotificationCompat.MediaStyle() 样式的通知,需要引入依赖:

implementation 'androidx.media:media:1.3.0'

并且给通知添加Action,用来和页面的播放起实现操作的联动。

如何创建通知,如下:

//创建通知private fun createNotification() {val notificationCompatAction = NotificationCompat.Action.Builder(if (FloatPlayer.getInstance().playing()) R.drawable.ic_baseline_pause_24else R.drawable.ic_baseline_play_arrow_24,"switch",PendingIntent.getBroadcast(this,111,Intent(PlayerActionBroadCastReceiver.actionSwitch),if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENTelse PendingIntent.FLAG_UPDATE_CURRENT)).build()val nextPendingIntent = PendingIntent.getBroadcast(this, 222,Intent(PlayerActionBroadCastReceiver.actionNext),if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENTelse PendingIntent.FLAG_UPDATE_CURRENT)val notification = NotificationCompat.Builder(this,FloatPlayer.notificationChannelMedia).setVisibility(NotificationCompat.VISIBILITY_PUBLIC).setSmallIcon(R.mipmap.ic_launcher).addAction(notificationCompatAction).addAction(R.drawable.ic_baseline_skip_next_24, "next", nextPendingIntent).setStyle(androidx.media.app.NotificationCompat.MediaStyle()).setContentTitle("这是标题").setContentText("这是内容这是内容").setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_player_cover)).build()val notificationManager =getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagerif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {notificationManager.createNotificationChannel(NotificationChannel(FloatPlayer.notificationChannelMedia,"播放器", NotificationManager.IMPORTANCE_DEFAULT))}notificationManager.notify(FloatPlayer.notificationMediaId, notification)}

四、通知栏播放器和浮窗播放器联动实现

实现原理:

1、播放器的操作通过更新同一个id的通知,去更新通知播放器的UI显示。

2、定义广播接收器:PlayerActionBroadCastReceiver,接收到对应操作的广播之后FloatPlayer相应操作,通知栏播放器的操作,在上边(三)中定义的Action当中的PendingIntent发送对应操作的广播。

广播接收器 PlayerActionBroadCastReceiver 如下:

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent/*** 通知栏播放控制按钮通知播放器操作* */
class PlayerActionBroadCastReceiver : BroadcastReceiver() {companion object {const val actionSwitch = "floatPlayer.switch"const val actionNext = "floatPlayer.next"}override fun onReceive(context: Context, intent: Intent) {when (intent.action) {actionSwitch -> {FloatPlayer.getInstance().playSwitch()}actionNext -> {FloatPlayer.getInstance().playNext()}}}
}

 页面注册广播

//注册控制接受receiverprivate fun registerPlayControlReceiver() {val intentFilter = IntentFilter()intentFilter.addAction(PlayerActionBroadCastReceiver.actionSwitch)intentFilter.addAction(PlayerActionBroadCastReceiver.actionNext)registerReceiver(FloatPlayer.getInstance().mPlayControlReceiver, intentFilter)}

页面取消注册广播

//取消注册receiverprivate fun cancelPlayControlReceiver() {unregisterReceiver(FloatPlayer.getInstance().mPlayControlReceiver)}

最后

播放器动画背景View,因为 ConstraintSet 通过显示,隐藏控件展现的动画显示在真机显示有瑕疵,所以自定义背景动画View,下边是PlayerBgView代码

import android.animation.TypeEvaluator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import androidx.interpolator.view.animation.FastOutSlowInInterpolator/*** 播放器自定义的背景View:因为Constraint动画有显示瑕疵* */
class PlayerBgView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {//控件宽度private var mWidth = 0f//背景圆形半径(控件高度一半)private var mRadius = 0f//动画执行中的终点坐标private var mPositionEndX = 0fprivate var mPaint: Paint? = null//是否展开状态private var bExpend = true//背景颜色private val mColorBG = Color.parseColor("#D7D7D7")//动画时长private val mTimeAnim = 400L//展开后终点坐标x,固定private var mEndX = 0finit {initPaint()}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)mWidth = w.toFloat()mRadius = h / 2fmPositionEndX = mWidth - mRadiusmEndX = mPositionEndXmeasurePaintStrokeWidth(h.toFloat())}private fun initPaint() {mPaint = Paint()mPaint!!.isAntiAlias = truemPaint!!.style = Paint.Style.FILLmPaint!!.color = mColorBGmPaint!!.strokeCap = Paint.Cap.ROUND}//设置画笔宽度为视图高度private fun measurePaintStrokeWidth(height: Float) {mPaint?.strokeWidth = height}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)drawBg(canvas)}private fun drawBg(canvas: Canvas) {canvas.drawCircle(mRadius, mRadius, mRadius, mPaint!!)if (mPositionEndX == 0f) returncanvas.drawLine(mRadius, mRadius, mPositionEndX, mRadius, mPaint!!)}fun doAnimation() {if (bExpend) doShrinkAnimation() else doExpandAnimation()bExpend = !bExpend}//展开动画private fun doExpandAnimation() {val animator = ValueAnimator.ofObject(PositiveEvaluator(), mRadius, mEndX)animator.addUpdateListener { valueAnimator: ValueAnimator ->mPositionEndX = valueAnimator.animatedValue as Floatinvalidate()}animator.duration = mTimeAnimanimator.interpolator = FastOutSlowInInterpolator()animator.start()}private inner class PositiveEvaluator : TypeEvaluator<Float> {override fun evaluate(v: Float, startValue: Float, endValue: Float): Float {return startValue + v * (endValue - startValue)}}//收缩动画private fun doShrinkAnimation() {val animator = ValueAnimator.ofObject(NegativeEvaluator(), mRadius, mEndX)animator.addUpdateListener { valueAnimator: ValueAnimator ->mPositionEndX = valueAnimator.animatedValue as Floatinvalidate()}animator.duration = mTimeAnimanimator.interpolator = FastOutSlowInInterpolator()animator.start()}private inner class NegativeEvaluator : TypeEvaluator<Float> {override fun evaluate(v: Float, startValue: Float, endValue: Float): Float {return endValue - v * (endValue - startValue)}}
}

这篇关于音频播放器浮窗+通知栏播放器控制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

使用Python实现文本转语音(TTS)并播放音频

《使用Python实现文本转语音(TTS)并播放音频》在开发涉及语音交互或需要语音提示的应用时,文本转语音(TTS)技术是一个非常实用的工具,下面我们来看看如何使用gTTS和playsound库将文本... 目录什么是 gTTS 和 playsound安装依赖库实现步骤 1. 导入库2. 定义文本和语言 3

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

使用DrissionPage控制360浏览器的完美解决方案

《使用DrissionPage控制360浏览器的完美解决方案》在网页自动化领域,经常遇到需要保持登录状态、保留Cookie等场景,今天要分享的方案可以完美解决这个问题:使用DrissionPage直接... 目录完整代码引言为什么要使用已有用户数据?核心代码实现1. 导入必要模块2. 关键配置(重点!)3.

SpringSecurity 认证、注销、权限控制功能(注销、记住密码、自定义登入页)

《SpringSecurity认证、注销、权限控制功能(注销、记住密码、自定义登入页)》SpringSecurity是一个强大的Java框架,用于保护应用程序的安全性,它提供了一套全面的安全解决方案... 目录简介认识Spring Security“认证”(Authentication)“授权” (Auth

python之流程控制语句match-case详解

《python之流程控制语句match-case详解》:本文主要介绍python之流程控制语句match-case使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录match-case 语法详解与实战一、基础值匹配(类似 switch-case)二、数据结构解构匹

详解如何使用Python提取视频文件中的音频

《详解如何使用Python提取视频文件中的音频》在多媒体处理中,有时我们需要从视频文件中提取音频,本文为大家整理了几种使用Python编程语言提取视频文件中的音频的方法,大家可以根据需要进行选择... 目录引言代码部分方法扩展引言在多媒体处理中,有时我们需要从视频文件中提取音频,以便进一步处理或分析。本文

Spring Security注解方式权限控制过程

《SpringSecurity注解方式权限控制过程》:本文主要介绍SpringSecurity注解方式权限控制过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、摘要二、实现步骤2.1 在配置类中添加权限注解的支持2.2 创建Controller类2.3 Us

Python中如何控制小数点精度与对齐方式

《Python中如何控制小数点精度与对齐方式》在Python编程中,数据输出格式化是一个常见的需求,尤其是在涉及到小数点精度和对齐方式时,下面小编就来为大家介绍一下如何在Python中实现这些功能吧... 目录一、控制小数点精度1. 使用 round() 函数2. 使用字符串格式化二、控制对齐方式1. 使用

Springboot控制反转与Bean对象的方法

《Springboot控制反转与Bean对象的方法》文章介绍了SpringBoot中的控制反转(IoC)概念,描述了IoC容器如何管理Bean的生命周期和依赖关系,它详细讲解了Bean的注册过程,包括... 目录1 控制反转1.1 什么是控制反转1.2 SpringBoot中的控制反转2 Ioc容器对Bea