当前位置: 首页 > news >正文

【Android】实现图片和视频混合轮播(无限循环、视频自动播放)

目录

  • 前言
  • 一、实现效果
  • 二、具体实现
    • 1. 导入依赖
    • 2. 布局
    • 3. Banner基础配置
    • 4. Banner无限循环机制
    • 5. 轮播适配器
    • 6. 视频播放处理
    • 7. 完整源码
  • 总结

前言

我们日常的需求基本上都是图片的轮播,而在一些特殊需求,例如用于展览的的数据大屏,又想展示图片又想展示视频,本文将利用第三方库com.youth.play.banner轮播控件实现图片和视频混合轮播的效果,自动+手动滑动,无限循环,视频自动播放。

其中图片使用Glide图片加载库,视频使用GSYVideoPlayer播放器。

第三方库移步:【Android】常用的第三方开源库汇总


一、实现效果

在这里插入图片描述在这里插入图片描述

二、具体实现

1. 导入依赖

implementation 'com.youth.banner:banner:2.1.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation 'com.shuyu:GSYVideoPlayer:7.1.8'

2. 布局

主页面:

<?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:layout_width="match_parent"android:layout_height="match_parent"><com.youth.banner.Bannerandroid:id="@+id/banner"android:layout_width="match_parent"android:layout_height="300dp"android:layout_margin="10dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

这里没有使用库里面写好的指示器Indicator,需要的可以自行查看文档使用:Banner。

3. Banner基础配置


// 添加生命周期管理,确保在适当的生命周期内开始和停止轮播
banner.addBannerLifecycleObserver(this).setAdapter(adapter,true) //是否开启无限循环.setLoopTime(loopTime) //轮播时间// 设置轮播图的点击事件监听器
banner.addOnPageChangeListener(object :OnPageChangeListener {/*** 滑动中的监听,当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法会一直得到调用*/override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}/*** 监听滑动到对应索引值的页面,第一个页面不执行*/override fun onPageSelected(position: Int) {}/*** 滑动状态监听*/override fun onPageScrollStateChanged(state: Int) {}
})

Banner支持图片的无限循环,可以选择性打开。我们需要在其中插入视频的播放,就需要先了解其无限循环的处理机制。

4. Banner无限循环机制

无限循环主要有两种实现,一种是设置页面总数设置得很大,一直滑不到底,这不是真正的无限循环,是一种伪循环,性能较低。另外一种就是接下来要说的,Banner的无限循环,它是通过ViewPage2去实现的,在原有页面的基础上左右各添加一个页面,滑动到最后一页跳转到第二页,滑动到第一页跳转到倒数第二页。

在这里插入图片描述

这里为什么要多两个页面呢?一个目的是为了滑动到中间时,有一半是原页面,有一半是新页面。第二个目的是为了有滑动的效果,不会出现跳动。

首先,ViewPage2有三个监听方法:

onPageScrolled :滑动中的监听,当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法会一直得到调用
onPageSelected :滑动或移动(左右滑动、点击指示器跳转)到某一页面时的监听,第一个页面不执行
onPageScrollStateChanged,滑动状态的监听

按理说,在滑动到最后一页时,我们只需要在 onPageSelected 中使用 setCurrentItem 跳转到第一页(总页数的第二页)即可,但是 onPageSelected 在滑动动画还没结束的时候就已经被调用了,此时调用 setCurrentItem 会强制取消当前正在进行的滑动动画并跳转,导致页面跳动不平滑。

所以可以这样去解决这一问题:
(1)先在 onPageSelected 中记录被选中的页面
(2)再在 onPageScrollStateChanged 判断当前动画是否结束,当动画结束时再去调用setCurrentItem方法跳转

我们查看Banner里面的源码也是这样处理无限循环的:
在这里插入图片描述

这里需要格外注意页面的下标,一个页面有两种下标,一个真实下标 realPosition,一个物理下标 position。同理,页面总数量、根据下标获取数据也是一样的逻辑。
在这里插入图片描述
以上面的图为例,下标如下

页面positionrealPosition
第一页02(最后一页)
第二页10
第三页21
第四页32
最后一页40 (第一页)
    /*** 获取真正的位置** @param isIncrease 首尾是否有增加* @param position  当前位置* @param realCount 真实数量* @return*/public static int getRealPosition(boolean isIncrease, int position, int realCount) {if (!isIncrease) {return position;}int realPosition;if (position == 0) {realPosition = realCount - 1;} else if (position == realCount + 1) {realPosition = 0;} else {realPosition = position - 1;}return realPosition;}

5. 轮播适配器

//轮播数据
private val resourcesList by lazy{arrayListOf<PlayResourcesBean>().apply {add(PlayResourcesBean(isImg = true, title = "图片", url = "https://upload-images.jianshu.io/upload_images/5809200-a99419bb94924e6d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"))add(PlayResourcesBean(isImg = true,title = "图片",url = "https://s10.mogucdn.com/mlcdn/c45406/170831_479g0ifl6f2i313feb5ech46kek21_778x440.jpg"))add(PlayResourcesBean(isImg = false, title = "视频",url = "https://media.w3.org/2010/05/sintel/trailer.mp4"))add(PlayResourcesBean(isImg = true,title = "图片",url = "https://s10.mogucdn.com/mlcdn/c45406/170831_7gee6d620i774ec3l5bfh55cfaeab_778x440.jpg"))add(PlayResourcesBean(isImg = false,title = "视频",url = "https://media.w3.org/2010/05/sintel/trailer.mp4"))add(PlayResourcesBean(isImg = true,title = "图片",url = "https://s10.mogucdn.com/mlcdn/c45406/170829_59ia6fd99ghkdkd9603kblha21h5b_778x440.jpg"))add(PlayResourcesBean(isImg = true,title = "图片",url = "https://img.zcool.cn/community/01233056fb62fe32f875a9447400e1.jpg"))add(PlayResourcesBean(isImg = false,title = "视频",url = "https://media.w3.org/2010/05/sintel/trailer.mp4"))}}private val adapter by lazy{PlayAdapter(this,resourcesList, this)
}@Keep
data class PlayResourcesBean(/*** 图片或者视频路径*/val url: String? = "",val isImg:Boolean,val title: String?=""
)

PlayAdapter需要继承BannerAdapter,处理两种不同类型的item(图片item和视频item)

class PlayAdapter(private val mContext: Context, private val dataList: ArrayList<PlayResourcesBean>,private val mActivity: MainActivity
):BannerAdapter<PlayResourcesBean, RecyclerView.ViewHolder>(dataList) {private val mVHMap = HashMap<Int,RecyclerView.ViewHolder>()private val options by lazy {RequestOptions().priority(Priority.HIGH).placeholder(R.mipmap.default_ic).error(R.mipmap.default_ic)}companion object{//视频const val VIDEO = 1//图片const val IMAGE = 2}override fun onBindViewHolder(holder: RecyclerView.ViewHolder,position: Int,payloads: MutableList<Any>) {super.onBindViewHolder(holder, position, payloads)val realPosition = getRealPosition(position)if (holder is VideoHolder){setVideo( holder, getData(realPosition),position,realPosition,realCount)mVHMap[position] = holder}else if(holder is ImageHolder){setImage(holder,getData(realPosition),realPosition,realCount)mVHMap[position] = holder}}override fun getItemViewType(position: Int): Int {//这里的position不是真实的坐标,获取数据需要转换return if (getData(getRealPosition(position)).isImg){IMAGE}else {VIDEO}}override fun onCreateHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {val holder:RecyclerView.ViewHolderval from = LayoutInflater.from(mContext)if (viewType==VIDEO){val view=from.inflate(R.layout.play_item_video,parent,false)holder=VideoHolder(view)}else {val view=from.inflate(R.layout.play_item_image,parent,false)holder=ImageHolder(view)}return holder}private fun setImage(holder: ImageHolder,data: PlayResourcesBean,position: Int,size: Int) {Glide.with(mContext).load(data.url).apply(options).into(holder.image)holder.title.text=data.title?:""holder.numIndicator.text="${position+1}/$size"}private fun setVideo(holder: VideoHolder,data: PlayResourcesBean,position: Int,realPosition: Int,size: Int) {holder.video.apply {setUp(data.url,true,mContext.externalCacheDir,null,"")titleTextView.visibility = View.GONEbackButton.visibility = View.GONEfullscreenButton.visibility = View.GONEstartButton.visibility = View.GONE//音频焦点冲突时是否释放isReleaseWhenLossAudio = true//禁止全屏isAutoFullWithSize = false//isStartAfterPrepared=truedismissControlTime=0isClickable=falseisEnabled=falseisLongClickable=falseplayPosition=position//禁止滑动setIsTouchWiget(false)setVideoAllCallBack(object :GSYSampleCallBack(){override fun onComplete(url: String?, vararg objects: Any?) {super.onComplete(url, *objects)mActivity.banner.isAutoLoop(true)mActivity.banner.start()}override fun onPlayError(url: String?, vararg objects: Any?) {super.onPlayError(url, *objects)mActivity.banner.isAutoLoop(true)mActivity.banner.start()}override fun onClickStop(url: String?, vararg objects: Any?) {super.onClickStop(url, *objects)mActivity.banner.isAutoLoop(true)mActivity.banner.start()}override fun onPrepared(url: String?, vararg objects: Any?) {super.onPrepared(url, *objects)}override fun onStartPrepared(url: String?, vararg objects: Any?) {super.onStartPrepared(url, *objects)}override fun onAutoComplete(url: String?, vararg objects: Any?) {super.onAutoComplete(url, *objects)mActivity.banner.isAutoLoop(true)//快速轮播到下一页mActivity.banner.setLoopTime(100)mActivity.banner.start()mActivity.banner.setLoopTime(mActivity.loopTime)}})}holder.title.text=data.title?:""holder.numIndicator.text="${realPosition+1}/$size"}internal class VideoHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {val video: MyVideoPlayerval title: TextViewval numIndicator:TextViewinit {video = itemView.findViewById(R.id.banner_vp)title = itemView.findViewById(R.id.video_title)numIndicator = itemView.findViewById(R.id.video_numIndicator)}}internal class ImageHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {val image: ImageViewval title: TextViewval numIndicator:TextViewinit {image = itemView.findViewById(R.id.banner_image)title = itemView.findViewById(R.id.image_title)numIndicator = itemView.findViewById(R.id.image_numIndicator)}}fun getVHMap(): HashMap<Int,RecyclerView.ViewHolder> {return mVHMap}override fun onBindView(holder: RecyclerView.ViewHolder?,data: PlayResourcesBean?,position: Int,size: Int) {}}

这里需要用一个HashMap去存储所有ViewHolder,以便后续控制视频自动播放,这里的key是物理下标,一个key对应一个页面,如果用realPosition作为key,不是一对一,容易产生value更新不及时。

6. 视频播放处理

GSYVideoPlayer 播放器的使用可以查看文档:GSYVideoPlayer

无限循环解决之后,就需要在轮播中自动播放视频,隐藏和关闭掉各种手动按钮和事件,需要对播放器进行改造(继承StandardGSYVideoPlayer),我是在翻看了它许多源码和github上的issues才得以解决。

class MyVideoPlayer(context: Context, attrs: AttributeSet?) : StandardGSYVideoPlayer(context,attrs){override fun changeUiToPreparingShow() {super.changeUiToPreparingShow()hide()}override fun changeUiToPauseShow() {super.changeUiToPauseShow()hide()}override fun changeUiToError() {super.changeUiToError()hide()}override fun changeUiToCompleteShow() {super.changeUiToCompleteShow()hide()}override fun changeUiToPlayingBufferingShow() {super.changeUiToPlayingBufferingShow()hide()}override fun changeUiToPrepareingClear() {super.changeUiToPrepareingClear()hide()}override fun changeUiToPlayingClear() {super.changeUiToPlayingClear()hide()}override fun changeUiToPauseClear() {super.changeUiToPauseClear()hide()}override fun changeUiToPlayingBufferingClear() {super.changeUiToPlayingBufferingClear()hide()}override fun changeUiToClear() {super.changeUiToClear()hide()}override fun changeUiToCompleteClear() {super.changeUiToCompleteClear()hide()}override fun changeUiToPlayingShow() {super.changeUiToPlayingShow()hide()}override fun changeUiToNormal() {super.changeUiToNormal()hide()}private fun hide() {setViewShowState(mTopContainer, INVISIBLE)setViewShowState(mBottomContainer, INVISIBLE)setViewShowState(mStartButton, INVISIBLE)setViewShowState(mLoadingProgressBar, INVISIBLE)setViewShowState(mThumbImageViewLayout, INVISIBLE)setViewShowState(mBottomProgressBar, INVISIBLE)setViewShowState(mLockScreen, GONE)}override fun onVideoPause() {super.onVideoPause()mPauseBeforePrepared=false //是否准备完成前调用了暂停,避免再次暂停}override fun touchDoubleUp() {//双击会暂停,重载清除}
}

StandardGSYVideoPlayer是GSYVideoPlayer中的一个标准样式的播放器,在视频准备、暂停、播放完成等等状态隐藏和关闭掉各种手动按钮和事件,设置mPauseBeforePrepared避免视频准备完成后自动暂停,重载touchDoubleUp清除掉原有的双击屏幕暂停视频的效果。

视频自动播放
接下来就是主要逻辑了,这里有几个关键点:

在轮播到视频时,将自动轮播关闭,同时播放视频
在视频播放错误、非正常完成时,暂停视频,同时继续轮播
在视频播放正常完成、手动滑动到下一页时,立即轮播到下一页

首先,就需要在MyVideoPlayer适配器中处理视频控件的各种监听事件(查看源码视频有各种状态):

setVideoAllCallBack(object :GSYSampleCallBack(){override fun onComplete(url: String?, vararg objects: Any?) {super.onComplete(url, *objects)mActivity.banner.isAutoLoop(true)mActivity.banner.start()}override fun onPlayError(url: String?, vararg objects: Any?) {super.onPlayError(url, *objects)mActivity.banner.isAutoLoop(true)mActivity.banner.start()}override fun onClickStop(url: String?, vararg objects: Any?) {super.onClickStop(url, *objects)mActivity.banner.isAutoLoop(true)mActivity.banner.start()}override fun onPrepared(url: String?, vararg objects: Any?) {super.onPrepared(url, *objects)}override fun onStartPrepared(url: String?, vararg objects: Any?) {super.onStartPrepared(url, *objects)}override fun onAutoComplete(url: String?, vararg objects: Any?) {super.onAutoComplete(url, *objects)mActivity.banner.isAutoLoop(true)//设置轮播时间,立即轮播到下一页mActivity.banner.setLoopTime(100)mActivity.banner.start()mActivity.banner.setLoopTime(mActivity.loopTime)}
})

其次,在Activity中设置任务,在页面切换时通过之前保存的ViewHolder去执行视频播放或者暂停的任务:

private val taskHandler by lazy{Handler(Looper.getMainLooper())
}
val task = Runnable { //可能是首尾切换页,两个页面循环跳转if(adapter.getVHMap().containsKey(currentPos)){if(adapter.getVHMap()[currentPos] is PlayAdapter.VideoHolder){val holder = (adapter.getVHMap()[currentPos] as PlayAdapter.VideoHolder)GSYVideoManager.onPause()holder.video.startPlayLogic()banner.isAutoLoop(true)banner.stop()banner.isAutoLoop(false)}else{GSYVideoManager.onPause()banner.isAutoLoop(true)banner.start()}}else{GSYVideoManager.onPause()banner.isAutoLoop(true)banner.start()}
}// 设置轮播图的点击事件监听器
banner.addOnPageChangeListener(object :OnPageChangeListener {/*** 滑动中的监听,当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法会一直得到调用*/override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}/*** 监听滑动到对应索引值的页面,第一个页面不执行*/override fun onPageSelected(position: Int) {if (position == banner.realCount - 1) {currentPos=banner.realCount} else if (position == 0) {currentPos = 1} else {currentPos = position + 1}}/*** 滑动状态监听*/override fun onPageScrollStateChanged(state: Int) {// Banner跳转之后再去控制视频播放if (state == ViewPager2.SCROLL_STATE_IDLE) {if (banner.isInfiniteLoop) {taskHandler.post(task)}}}
})

任务一定要在onPageScrollStateChanged中去执行,这时候特殊的两个页面已经跳转完成了,只会执行一次任务,否则执行两个异步任务会导致一个视频播放一个视频暂停,两个异步没有固定的执行顺序,出现视频没有画面但是有声音的问题。

完整的Activity代码:

class MainActivity:AppCompatActivity() {val loopTime=6000L//列表+前后两页过渡页的坐标private var currentPos= 1private val taskHandler by lazy{Handler(Looper.getMainLooper())}private val adapter by lazy{PlayAdapter(this,resourcesList, this)}private val resourcesList by lazy{arrayListOf<PlayResourcesBean>().apply {add(PlayResourcesBean(isImg = true, title = "图片", url = "https://upload-images.jianshu.io/upload_images/5809200-a99419bb94924e6d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"))add(PlayResourcesBean(isImg = true,title = "图片",url = "https://s10.mogucdn.com/mlcdn/c45406/170831_479g0ifl6f2i313feb5ech46kek21_778x440.jpg"))add(PlayResourcesBean(isImg = false, title = "视频",url = "https://media.w3.org/2010/05/sintel/trailer.mp4"))add(PlayResourcesBean(isImg = true,title = "图片",url = "https://s10.mogucdn.com/mlcdn/c45406/170831_7gee6d620i774ec3l5bfh55cfaeab_778x440.jpg"))add(PlayResourcesBean(isImg = false,title = "视频",url = "https://media.w3.org/2010/05/sintel/trailer.mp4"))add(PlayResourcesBean(isImg = true,title = "图片",url = "https://s10.mogucdn.com/mlcdn/c45406/170829_59ia6fd99ghkdkd9603kblha21h5b_778x440.jpg"))add(PlayResourcesBean(isImg = true,title = "图片",url = "https://img.zcool.cn/community/01233056fb62fe32f875a9447400e1.jpg"))add(PlayResourcesBean(isImg = false,title = "视频",url = "https://media.w3.org/2010/05/sintel/trailer.mp4"))}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)initBanner()}private fun initBanner() {// 添加生命周期管理,确保在适当的生命周期内开始和停止轮播banner.addBannerLifecycleObserver(this).setAdapter(adapter,true) //是否开启无限循环.setLoopTime(loopTime) //轮播时间// 设置轮播图的点击事件监听器banner.addOnPageChangeListener(object :OnPageChangeListener {/*** 滑动中的监听,当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法会一直得到调用*/override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}/*** 监听滑动到对应索引值的页面,第一个页面不执行*/override fun onPageSelected(position: Int) {if (position == banner.realCount - 1) {currentPos=banner.realCount} else if (position == 0) {currentPos = 1} else {currentPos = position + 1}}/*** 滑动状态监听*/override fun onPageScrollStateChanged(state: Int) {// Banner跳转之后再去控制视频播放if (state == ViewPager2.SCROLL_STATE_IDLE) {if (banner.isInfiniteLoop) {taskHandler.post(task)}}}})//重新设置banner数据//banner.setDatas(resourcesList)}val task = Runnable {//可能是首尾切换页,两个页面循环跳转if(adapter.getVHMap().containsKey(currentPos)){if(adapter.getVHMap()[currentPos] is PlayAdapter.VideoHolder){val holder = (adapter.getVHMap()[currentPos] as PlayAdapter.VideoHolder)GSYVideoManager.onPause()holder.video.startPlayLogic()banner.isAutoLoop(true)banner.stop()banner.isAutoLoop(false)}else{GSYVideoManager.onPause()banner.isAutoLoop(true)banner.start()}}else{GSYVideoManager.onPause()banner.isAutoLoop(true)banner.start()}}override fun onResume() {super.onResume()taskHandler.post(task)}override fun onPause() {super.onPause()GSYVideoManager.onPause()}override fun onDestroy() {super.onDestroy()GSYVideoManager.releaseAllVideos()//移除数据绑定,否则第二次设置适配器出错banner.destroy()}}

7. 完整源码

到这就完结撒花了🎉,完整代码我已上传github,需要的可以自行pull:https://github.com/FullCourage/BannerPlayer


总结

解决jar包的一些底层问题,不仅要知道原理,还要学会从源码中了解它的执行逻辑。

相关文章:

【Android】实现图片和视频混合轮播(无限循环、视频自动播放)

目录 前言一、实现效果二、具体实现1. 导入依赖2. 布局3. Banner基础配置4. Banner无限循环机制5. 轮播适配器6. 视频播放处理7. 完整源码 总结 前言 我们日常的需求基本上都是图片的轮播&#xff0c;而在一些特殊需求&#xff0c;例如用于展览的的数据大屏&#xff0c;又想展…...

VLAN基础

一、什么是Vlan VLAN&#xff08;Virtual Local Area Network&#xff09;是虚拟局域网的简称&#xff0c;是一种将单一物理局域网&#xff08;LAN&#xff09;在逻辑层面上划分为多个独立的广播域的技术。每个VLAN都是一个独立的广播域&#xff0c;其内部主机可以直接通信&am…...

pytest-yaml-sanmu(五):跳过执行和预期失败

除了手动注册标记之外&#xff0c;pytest 还内置了一些标记可直接使用&#xff0c;每种内置标记都会用例带来不同的特殊效果&#xff0c;本文先介绍 3 种。 1. skip skip 标记通常用于忽略暂时无法执行&#xff0c;或不需要执行的用例。 pytest 在执行用例时&#xff0c;如果…...

linux指令整合(centos系统持续更新中。。。)

1、查询java进程 ps -ef|grep java 2、查询端口占用 lsof -i:端口号 3、 启动java程序 java -jar jar包路径 后台启动 nohup java -jar jar包路径 -Xms512m -Xmx512m > 日志路径 2>&1 & 4、查看服务器资源占用 top 5、关闭进程 kill -9 进程号...

个人开发实现AI套壳网站快速搭建(Vue+elementUI+SpringBoot)

目录 一、效果展示 二、项目概述 三、手把手快速搭建实现本项目 3.1 前端实现 3.2 后端方向 五、后续开发计划 一、效果展示 默认展示 一般对话展示&#xff1a; 代码对话展示&#xff1a; 二、项目概述 本项目是一个基于Web的智能对话服务平台&#xff0c;通过后端与第…...

Cesium与Three相机同步(3)

Cesium与Three融合的案例demo <!DOCTYPE html> <html lang"en" class"dark"><head><meta charset"UTF-8"><link rel"icon" href"/favicon.ico"><meta name"viewport" content&q…...

PMP考试报名项目经历怎么填写?指引请收好

PMP&#xff0c;这一全球公认的项目管理金牌认证&#xff0c;不仅是对项目管理能力的认可&#xff0c;更是职业生涯中的一大助力。然而&#xff0c;在报名PMP时&#xff0c;很多小伙伴都面临一个共同的难题&#xff1a;如何书写项目经验&#xff1f;今天&#xff0c;就让我们一…...

Git的基本使用方法

Git的基本使用方法 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨Git的基本使用方法&#xff0c;Git作为目前最流行的版本控制系统之一&…...

深入剖析 @Autowired 和 @Resource 在 Spring 中的区别

在 Spring 框架中&#xff0c;Autowired 和 Resource 是两个常用的注解&#xff0c;用于实现依赖注入。尽管它们都能达到将依赖对象注入到目标 bean 的目的&#xff0c;但在细节上存在一些显著的差异。本文将深入探讨这两个注解的区别&#xff0c;并结合 Spring 源码进行分析&a…...

Golang-slice理解

slice golang-slice语雀笔记整理 slicego为何设计slice&#xff1f;引用传递实现扩容机制 go为何设计slice&#xff1f; 切片对标其他语言的动态数组&#xff0c;底层通过数组实现&#xff0c;可以说是对数组的抽象&#xff0c;底层的内存是连续分配的所以效率高&#xff0c;可…...

【Linux系统】文件描述符fd

1.回顾一下文件 我们之前对文件的理解是在语言层上&#xff0c;而语言层去理解文件是不可能的&#xff01;&#xff01;&#xff01; 下面是一份c语言文件操作代码&#xff01;&#xff01;&#xff01; #include<stdio.h> int main() {FILE* fd fopen("lo…...

【嵌入式——FreeRTOS】启动任务调度器

【嵌入式——FreeRTOS】启动任务调度器 开启任务调度器vTaskStartScheduler()xPortStartScheduler()prvStartFirstTask()启动第一个任务 开启任务调度器 用于启动任务调度器&#xff0c;任务调度器启动后&#xff0c;FreeRTOS便会开始进行任务调度。 //启动任务&#xff0c;开…...

EFCore_客户端评估与服务端评估

定义 客户端评估: 先将表的所有数据读取至内存&#xff0c;再在内存中对数据进行筛选&#xff0c;数据的筛选工作在客户端服务端评估: 先将代码翻译为SQL语句&#xff0c;再执行SQL语句对数据进行筛选&#xff0c;数据的筛选工作在服务端&#xff08;默认方式&#xff09; 如何…...

Java面试题--JVM大厂篇之深入了解G1 GC:高并发、响应时间敏感应用的最佳选择

引言&#xff1a; 在现代Java应用的性能优化中&#xff0c;垃圾回收器&#xff08;GC&#xff09;的选择至关重要。对于高并发、响应时间敏感的应用而言&#xff0c;G1 GC&#xff08;Garbage-First Garbage Collector&#xff09;无疑是一个强大的工具。本文将深入探讨G1 GC适…...

SAP配置发布WebService接口并调用(超级详细)

文章目录 前言一、案例介绍/笔者需求二、WebService是什么&#xff1f; a.传输协议 b.数据协议 c.WSDL d.UDDI 三、WebService 和 WebApi 的区别以及优缺点 a.主要区别 b.优缺点 四、SAP如何发布一个webser…...

中英双语介绍美国首都:华盛顿哥伦比亚特区(Washington, D.C.)

中文版 华盛顿哥伦比亚特区&#xff08;Washington, D.C.&#xff09;&#xff0c;简称华盛顿或D.C.&#xff0c;是美国的首都和联邦直辖区。以下是对华盛顿哥伦比亚特区各方面的详细介绍&#xff1a; 人口 截至2020年&#xff0c;美国人口普查数据显示&#xff0c;华盛顿哥…...

java:aocache的单实例缓存(一)

上一篇博客《java:aocache:基于aspectJ实现的方法缓存工具》介绍了aocache的基本使用&#xff0c; 介绍AoCacheable注解时说过&#xff0c;AoCacheable可以定义在构造方法上&#xff0c;定义在构造方法&#xff0c;该构建方法就成了单实例模式。 也就是说&#xff0c;只要构建…...

pcap包常见拆分方法

文章目录 Wireshark 拆分流量包SplitCap使用简介魔数报错示例结果 在进行流量分析时&#xff0c;经常需要分析pcap流量包。但是体积过大的流量包不容易直接分析&#xff0c;经常需要按照一定的规则把它拆分成小的数据包。 这里统一选择cic数据集里的Thursday-WorkingHours.pcap…...

C++中的类型转换操作符:static_cast reinterpret_cast const_cast dynamic_cast

目录​​​​​​​ C语言中的类型转换 C中的类型转换 C中的类型转换操作符 static_cast reinterpret_cast const_cast volatile关键字 赋值兼容 dynamic_cast C语言中的类型转换 基本概念&#xff1a;赋值运算符左右两侧类型不同&#xff0c;或形参与实参类型不匹配…...

MySQL-SQL优化Explain命令以及参数详解

前言 在MySQL优化的众多手段中&#xff0c;EXPLAIN命令扮演着至关重要的角色。它是数据库管理员和开发者手中的利器&#xff0c;用于分析SQL查询的执行计划。通过执行EXPLAIN&#xff0c;MySQL会提供一份详细的查询执行计划报告&#xff0c;这份报告揭示了查询将如何执行&…...

别只会重启了!进来告诉你AP无法上线怎么办

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 你们好&#xff0c;我的网工朋友。 作为网工&#xff0c;咱们都知道无线网络的重要性&#xff0c;尤其是对于企业网络来说&#xff0c;无线接入点…...

数据恢复篇:如何在 Android 手机上恢复未保存/删除的 Word 文档

在 Android 手机上访问 Word 文档通常很简单&#xff0c;但是当这些重要文件被删除或未保存时会发生什么&#xff1f;这种情况虽然令人痛苦&#xff0c;但并非毫无希望。到 2024 年&#xff0c;有几种强大的方法来处理此类数据丢失。本指南重点介绍如何在Android手机上恢复已删…...

Python | Leetcode Python题解之第208题实现Trie(前缀树)

题目&#xff1a; 题解&#xff1a; class Trie:def __init__(self):self.children [None] * 26self.isEnd Falsedef searchPrefix(self, prefix: str) -> "Trie":node selffor ch in prefix:ch ord(ch) - ord("a")if not node.children[ch]:retur…...

Ethernet是以太网通讯

...

咖啡消费旺季到来 为何想转让的库迪联营商却越来越多

文 | 智能相对论 作者 | 霖霖 去年还在朝“三年万店”计划狂奔的库迪&#xff0c;今年已出现明显“失速”。 早在今年2月&#xff0c;库迪就官宣其门店数已超过7000家&#xff0c;如今4个多月过去&#xff0c;据极海品牌监测数据显示&#xff0c;截至6月27日&#xff0c;其总…...

神经网络原理

神经网络原理是一种模拟人脑的机器学习技术&#xff0c;通过大量的神经元和层次化的连接进行信息处理和学习。 图1 神经元 神经网络由许多简单的计算单元或“神经元”组成&#xff0c;这些神经元通过连接传递信息。每个连接都有一个权重&#xff0c;用于调整传递的信号强度。这…...

安卓应用开发学习:获取经纬度及地理位置描述信息

前段时间&#xff0c;我在学习鸿蒙应用开发的过程中&#xff0c;在鸿蒙系统的手机上实现了获取经纬度及地理位置描述信息&#xff08;鸿蒙应用开发学习&#xff1a;手机位置信息进阶&#xff0c;从经纬度数据获取地理位置描述信息&#xff09;。反而学习时间更长的安卓应用开发…...

各类排序方法 手撕快排 回顾经典快排 优化版快排

快排的主要思想是分而治之 第一步&#xff0c;确定分界点&#xff0c;a 第二步&#xff0c;调整区间&#xff0c;利用分界点a&#xff0c;把小于分界点a的数放在左边&#xff0c;大于的放在右边&#xff0c;相等的放在哪都可以 第三步&#xff0c;递归处理左右两段 实现(暴…...

独一无二的设计模式——单例模式(Java实现)

1. 引言 亲爱的读者们&#xff0c;欢迎来到我们的设计模式专题&#xff0c;今天的讲解的设计模式&#xff0c;还是单例模式哦&#xff01;上次讲解的单例模式是基于Python实现&#xff08;独一无二的设计模式——单例模式&#xff08;python实现&#xff09;&#xff09;的&am…...

使用MoA(Mixture of Agents)混合智能体技术,结合多个开源大语言模型如Llama3、phi-3和Mistral,实现一个强大的AI智能体

1.简介 论文简介: 论文提出了一种称为混合智能体(Mixture-of-Agents,MoA)的方法,利用多个大语言模型(LLM)的集体智慧来提高自然语言理解和生成任务的性能。 MoA采用了分层结构,每一层包含多个LLM智能体。每个智能体都将前一层所有智能体的输出作为辅助信息来生成自己的回答。通…...

前端面试题_Css

一、说一下Css的盒子模型&#xff1f; HTML中所有元素都可以看成是一个盒子 盒子的组成&#xff1a;content、padding、border、margin 盒子的类型&#xff1a; 标准盒模型&#xff1a;marginborderpaddingcontent -- box-sizing&#xff1a;content-box&#xff08;默认&a…...

AI在线免费视频工具3:声音生视频

1、声音生视频 Noisee&#xff1a;通过声音生成对应视频&#xff0c;可以增加prompt指定生成内容相关视频 https://noisee.ai/create...

final、const、readonly关键字在不同语言中代表着什么

一、Java 1.被final修饰的类不能被继承。 2.被final修饰的方法不能被重写。 被 final 修饰的类中所有的成员方法都会隐式的定义为 final 方法。 若父类中 final 方法的访问权限为 private &#xff0c;则子类中不能直接继承该方法。此时可以在子类中定义相同方法名的函数&…...

HarmonyOS ArkUi Tabs+TabContent+List实现tab吸顶功能

Demo效果 Entry Component struct StickyNestedScroll {State message: string Hello WorldState arr: number[] []scroller new Scroller()StyleslistCard() {.backgroundColor(Color.White).height(72).width("100%").borderRadius(12)}build() {Scroll(this.sc…...

Hugging Face Accelerate 两个后端的故事:FSDP 与 DeepSpeed

社区中有两个流行的零冗余优化器 (Zero Redundancy Optimizer&#xff0c;ZeRO)算法实现&#xff0c;一个来自DeepSpeed&#xff0c;另一个来自PyTorch。Hugging FaceAccelerate对这两者都进行了集成并通过接口暴露出来&#xff0c;以供最终用户在训练/微调模型时自主选择其中之…...

TextField是用于在用户界面中输入文本的控件。它广泛应用于表单、搜索框、评论区等需要用户输入文字的场景

TextField是用于在用户界面中输入文本的控件。它广泛应用于表单、搜索框、评论区等需要用户输入文字的场景。以下是对TextField的详细解释&#xff0c;涵盖其各个方面的功能和属性。 基本属性 text 描述&#xff1a;TextField中当前显示的文本。用法&#xff1a;text: "示…...

MYSQL 四、mysql进阶 5(InnoDB数据存储结构)

一、数据库的存储结构&#xff1a;页 索引结构给我们提供了高效的索引方式&#xff0c;不过索引信息以及数据记录都是保存在文件上的&#xff0c;确切说时存储在页结构中&#xff0c;另一方面&#xff0c;索引是在存储引擎中实现的&#xff0c;Mysql服务器上的存储引擎负责对表…...

Spring企业开发核心框架-下

五、Spring AOP面向切面编程 1、场景设定和问题复现 ①准备AOP项目 项目名&#xff1a;Spring-aop-annotation ②声明接口 /*** - * / 运算的标准接口!*/ public interface Calculator { int add(int i, int j); int sub(int i, int j); int mul(int i, in…...

X射线底片焊缝缺陷检测

实现四种焊缝缺陷的检测和分割处理。...

直播的js代码debug解析找到protobuf消息的定义

我们都知道直播的弹幕消息是通过websocket发送的&#xff0c;而且是通过protobuf传输的&#xff0c;那么这里面传输了哪些内容&#xff0c;这个proto文件又要怎么定义&#xff1f;每个消息叫什么&#xff0c;消息里面又包含有哪些字段&#xff0c;每个字段又是什么类型&#xf…...

详细学习es6扩展运算符

ES6中的扩展运算符&#xff08;Spread Operator&#xff09;是一种非常方便的语法&#xff0c;主要用于将可迭代对象&#xff08;比如数组、字符串等&#xff09;展开成多个参数。以下是关于ES6扩展运算符的详细内容&#xff1a; 用法&#xff1a; 在数组字面量中展开数组&am…...

HEC-HMS水文模型教程

原文链接&#xff1a;HEC-HMS水文模型教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247607904&idx5&sn1a210328a3fc8f941b433674d8fe2c85&chksmfa826787cdf5ee91d01b6981ebd89deac3e350d747d0fec45ce2ef75d7cb8009341c6f55114d&token90645021…...

Spring Cloud LoadBalancer基础入门与应用实践

官网地址&#xff1a;https://docs.spring.io/spring-cloud-commons/reference/spring-cloud-commons/loadbalancer.html 【1】概述 Spring Cloud LoadBalancer是由SpringCloud官方提供的一个开源的、简单易用的客户端负载均衡器&#xff0c;它包含在SpringCloud-commons中用…...

layui在表格中嵌入上传按钮,并修改上传进度条

当需要在表格中添加上传文件按钮&#xff0c;并不需要弹出填写表单的框的时候&#xff0c;需要在layui中&#xff0c;用按钮触发文件选择 有一点需要说明的是&#xff0c;layui定义table并不是在定义的标签中渲染&#xff0c;而是在紧接着的标签中渲染&#xff0c;所以要获取实…...

14-10 AIGC 项目生命周期——第一阶段

生成式 AI 项目生命周期的整个过程类似于从范围、选择、调整和对齐/协调模型以及应用程序集成开始的顺序依赖过程。流程表明每个步骤都建立在前一步的基础上。有必要了解每个阶段对于项目的成功都至关重要。 下面的流程图重点介绍了生成式 AI 项目生命周期的第一阶段 1 — “范…...

经典小游戏(一)C实现——三子棋

switch(input){case 1:printf("三子棋\n");//这里先测试是否会执行成功break;case 0:printf("退出游戏\n");break;default :printf("选择错误&#xff0c;请重新选择!\n");break;}}while(input);//直到输入的结果为假&#xff0c;循环才会结束} …...

如何利用AI生成可视化图表(统计图、流程图、思维导图……)免代码一键绘制图表

由于目前的AI生成图表工具存在以下几个方面的问题&#xff1a; 大多AI图表平台是纯英文&#xff0c;对国内用户来说不够友好&#xff1b;部分平台在生成图表前仍需选择图表类型、配置项&#xff0c;操作繁琐&#xff1b;他们仍需一份规整的数据表格&#xff0c;需要人为对数据…...

Firefox 编译指南2024 Windows10-使用Git 管理您的Firefox(五)

1. 引言 在现代软件开发中&#xff0c;版本控制系统&#xff08;VCS&#xff09;是不可或缺的工具&#xff0c;它不仅帮助开发者有效管理代码的变化&#xff0c;还支持团队协作与项目管理。Mercurial 是一个高效且易用的分布式版本控制系统&#xff0c;其设计目标是简洁、快速…...

ubuntu 18 虚拟机安装(1)

ubuntu 18 虚拟机安装 ubuntu 18.04.6 Ubuntu 18.04.6 LTS (Bionic Beaver) https://releases.ubuntu.com/bionic/ 参考&#xff1a; 设置固定IP地址 https://blog.csdn.net/wowocpp/article/details/126160428 https://www.jianshu.com/p/1d133c0dec9d ubuntu-18.04.6-l…...

Github 上 Star 数最多的大模型应用基础服务 Dify 深度解读(一)

背景介绍 接触过大模型应用开发的研发同学应该都或多或少地听过 Dify 这个大模型应用基础服务&#xff0c;这个项目自从 2023 年上线以来&#xff0c;截止目前&#xff08;2024-6&#xff09;已经获得了 35k 多的 star&#xff0c;是目前大模型应用基础服务中最热门的项目之一…...

opencv 处理图像去噪的几种方法

OpenCV 提供了多种图像去噪的方法&#xff0c;以下是一些常见的去噪技术以及相应的 Python 代码示例&#xff1a; 均值滤波&#xff1a;使用像素邻域的灰度均值代替该像素的值。 import cv2 import numpy as np import matplotlib.pyplot as pltimg cv2.imread("4.jpg&qu…...

数组理论基础

1. **数组定义**&#xff1a; - 数组是存放在连续内存空间上的相同类型数据的集合。 2. **数组特性**&#xff1a; - 数组下标从0开始。 - 数组的内存空间地址是连续的。 3. **数组操作**&#xff1a; - 数组可以通过下标索引快速访问元素。 - 数组元素的删除…...

LabVIEW项目外协时选择公司与个人兼职的比较

​在选择LabVIEW项目外协合作伙伴时&#xff0c;外协公司和个人兼职各有优劣。个人兼职成本较低且灵活&#xff0c;但在可靠性、技术覆盖面、资源和风险管理上存在不足。而外协公司拥有专业团队、丰富资源、完善的项目管理和风险控制&#xff0c;尽管成本较高&#xff0c;但能提…...

Spark on k8s 源码解析执行流程

Spark on k8s 源码解析执行流程 1.通过spark-submit脚本提交spark程序 在spark-submit脚本里面执行了SparkSubmit类的main方法 2.运行SparkSubmit类的main方法&#xff0c;解析spark参数&#xff0c;调用submit方法 3.在submit方法里调用doRunMain方法&#xff0c;最终调用r…...

黄子韬vs徐艺洋卫生间风波

【热搜爆点】黄子韬VS徐艺洋&#xff1a;卫生间风波背后的职场与友情界限探讨在这个充满欢笑与意外的综艺时代&#xff0c;《跟我出游吧》再次以它独有的魅力&#xff0c;引爆了一个既尴尬又引人深思的话题——“黄子韬要上徐艺洋的卫生间&#xff1f;”这不仅仅是一句简单的调…...

【ES】--Elasticsearch的翻页详解

目录 一、前言二、from+size浅分页1、from+size导致深度分页问题三、scroll深分页1、scroll原理2、scroll可以返回总计数量四、search_after深分页1、search_after避免深度分页问题一、前言 ES的分页常见的主要有三种方式:from+size浅分页、scroll深分页、search_after分页。…...

底盘革新+M9同款雷达,问界M7Ultra升级点都在这

20万级的SUV,8个月获得超过18万的用户,这样辉煌的成绩你几乎很难在汽车圈找到第二家,问界M7国民SUV名副其实。???但华为与赛力斯绝不止步于此,5月31日晚,问界M7 Ultra正式上市,次共推出四款车型,售价区间为28.98-32.98万元。与此同时北汽新能源与华为合作的享界S9也正…...

上海数据交易所入选人民银行上海总部金融“五篇大文章”协同推进机制牵头机构

来自上海数据交易所的消息,上海数据交易所近日入选人民银行上海总部金融“五篇大文章”协同推进机制牵头机构之一。该机制设立的目的是,深入调研做好金融“五篇大文章”面临的热点、难点、堵点问题,合力探索切实可行的政策建议与行动方案。上海数据交易所一直致力于推动数据…...

九章云极DataCanvas公司重磅亮相第七届数字中国建设峰会

近日&#xff0c;由国家发展改革委、国家数据局、国家网信办、科技部、国务院国资委、福建省人民政府共同主办的第七届数字中国建设峰会在福州盛大举行&#xff0c;九章云极DataCanvas公司重磅亮相峰会现场&#xff0c;深度展示智算中心建设核心成果及“算法算力”一体化AI智算…...

【JavaScript】ECMAS6(ES6)新特性概览(二):解构赋值、扩展与收集、class类全面解析

&#x1f525; 个人主页&#xff1a;空白诗 &#x1f525; 热门专栏&#xff1a;【JavaScript】 文章目录 &#x1f33f; 引言五、 Destructuring Assignment - 解构赋值&#xff0c;数据提取的艺术 &#x1f3a8;&#x1f4cc; 数组解构&#x1f4cc; 对象解构&#x1f4cc; 特…...

Python面试宝典:Python中与动态规划和排序算法相关的面试笔试题(1000加面试笔试题助你轻松捕获大厂Offer)

Python面试宝典:1000加python面试题助你轻松捕获大厂Offer【第二部分:Python高级特性:第十二章:高级数据结构和算法:第二节:Python中实现各类高级数据结构与算法三】 第十二章:高级数据结构和算法第二节:Python中实现各类高级数据结构与算法2.3、python中与动态规划和排…...

【区块链】外部应用程序与区块链进行交互

一&#xff0c;外部应用程序与区块链进行交互案例目标与流程 1.1案例目标 掌握FISCO BCOS应用环境的搭建 与使用&#xff08;FISCO BCOSWeBASE&#xff09;掌握基于Java SpringBoot的应 用程序后端项目搭建与开发。掌握应用程序后端与FISCO BCOS 链的交互。掌握应用程序前端…...