【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包的一些底层问题,不仅要知道原理,还要学会从源码中了解它的执行逻辑。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

跟着DW学习大语言模型-什么是知识库,如何构建知识库

建立一个高效的知识库对于个人和组织来说非常重要。无论是为了个人学习和成长&#xff0c;还是为了组织的持续创新和发展&#xff0c;一个完善的知识管理系统都是不可或缺的。那么&#xff0c;如何建立一个高效的知识库呢&#xff1f; 在建立知识库之前&#xff0c;首先需要确定…

第3章 小功能大用处-事务与Lua

为了保证多条命令组合的原子性&#xff0c;Redis提供了简单的事务功能以及集成Lua脚本来解决这个问题。 首先简单介绍Redis中事务的使用方法以及它的局限性&#xff0c;之后重点介绍Lua语言的基本使用方法&#xff0c;以及如何将Redis和Lua脚本进行集成&#xff0c;最后给出Red…

项目实训-vue(十三)

项目实训-vue&#xff08;十三&#xff09; 文章目录 项目实训-vue&#xff08;十三&#xff09;1.概述2.处理按钮 1.概述 本篇博客将记录我在图片上传页面中的工作。 2.处理按钮 实现了图片的上传之后&#xff0c;还需要设置具体的上传按钮。 这段代码使用 Element UI 的 …

Spring学习02-[Spring容器核心技术IOC学习]

Spring容器核心技术IOC学习 什么是bean?如何配置bean?Component方式bean配合配置类的方式import导入方式 什么是bean? 被Spring管理的对象就是bean,和普通对象的区别就是里面bean对象里面的属性也被注入了。 如何配置bean? Component方式、bean配合配置类的方式、import…

C语言 | Leetcode C语言题解之第190题颠倒二进制位

题目&#xff1a; 题解&#xff1a; const uint32_t M1 0x55555555; // 01010101010101010101010101010101 const uint32_t M2 0x33333333; // 00110011001100110011001100110011 const uint32_t M4 0x0f0f0f0f; // 00001111000011110000111100001111 const uint32_t M8…

【containerd】Containerd高阶命令行工具nerdctl

前言 对于习惯了使用docker cli的用户来说&#xff0c;containerd的命令行工具ctr使用起来不是很顺手&#xff0c;此时别慌&#xff0c;还有另外一个命令行工具项目nerdctl可供我们选择。 nerdctl是一个与docker cli风格兼容的containerd的cli工具。 nerdctl已经作为子项目加入…

秋招突击——6/24——复习{完全背包问题——买书,状态转换机——股票买卖V}——新作{两数相除,LRU缓存实现}

文章目录 引言复习完全背包问题——买书个人实现 状态转换机——股票买卖V个人实现参考实现 新作两数相除个人实现 新作LRU缓存实现个人实现unordered_map相关priority_queue相关 参考实现自己复现 总结 引言 今天知道拼多多挂掉了&#xff0c;难受&#xff0c;那实习就是颗粒无…

汪汪队短视频:成都柏煜文化传媒有限公司

汪汪队短视频&#xff1a;萌宠与冒险的交织乐章 在数字时代的浪潮中&#xff0c;短视频以其短小精悍、内容丰富的特点&#xff0c;迅速占领了人们的闲暇时光。而在这些琳琅满目的短视频中&#xff0c;有一类作品以其独特的魅力吸引了无数观众的目光&#xff0c;那就是以萌宠为…

单门户上集成多种数据库查询入口

&#xff08;作者&#xff1a;陈玓玏&#xff09; 开源项目&#xff0c;欢迎star哦&#xff0c;https://github.com/tencentmusic/cube-studio 在一家公司&#xff0c;我们通常会有多种数据库&#xff0c;每种数据库因为其特性承担不同的角色&#xff0c;比如mysql这种轻量…

AI-024人工智能指数报告(三):经济

概述 人工智能融入经济会引发许多很迷人的问题。有人预测人工智能会推动生产力得到改进&#xff0c;但其影响程度仍未确定。其中一个主要关切是大规模劳动替代的可能性——工作究竟会在多大程度上被自动化还是人工智能主要起到增强作用&#xff1f;各个行业的企业已经在用各种…

基于FPGA的温湿度检测

初始化部分就不过多赘述&#xff0c;我会给出对应的文件&#xff0c;我只说明这部分里面涉及到使用的代码部分 1、数据的读取和校验 数据的读取和检验代码如下 always (posedge clk_us)if (data_temp[7:0] data_temp[39:32] data_temp[31:24] data_temp[23:16] data_te…

SpringBoot+Vue集成富文本编辑器

1.引入 我们常常在各种网页软件中编写文档的时候&#xff0c;常常会有富文本编辑器&#xff0c;就比如csdn写博客的这个页面&#xff0c;包含了富文本编辑器&#xff0c;那么怎么实现呢&#xff1f;下面来详细的介绍&#xff01; 2.安装wangeditor插件 在Vue工程中&#xff0c;…

基于 SpringBoot + Vue 的图书购物商城项目

本项目是一个基于 SpringBoot 和 Vue 的图书购物商城系统。系统主要实现了用户注册、登录&#xff0c;图书浏览、查询、加购&#xff0c;购物车管理&#xff0c;订单结算&#xff0c;会员折扣&#xff0c;下单&#xff0c;个人订单管理&#xff0c;书籍及分类管理&#xff0c;用…

PCL 使用列文伯格-马夸尔特法计算变换矩阵

目录 一、算法原理1、计算过程2、主要函数3、参考文献二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、计算过程 2、主要函数 void pcl

EXCEL快速填充空白内容

** EXCEL快速填充空白内容 ** 1.全选所有需要填充的内容&#xff0c;按住电脑的F5或者CTRLG点击定位 2.可以看到空白处被自动选定&#xff0c;之后按电脑和⬆&#xff0c;最后CTRLenter 可以看到空白处已经被填充。

计网入门还没到放弃

TCP报文段格式 源端口&#xff1a;标识报文的返回地址 目的端口&#xff1a;指明计算机上的应用程序接口 序号&#xff1a;通过SYN包传给接收端主机&#xff0c;每传送一次就1&#xff0c;用来解决网络包乱序的问题。 确认号&#xff1a;期望下一次收到的数据的序列号&#xff…

酷开科技用酷开系统打造数字化家庭娱乐与教育的新天地

家庭教育的重要性不言而喻&#xff0c;俗话说&#xff1a;父母是孩子的第一任老师&#xff0c;也是终生的老师。一个人是否能够成长成才&#xff0c;家庭教育是基础和前提&#xff0c;学校教育能够起到促进和引导作用&#xff0c;社会教育也会对一个人的成长产生重要影响。在数…

【YOLOv5/v7改进系列】引入PConv——轻量快速的卷积块

一、导言 《奔跑吧&#xff0c;别走&#xff1a;追求更高的FLOPS以实现更快的神经网络》&#xff0c;主要探讨了如何设计快速且高效的神经网络模型。文章指出&#xff0c;尽管许多研究致力于减少浮点运算次数(FLOPs)来提升模型速度&#xff0c;但FLOPs的减少并不直接等同于延迟…

Zookeeper部署

Zookeeper部署 下载安装包Linux解压安装包修改配置文件编辑zoo.cf配置 启动服务停止服务常用zookeeper指令查看namespace列表创建namespace删除namespace 注意&#xff1a;该文章为简单部署操作&#xff0c;没有复杂的配置内容&#xff0c;用的是3.7.2版本。 下载安装包 进入z…

python爬虫需要什么HTTP代理?

用来爬虫的话&#xff0c;还是建议用高匿名代理&#xff0c;但显然题主用了高匿名代理还是出现了一部分问题&#xff0c;我们可以先找到问题关键再解决它&#xff0c;一般爬虫用了高匿名代理出现被封会有以下几种原因&#xff1a; 1.代理IP的质量不过关 一般来说每个网站都有…