Как отобразить стрим камеры телефона в RecyclerView?

Я пытаюсь реализовать отображение стрима с камеры в первый ViewHolder адаптера. Но проблема в фризах при пролистывании RecyclerView. Когда ViewHolder c превью выезжает или скрывается за экраном происходит фриз на пол секунды. Фриз происходит только на реальном устройстве, на эмуляторе все нормально.

Для отображения стрима с камеры я использую библиотеку CameraX и PreviewView

Как можно избежать фризов или есть ли способ реализовать это не используя CameraX без фризов?

Fragment:

class PhotoCommentFragment : Fragment() {
    lateinit var binding: FragmentPhotoCommentBinding
    lateinit var viewModel: PhotoCommentViewModel
    private lateinit var adapter: AddingFilesAdapter
    private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
    private lateinit var preview: Preview
    private lateinit var cameraSelector: CameraSelector
    private lateinit var camera: Camera
    private var previewView: PreviewView? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentPhotoCommentBinding.inflate(inflater)

        viewModel =
            ViewModelProvider(this).get(PhotoCommentViewModel::class.java)
        
        adapter = AddingFilesAdapter{
            previewView = it
            if (PermissionUtils.checkCameraPermissions(this))
                cameraProvide(it)
        }
        
        binding.recyclerView.setItemViewCacheSize(0)
        binding.recyclerView.addItemDecoration(GridSpacingItemDecoration(4, requireContext()))
        binding.recyclerView.setHasFixedSize(true)
        binding.recyclerView.adapter = adapter
        viewModel.itemsMultimedia.observe(viewLifecycleOwner) { items ->
            adapter.refresh(items)
        }
        
        return binding.root
    }
    
    private fun cameraProvide(previewView: PreviewView){
        cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
    
        cameraProviderFuture.addListener({
            val cameraProvider = cameraProviderFuture.get()
            bindPreview(cameraProvider, previewView)
        }, ContextCompat.getMainExecutor(requireContext()))
    }
    
    private fun bindPreview(cameraProvider : ProcessCameraProvider, previewView: PreviewView) {
        if(!::preview.isInitialized){
            preview = Preview.Builder()
                .build()
        }
        if(!::cameraSelector.isInitialized){
            cameraSelector = CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                .build()
        }
    
        preview.setSurfaceProvider(previewView.createSurfaceProvider())
        if(!::camera.isInitialized){
            camera = cameraProvider.bindToLifecycle(viewLifecycleOwner, cameraSelector, preview)
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        when (requestCode) {
            PermissionUtils.CAMERA_PERMISSIONS -> {
                if (grantResults.isNotEmpty()
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED
                ) {
                    previewView?.let {
                        cameraProvide(it)
                    }
                } else {
                    // permission denied
                }
            }
        }
    }
}

Adapter:

class AddingFilesAdapter(private val bindPreview: (previewView: PreviewView) -> Unit): RecyclerView.Adapter<AddingFilesAdapter.BaseViewHolder>() {
    private var items: List<Multimedia> = listOf()
    
    init {
        setHasStableIds(true)
    }
    
    abstract class BaseViewHolder(binding: ViewDataBinding): ViewHolderDetail(binding.root) {
        abstract fun bind()
    }

    class CameraViewHolder(val binding: ItemAddingCameraBinding,
                           val bindPreview: (previewView: PreviewView) -> Unit,
    ) : BaseViewHolder(binding){
        companion object{
            fun from(parent: ViewGroup,
                     bindPreview: (previewView: PreviewView) -> Unit,
            ): CameraViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = ItemAddingCameraBinding.inflate(layoutInflater, parent, false)
                return CameraViewHolder(binding, bindPreview)
            }
        }

        override fun bind() {
            bindPreview(binding.previewView)
        }
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
        
        return when (viewType) {
            0 -> {
                CameraViewHolder.from(parent, bindPreview)
            }
            else -> {
                ImageViewHolder.from(parent)
            }
        }
    }
    
    override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
        holder.bind()
    }
    
    override fun getItemCount(): Int {
        return items.size + 1
    }
    
    override fun getItemViewType(position: Int): Int {
        return position
    }
    
    fun refresh(_items: List<Multimedia>) {
        val productDiffUtilCallback = AddingFilesDiffCallback(items, _items)
        val productDiffResult = DiffUtil.calculateDiff(productDiffUtilCallback)
        
        items = _items
        
        productDiffResult.dispatchUpdatesTo(this)
    }
}

item_adding_camera:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/body"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <androidx.camera.view.PreviewView
            android:id="@+id/previewView"
            android:layout_width="90dp"
            android:layout_height="90dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />


        <ImageView
            android:id="@+id/imageView7"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:src="@drawable/ic_photo"
            app:layout_constraintBottom_toBottomOf="@+id/previewView"
            app:layout_constraintEnd_toEndOf="@+id/previewView"
            app:layout_constraintStart_toStartOf="@+id/previewView"
            app:layout_constraintTop_toTopOf="@+id/previewView" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

grabley:

    def camerax_version = "1.0.0-beta06"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
    implementation "androidx.camera:camera-view:1.0.0-alpha13"
    implementation "androidx.camera:camera-extensions:1.0.0-alpha13"

Ответы (1 шт):

Автор решения: Birugov

Попробуй во вьюхолдере для изображений Glide.with(context).load(uri).into(view)

Столкнулся с такой же проблемой со скроллом, решилась этим

→ Ссылка