Как отобразить стрим камеры телефона в 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)
Столкнулся с такой же проблемой со скроллом, решилась этим