Search code examples
androidkotlinandroid-fragmentsandroid-viewpagerandroid-mvvm

Communication between ViewPager's host Fragment and ViewPager's Fragment item


I have a Fragment(InsiraDocumentosFragment) that has a viewpager and in that fragment I initialize an adapter(InsiraDocumentoAdapter) to initialize the viewpager. The adapter(InsiraDocumentoAdapter) initializes generic fragments (DocumentoFragment) that I update dynamically, I can now update the image that is shown inside it, but I need to get the click on that image to either change it or to remove it, but this needs to be done in the Fragment that contains the viewpager, because is in this fragment(InsiraDocumentosFragment) that I have the viewmodel that communicates with the database. How can I watch from InsiraDocumentosFragment the click on the imageview of DocumentFragment ?

Here are my code samples.

class InsiraDocumentosFragment : Fragment() {
    private lateinit var _view: View
    private lateinit var _imgBack: ImageView
    private lateinit var _viewpager: ViewPager
    private lateinit var _imgCamera: ImageView
    private lateinit var _footerTitle: TextView
    private lateinit var _titles: List<String>
    private lateinit var _files: MutableList<File?>
    private lateinit var _viewModel: DocumentoViewModel
    private lateinit var _viewModelProposta: PropostaViewModel
    private lateinit var _viewModelArquivo: ArquivoViewModel

    private lateinit var _adapter: InsiraDocumentoAdapter

    private lateinit var _proposta: PropostaEntity

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_insira_documento, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        _view = view
        _imgBack = _view.findViewById(R.id.imgBack)
        _viewpager = _view.findViewById(R.id.viewpager)

        _imgCamera = _view.findViewById(R.id.cameraDocumento)
        _footerTitle = _view.findViewById(R.id.footerTitle)
        _files = mutableListOf(null, null, null)
        _titles = listOf(getString(R.string.identidade), getString(R.string.comprovante_renda), getString(R.string.foto))
        _adapter = InsiraDocumentoAdapter(_titles, _files, childFragmentManager)
        _viewpager.adapter = _adapter

        _viewModel = ViewModelProvider(
            this,
                    DocumentoViewModel.DocumentoViewModelFactory(DocumentoRepositorio(AppDatabase.getDatabase(_view.context).documentoDao()))
        ).get(DocumentoViewModel::class.java)
        _viewModelProposta = ViewModelProvider(
                this,
                PropostaViewModel.PropostaViewModelFactory(PropostaRepositorio(AppDatabase.getDatabase(_view.context).propostaDao()))
        ).get(PropostaViewModel::class.java)

        _viewModelArquivo = ViewModelProvider(
                this,
                ArquivoViewModel.ArquivoViewModelFactory(ArquivoRepositorio(AppDatabase.getDatabase(_view.context).arquivoDao()))
        ).get(ArquivoViewModel::class.java)

        addViewPagerListener()
        onClickImgBack()
        onClickImgCamera()
        onClickFooterTitle()

        val propostaStr = arguments?.getString(PROPOSTA)
        if (!propostaStr.isNullOrBlank()) {
            _proposta = jsonToObjProposta(propostaStr)
        }
    }

    private fun addViewPagerListener() {
        _viewpager.addOnPageChangeListener(object : OnPageChangeListener {
            override fun onPageScrollStateChanged(state: Int) {}
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
            override fun onPageSelected(position: Int) {
                if (position == _viewpager.adapter!!.count - 1) {
                    _footerTitle.text = getString(R.string.concluir)
                } else {
                    _footerTitle.text = getString(R.string.proximo)
                }
                updateViewPagerLayout(position)
            }
        })
    }

    private fun updateViewPagerLayout(currentPage: Int) {
        when (currentPage) {
            0 -> {
                habilitaIndicadorPagina(_view.findViewById(R.id.primeiraPagina), _view.findViewById(R.id.imgPrimeiraPagina), R.drawable.primeira_pagina_selecionada)
                desabilitaIndicadorPagina(_view.findViewById(R.id.segundaPagina), _view.findViewById(R.id.imgSegundaPagina), R.drawable.segunda_pagina_nao_selecionada)
                desabilitaIndicadorPagina(_view.findViewById(R.id.terceiraPagina), _view.findViewById(R.id.imgTerceiraPagina), R.drawable.terceira_pagina_nao_selecionada)
            }
            1 -> {
                desabilitaIndicadorPagina(_view.findViewById(R.id.primeiraPagina), _view.findViewById(R.id.imgPrimeiraPagina), R.drawable.primeira_pagina_nao_selecionada)
                habilitaIndicadorPagina(_view.findViewById(R.id.segundaPagina), _view.findViewById(R.id.imgSegundaPagina), R.drawable.segunda_pagina_selecionada)
                desabilitaIndicadorPagina(_view.findViewById(R.id.terceiraPagina), _view.findViewById(R.id.imgTerceiraPagina), R.drawable.terceira_pagina_nao_selecionada)
            }
            2 -> {
                desabilitaIndicadorPagina(_view.findViewById(R.id.primeiraPagina), _view.findViewById(R.id.imgPrimeiraPagina), R.drawable.primeira_pagina_nao_selecionada)
                desabilitaIndicadorPagina(_view.findViewById(R.id.segundaPagina), _view.findViewById(R.id.imgSegundaPagina), R.drawable.segunda_pagina_nao_selecionada)
                habilitaIndicadorPagina(_view.findViewById(R.id.terceiraPagina), _view.findViewById(R.id.imgTerceiraPagina), R.drawable.terceira_pagina_selecionada)
            }
        }
    }

    private fun desabilitaIndicadorPagina(layout: ConstraintLayout, image: ImageView, imageDrawable: Int) {
        layout.setBackgroundResource(R.drawable.not_selected_indicator)
        image.setBackgroundResource(imageDrawable)
    }

    private fun habilitaIndicadorPagina(layout: ConstraintLayout, image: ImageView, imageDrawable: Int, ) {
        layout.setBackgroundResource(R.drawable.indicador_selecionado)
        image.setBackgroundResource(imageDrawable)
    }

    private fun onClickFooterTitle() {
        _footerTitle.setOnClickListener {
            if (_viewpager.currentItem == _viewpager.adapter!!.count - 1) {
                var success = true
                for (i in 0..2) {
                    if (_files[i] == null || _files[i]?.path.isNullOrEmpty()) {
                        Toast.makeText(_view.context, R.string.favor_preencher_todas_as_fotos, Toast.LENGTH_LONG).show()
                        _viewpager.currentItem = i
                        success = false
                        break
                    }
                }
                if (success) {
                    _viewModelProposta.addProposta(_proposta.numeroProposta, _proposta.valorPropasta, _proposta.clienteId).observe(viewLifecycleOwner, { idProposta ->
                        _proposta.id = idProposta
                        _viewModel.addDocumento("Nome do Documento", _proposta.id).observe(viewLifecycleOwner, { idDocumento ->
                            val arquivos = mutableListOf<ArquivoEntity>()
                            for (i in 0..2) {
                                arquivos.add(ArquivoEntity(0, _files[i]!!.path, _titles[i], idDocumento))
                            }
                            _viewModelArquivo.addArquivos(arquivos).observe(viewLifecycleOwner, {
                                _view.findNavController().popBackStack(R.id.clienteFragment, false)
                            })
                        })
                    })
                }
            } else {
                _viewpager.currentItem = _viewpager.currentItem + 1
            }
        }
    }

    private fun onClickImgBack() {
        _imgBack.setOnClickListener {
            _view.findNavController().navigateUp()
        }
    }

    private fun onClickImgCamera() {
        _imgCamera.setOnClickListener {
            val intent = Intent(_view.context, ScannerActivity::class.java)
            startActivityForResult(intent, REQUEST_CODE)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            val mediaList = ScannerActivity.getOutputDirectory(_view.context).listFiles()?.toMutableList() ?: mutableListOf()
            if (mediaList.size > 0) {
                val documentoFile = mediaList[mediaList.size - 1]
                _files.removeAt(_viewpager.currentItem)
                _files.add(_viewpager.currentItem, documentoFile)
                _adapter.notifyDataSetChanged()
            }
        }
    }

    private fun jsonToObjProposta(json: String): PropostaEntity {
        val gson = Gson()
        val arrayTutorialType = object : TypeToken<PropostaEntity>() {}.type

        return gson.fromJson(json, arrayTutorialType)
    }

    override fun onResume() {
        super.onResume()
        _view = requireView()
    }

    companion object {
        const val REQUEST_CODE = 1
        const val IMAGE = "IMAGE"
        const val ID_PROPOSTA = "ID_PROPOSTA"
        const val PROPOSTA = "PROPOSTA"
    }
}
class InsiraDocumentoAdapter(private val titles: List<String>, private var files: List<File?>, manager: FragmentManager): FragmentStatePagerAdapter(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
    override fun getCount() = 3
    override fun getItem(position: Int): Fragment {
        return DocumentoFragment.newInstance(titles[position], files[position])
    }
    override fun getItemPosition(`object`: Any): Int {
        return POSITION_NONE
    }
}

class DocumentoFragment : Fragment() {
    private lateinit var _view: View
    private lateinit var _documento: ImageView
    private lateinit var _documentoShape: ImageView
    private lateinit var _txtTitle: TextView
    private var _file: File? = null

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_documento, container, false)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        _view = view
    }

    private fun setUpView() {
        _documento = _view.findViewById(R.id.documento)
        _documentoShape = _view.findViewById(R.id.documentoShape)
        _txtTitle = _view.findViewById(R.id.txtTitle)
        _txtTitle.text = arguments?.getString(TITLE)
        if (!arguments?.getString(FILE).isNullOrEmpty()) {
            _file = File(arguments?.getString(FILE))
        }
        onImgClick()
        insereImagem()
    }

    private fun onImgClick() {
        _documento.setOnClickListener {
            if (_documentoShape.visibility == View.GONE && _file != null) {
                val intent = Intent(_view.context, FullScreenActivity::class.java)
                intent.putExtra(InsiraDocumentosFragment.IMAGE, _file?.path)
                intent.putExtra(TITLE, _txtTitle.text)
                startActivity(intent)
            }
        }
    }

    private fun insereImagem() {
        if (_file != null) {
            Picasso.get().load(_file!!).into(_view.findViewById<ImageView>(R.id.documento))
            _view.findViewById<ImageView>(R.id.documentoShape)?.visibility = View.GONE
        } else {
            _view.findViewById<ImageView>(R.id.documentoShape)?.visibility = View.VISIBLE
        }
    }

    override fun onResume() {
        super.onResume()
        setUpView()
    }

    companion object {
        fun newInstance(title: String, file: File?): DocumentoFragment {
            val fragmentFirst = DocumentoFragment()
            val args = bundleOf(
                TITLE to title,
                FILE to file?.path
            )
            fragmentFirst.arguments = args
            return fragmentFirst
        }

        const val TITLE = "TITLE"
        const val FILE = "FILE"
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".documento.view.DocumentoFragment">

    <TextView
        android:id="@+id/txtTitle"
        android:layout_width="wrap_content"
        android:layout_height="@dimen/dimen_24dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        tools:text="Identidade"
        android:textColor="#015669"
        android:textSize="@dimen/dimen_20sp"
        />

    <ImageView
        android:id="@+id/documento"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="#ffffff"
        android:src="@drawable/rectangle_white"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/txtTitle"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:scaleType="fitXY"
        android:layout_marginTop="@dimen/dimen_20dp"
        android:contentDescription="@string/foto_documento"/>

    <ImageView
        android:id="@+id/documentoShape"
        android:layout_width="88dp"
        android:layout_height="101dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:src="@drawable/document_shape"
        android:contentDescription="@string/foto_documento"/>
</androidx.constraintlayout.widget.ConstraintLayout>

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".documento.view.InsiraDocumentosFragment"
    android:background="#f0f0f0">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_56dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:background="@drawable/rectangle_blue_toolbar">

        <ImageView
            android:id="@+id/imgBack"
            android:layout_width="@dimen/dimen_12dp"
            android:layout_height="@dimen/dimen_20dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginStart="@dimen/dimen_20dp"
            android:layout_marginTop="18dp"
            android:src="@drawable/tint_color"
            android:contentDescription="@string/voltar"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:text="@string/insira_os_documentos"
            android:textColor="#ffffff"
            android:textSize="@dimen/dimen_20sp"
            />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/pagerIndicator"
        android:layout_width="wrap_content"
        android:layout_height="@dimen/dimen_30dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar"
        android:layout_marginTop="@dimen/dimen_20dp">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/primeiraPagina"
            android:layout_width="@dimen/dimen_30dp"
            android:layout_height="@dimen/dimen_30dp"
            android:background="@drawable/indicador_selecionado"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/segundaPagina"
            android:layout_marginHorizontal="@dimen/dimen_10dp"
            >
            <ImageView
                android:id="@+id/imgPrimeiraPagina"
                android:layout_width="@dimen/dimen_9dp"
                android:layout_height="@dimen/dimen_18dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:background="@drawable/primeira_pagina_selecionada"/>
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/segundaPagina"
            android:layout_width="@dimen/dimen_30dp"
            android:layout_height="@dimen/dimen_30dp"
            android:background="@drawable/not_selected_indicator"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@id/primeiraPagina"
            android:layout_marginHorizontal="@dimen/dimen_10dp"
            >
            <ImageView
                android:id="@+id/imgSegundaPagina"
                android:layout_width="@dimen/dimen_9dp"
                android:layout_height="@dimen/dimen_18dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:background="@drawable/segunda_pagina_nao_selecionada"/>
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/terceiraPagina"
            android:layout_width="@dimen/dimen_30dp"
            android:layout_height="@dimen/dimen_30dp"
            android:background="@drawable/not_selected_indicator"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toEndOf="@id/segundaPagina"
            android:layout_marginHorizontal="@dimen/dimen_10dp"
            >
            <ImageView
                android:id="@+id/imgTerceiraPagina"
                android:layout_width="@dimen/dimen_9dp"
                android:layout_height="@dimen/dimen_18dp"
                android:layout_gravity="center"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:background="@drawable/terceira_pagina"/>
        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="@dimen/dimen_280dp"
        android:layout_height="0dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/pagerIndicator"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/footer"
        android:layout_marginHorizontal="@dimen/dimen_40dp"
        android:layout_marginTop="@dimen/dimen_20dp"
        android:layout_marginBottom="@dimen/dimen_20dp"/>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/footer"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_69dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:background="@drawable/footer">

        <ImageView
            android:id="@+id/imgDocumento"
            android:layout_width="@dimen/dimen_26dp"
            android:layout_height="@dimen/dimen_30dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginStart="@dimen/dimen_20dp"
            android:layout_marginVertical="@dimen/dimen_20dp"
            android:src="@drawable/combined_shape"
            android:backgroundTint="#0179b0"
            android:contentDescription="@string/formato_documento"
            />

        <TextView
            android:id="@+id/footerTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toEndOf="@id/imgDocumento"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/cameraDocumento"
            app:layout_constraintBottom_toBottomOf="parent"
            android:textSize="@dimen/dimen_20sp"
            android:textColor="#0179b0"
            android:gravity="center"
            android:text="@string/proximo"/>

        <ImageView
            android:id="@+id/cameraDocumento"
            android:layout_width="@dimen/dimen_33dp"
            android:layout_height="@dimen/dimen_30dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginEnd="@dimen/dimen_20dp"
            android:layout_marginVertical="@dimen/dimen_20dp"
            android:src="@drawable/camera_shape"
            android:backgroundTint="#0179b0"
            android:contentDescription="@string/capturar_foto"
            />

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

Solution

  • I need to get the click on that image to either change it or to remove it, but this needs to be done in the Fragment that contains the viewpager, because is in this fragment(InsiraDocumentosFragment) that I have the viewmodel that communicates with the database.

    You have a couple of options to do that:

    First one:

    Make the ViewModel shared between both fragments (InsiraDocumentosFragment & DocumentoFragment) by instantiating it in both fragments with the activity as the owner using requireActivity() that hosts both fragments:

    val viewModel =
            ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
    

    And then no need to access the parent fragment for changing the image; because now you've an instance of the ViewModel in the DocumentoFragment.

    Second one:

    Access the InsiraDocumentosFragment from the DocumentoFragment using requireParentFragment(), but first create a method in parent that you want to call from the page fragment to do your needed job.

    Assuming the method that you created in InsiraDocumentosFragment is someMethodInParent()

    In DocumentoFragment:

    val parentFragment = requireParentFragment() as InsiraDocumentosFragment
    parentFragment.someMethodInParent()