I want to show videoes in recycler view, the problem is when I swap from the first video and back to the first, the first video reloads again, I want to make all videoes loaded at once and when I swap keep the video loaded. this lines in fragment =>
// Setup StoryBoardAdapter
val storyboardAdapter = StoryBoardAdapter(requireContext(), DataRepository.storyBoardList)
val layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
binding.recyclerViewStoryBoard.layoutManager = layoutManager
binding.recyclerViewStoryBoard.adapter = storyboardAdapter
// Attach PagerSnapHelper to show one item at a time
val pagerSnapHelper = PagerSnapHelper()
pagerSnapHelper.attachToRecyclerView(binding.recyclerViewStoryBoard)
and this is the adapter =>
class StoryBoardAdapter(
private val context: Context,
private val storyBoardList: List<StoryBoard>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
private const val TYPE_IMAGE = 0
private const val TYPE_VIDEO = 1
private const val BASE_URL = "Link"
}
private fun isVideo(url: String): Boolean {
val lowerUrl = url.lowercase()
return lowerUrl.endsWith(".mp4") || lowerUrl.endsWith(".webm")
}
private fun getFullUrl(relativeUrl: String): String {
return if (relativeUrl.startsWith("http")) relativeUrl else "$BASE_URL$relativeUrl"
}
override fun getItemViewType(position: Int): Int {
return if (isVideo(storyBoardList[position].imageEn)) TYPE_VIDEO else TYPE_IMAGE
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == TYPE_IMAGE) {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_story_board_image, parent, false)
ImageViewHolder(view)
} else {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_story_board_video, parent, false)
VideoViewHolder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = storyBoardList[position]
// Check system or app language (you can replace with a stored preference if needed)
val currentLanguage = context.resources.configuration.locales[0].language
val imageUrl = if (currentLanguage == "ar") item.imageAr else item.imageEn
val fullUrl = getFullUrl(imageUrl)
if (holder is ImageViewHolder) {
Glide.with(holder.imageView.context)
.load(fullUrl)
.into(holder.imageView)
} else if (holder is VideoViewHolder) {
val videoUri = Uri.parse(fullUrl)
if (holder.videoView.tag != fullUrl) {
holder.videoView.tag = fullUrl
holder.videoView.setVideoURI(videoUri)
holder.videoView.setOnPreparedListener { mediaPlayer ->
mediaPlayer.isLooping = true
holder.videoView.start()
}
}
}
}
override fun getItemCount(): Int = storyBoardList.size
inner class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.imageViewItem)
}
inner class VideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val videoView: VideoView = itemView.findViewById(R.id.videoViewItem)
}
}
I want to show videoes in recycler view, the problem is when I swap from the first video and back to the first, the first video reloads again, I want to make all videoes loaded at once and when I swap keep the video loaded. this lines in fragment =>
// Setup StoryBoardAdapter
val storyboardAdapter = StoryBoardAdapter(requireContext(), DataRepository.storyBoardList)
val layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
binding.recyclerViewStoryBoard.layoutManager = layoutManager
binding.recyclerViewStoryBoard.adapter = storyboardAdapter
// Attach PagerSnapHelper to show one item at a time
val pagerSnapHelper = PagerSnapHelper()
pagerSnapHelper.attachToRecyclerView(binding.recyclerViewStoryBoard)
and this is the adapter =>
class StoryBoardAdapter(
private val context: Context,
private val storyBoardList: List<StoryBoard>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
private const val TYPE_IMAGE = 0
private const val TYPE_VIDEO = 1
private const val BASE_URL = "Link"
}
private fun isVideo(url: String): Boolean {
val lowerUrl = url.lowercase()
return lowerUrl.endsWith(".mp4") || lowerUrl.endsWith(".webm")
}
private fun getFullUrl(relativeUrl: String): String {
return if (relativeUrl.startsWith("http")) relativeUrl else "$BASE_URL$relativeUrl"
}
override fun getItemViewType(position: Int): Int {
return if (isVideo(storyBoardList[position].imageEn)) TYPE_VIDEO else TYPE_IMAGE
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == TYPE_IMAGE) {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_story_board_image, parent, false)
ImageViewHolder(view)
} else {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_story_board_video, parent, false)
VideoViewHolder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = storyBoardList[position]
// Check system or app language (you can replace with a stored preference if needed)
val currentLanguage = context.resources.configuration.locales[0].language
val imageUrl = if (currentLanguage == "ar") item.imageAr else item.imageEn
val fullUrl = getFullUrl(imageUrl)
if (holder is ImageViewHolder) {
Glide.with(holder.imageView.context)
.load(fullUrl)
.into(holder.imageView)
} else if (holder is VideoViewHolder) {
val videoUri = Uri.parse(fullUrl)
if (holder.videoView.tag != fullUrl) {
holder.videoView.tag = fullUrl
holder.videoView.setVideoURI(videoUri)
holder.videoView.setOnPreparedListener { mediaPlayer ->
mediaPlayer.isLooping = true
holder.videoView.start()
}
}
}
}
override fun getItemCount(): Int = storyBoardList.size
inner class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.imageViewItem)
}
inner class VideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val videoView: VideoView = itemView.findViewById(R.id.videoViewItem)
}
}
Share Improve this question edited Mar 14 at 10:34 Ahmed Salah 9812 gold badges11 silver badges33 bronze badges asked Mar 12 at 23:57 Ammar MomaniAmmar Momani 11 bronze badge3 Answers
Reset to default 0The standard behavior of RecyclerView
is to recycle the viewholder when it is not visible. In our case, it means the state of viewholder including video would get lost when you scroll. One way to disable recycling is to call setIsRecyclable(false)
on viewholder object. This way, we would lose all benefits of RecyclerView
and can impact on performance but we get the desired effect.
RecyclerView is designed to efficiently recycle ViewHolders for better memory management and performance. you can use setIsRecyclable(false)
on the ViewHolder, that will prevent recycling, it's not an ideal solution as it negates the benefits of RecyclerView.
A better approach is to use Google's ExoPlayer
:
- Maintain an ExoPlayer instance per ViewHolder to avoid reloading videos unnecessarily.
- Instead of recreating the player when scrolling, detach and reattach the player to the appropriate ViewHolder, and use some caching mechanism.
As @kirtan mentioned, you can use instance of an ExoPlayer in each ViewHolder (But there is limit for that depending upon media codec available in that device). Retain the seek position in a HashMap in your Adapter, and override 'onViewAttachedToWindow()' and 'onViewDetachedFromWindow()
' methods to pause/play the players, and save/retrieve current playing url and seekPosition in that HashMap. If the video is already played before then its loading will be faster as it will be playing from cache.