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

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>

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()

Related

How to display JSON data from Horizontal recycle view kotlin using MVP method

I hope is all well with you.
I have constructed my code and build according the MVP style as well pulling JSON data but when I run the the code the horizontal recycle view and the JSON data is not being displayed. I tried going through every line of code and watching other tutorials but still no results.
Here is below my main activity:
class ProductCategoryActivity : BaseMvpActivity<ProductCategoryActivityView, ProductCategoryActivityPresnter> (),
ProductCategoryActivityView, CategoryAdapter.onItemClickListener{
private lateinit var binding: FragmentProductCategoryBinding
val data :MutableList<CateogryResponse> = ArrayList()
val adapter= CategoryAdapter(data, this)
#Inject
lateinit var presenter: ProductCategoryActivityPresnter
#Inject
lateinit var progressDialog: ProgressDialog
override fun onCreateComponent() {
userComponent.plus(CategoryActivityModule(this)).inject(this)
}
override fun providePresenter(): ProductCategoryActivityPresnter {
return presenter
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = FragmentProductCategoryBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
presenter.fetchcatogries()
showdata(ArrayList())
}
override fun showError(message: String) {
}
override fun showProgress() {
}
override fun hideProgress() {
}
override fun showdata(data: ArrayList<CateogryResponse>) {
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recyclerViewPrimary.layoutManager = layoutManager
recyclerViewPrimary.setHasFixedSize(true)
recyclerViewPrimary.adapter = adapter
for(i in data)
data.add(CateogryResponse("product"))
}
override fun onItemClick(position: Int) {
Toast.makeText(this, "Item $position clicked", Toast.LENGTH_SHORT).show()
val clickedItem = data[position]
adapter.notifyItemChanged(position)
}
}
Here is my presenter:
#ActivityScope
class ProductCategoryActivityPresnter #Inject constructor(
private val stringProvider: StringProvider,
#AndroidScheduler private val observeOnScheduler: Scheduler,
#IOScheduler private val subscribeOnScheduler: Scheduler,
private val getCategoryUseCase: CategoryUseCase
) : BasePresenter<ProductCategoryActivityView>() {
lateinit var catogriesservice: CategoryRepositoryImpl
val catogriesLoadError = MutableLiveData<Boolean>()
val loading = MutableLiveData<Boolean>()
var catogries: ArrayList<CateogryResponse> = arrayListOf()
override fun onCreatePresenter(savedInstanceState: Bundle?) {
}
override fun onSaveInstanceState(outState: Bundle?) {
}
override fun onLoadData(arguments: Bundle?) {
fetchcatogries()
}
fun fetchcatogries() {
getCategoryUseCase.execute()
.observeOn(observeOnScheduler)
.subscribeOn(subscribeOnScheduler)
.subscribe(
SingleRequestSubscriber(
{
it
if (it != null && it.size > 0) {
catogries.removeAll(catogries)
catogries.addAll(it)
}
},
onFailure = { appException ->
view?.showError(
ErrorHandler.getErrorMessage(
appException,
stringProvider
)
)
},
onApiError = { apiException ->
view?.showError(
ErrorHandler.getErrorMessage(
apiException,
stringProvider
)
)
},
onAuthenticationError = { requestxception ->
view?.showError(
ErrorHandler.getErrorMessage(
requestxception,
stringProvider
)
)
},
onShowProgress = {
if (it) {
view?.showProgress()
} else {
view?.hideProgress()
}
},
onSubscribed = {
disposable.add(it)
})
)
}
}
Here is my view:
interface ProductCategoryActivityView {
fun showError(message: String)
fun showProgress()
fun hideProgress()
fun showdata ( data: ArrayList<CateogryResponse>)
}
Here is my adapter:
class CategoryAdapter(
private val data: List<CateogryResponse>,
private val listener: onItemClickListener
) : RecyclerView.Adapter<CategoryAdapter.ViewHolder>() {
private val items: MutableList<CardView>
init {
this.items = ArrayList()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_category_adapter, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvTitle.text = data[position].product
items.add(holder.card)
}
override fun getItemCount(): Int {
return data.size
}
inner class ViewHolder(itemView: View
) : RecyclerView.ViewHolder(itemView),
View.OnClickListener {
val tvTitle: TextView = itemView.featured_title
val card: CardView = itemView.CardView
init {
itemView.setOnClickListener(this)
}
override fun onClick(v: View?) {
val position: Int = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener.onItemClick(position)
}
}
}
interface onItemClickListener {
fun onItemClick(position: Int)
}
}
Image:
Fragment Category Adapter:
android:id="#+id/CardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:cardCornerRadius="2dp"
app:cardElevation="8dp">
<!-- We Will Add here the card & ImageViews -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp">
<ImageView
android:id="#+id/featured_image"
android:layout_width="match_parent"
android:layout_height="140dp"
android:scaleType="centerCrop" />
<TextView
android:id="#+id/featured_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineHeight="23dp"
android:text="Chairs"
android:textColor="#color/colorAccent"
android:textSize="20sp" />
<TextView
android:id="#+id/featured_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="asbkd asudhlasn saudnas jasdjasl hisajdl asjdlnas" />
</LinearLayout>
</androidx.cardview.widget.CardView>
Fragment Product Category:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fafcfe"
tools:context="ui.category.ProductCategoryActivity">
<ImageView
android:id="#+id/imageView3"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginTop="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="#+id/imageView"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewPrimary"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginTop="20dp"
android:text="#string/topselling"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/constraintLayout" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toTopOf="#+id/imageView3"
app:layout_constraintEnd_toEndOf="#+id/constraintLayout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewSecondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="#+id/imageView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toBottomOf="#+id/imageView3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
/>
<TextView
android:id="#+id/textViewCategories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="15dp"
android:text="#string/catogries"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="#+id/constraintLayout"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteX="39dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
Not sure what went wrong.
If someone can point it out or show me what needs to be done to be able to display the data in horzintal recycle view, I would be really thanlful
You should update your recycler view adapter once your fetch call is executed, not before it ends.
Modify your fetchcatogries declaration so it accepts a callback method, which will be executed after the data are loaded.
This method will accept a list object as a parameter, which you will manage in your ProductCategoryActivity to populate the RecyclerView.
fun fetchcatogries(callback: (MutableList<CateogryResponse>) -> Unit) {
getCategoryUseCase.execute()
.observeOn(observeOnScheduler)
.subscribeOn(subscribeOnScheduler)
.subscribe(
SingleRequestSubscriber(
{ categories ->
callback(categories)
},
...
)
}
You can now edit your onCreate and showdata methods in ProductCategoryActivity like this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = FragmentProductCategoryBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
presenter.fetchcatogries { categories ->
showdata(categories as ArrayList<CateogryResponse>)
}
}
override fun showdata(data: ArrayList<CateogryResponse>) {
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recyclerViewPrimary.layoutManager = layoutManager
recyclerViewPrimary.setHasFixedSize(true)
recyclerViewPrimary.adapter = CategoryAdapter(data.toList(), this)
}

Bind viewmodel to recyclerview item.xml with databinding, android?

I want to bind data on adapter from viewmodel in xml layout file
This is my fragment class.
class NotificationFragment : Fragment() {
var customeProgressDialog: CustomeProgressDialog? = null
private val appPreferences: AppPreference by inject()
private val notificationViewModel: NotificationViewModel by viewModel()
private lateinit var binding: FragmentNotificationBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentNotificationBinding.inflate(inflater, container, false)
return binding.getRoot()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.notification.layoutManager=LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
customeProgressDialog = CustomeProgressDialog(activity)
notificationViewModel.notifications(
appPreferences.getUsername(),
appPreferences.getPassword(),
appPreferences.getUserId()
)
initObservables()
}
private fun initObservables() {
notificationViewModel.progressDialog?.observe(this, Observer {
if (it!!) customeProgressDialog?.show() else customeProgressDialog?.dismiss()
})
notificationViewModel.apiResponse?.observe(
viewLifecycleOwner,
androidx.lifecycle.Observer { response ->
if (response.dataList != null) {
var notificationAdapter = NotificationAdapter(response.dataList as List<Data>)
notificationAdapter.notifyDataSetChanged()
binding.notification.adapter = notificationAdapter
}
})
}
}
My viewmodel
class NotificationViewModel(networkCall: NetworkCall) : ViewModel(),
Callback<ApiResponse> {
var progressDialog: SingleLiveEvent<Boolean>? = null
var apiResponse: MutableLiveData<ApiResponse>? = null
var networkCall: NetworkCall;
init {
progressDialog = SingleLiveEvent<Boolean>()
apiResponse = MutableLiveData<ApiResponse>()
this.networkCall = networkCall
}
fun notifications(username: String?, password: String?, userId: String?) {
progressDialog?.value = true
val apiPost = ApiPost()
apiPost.userName = username
apiPost.password = password
apiPost.UserId = userId
apiPost.FileType = NetworkConstant.FILE_TYPE_NOT
networkCall.getPDF(apiPost).enqueue(this)
}
override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
progressDialog?.value = false
}
override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
progressDialog?.value = false
apiResponse?.value = response.body()
}
}
The adapter class
lass NotificationAdapter(private val list: List<Data>) :
RecyclerView.Adapter<NotificationAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.element_list, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val movie: Data = list[position]
holder.bind(movie)
holder.itemView.setOnClickListener {
if (!TextUtils.isEmpty(movie.filePath)) {
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(movie.filePath))
holder.itemView.context.startActivity(browserIntent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
override fun getItemCount(): Int = list.size
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(movie: Data) {
with(itemView) {
tv_notifi.text = movie.displayName
tv_date.text = movie.UpdatedDate
if (movie.description != null) {
tv_brief.text = movie.description
tv_brief.visibility = View.VISIBLE
}
}
}
}
}
This is my item xml layout
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="com.mountmeru.viewmodel.NotificationViewModel" />
</data>
<androidx.cardview.widget.CardView
android:id="#+id/main_cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/main_cardrl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<RelativeLayout
android:id="#+id/rl_newsdate"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.3">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_notifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:maxLines="2"
android:text="hey i am notification text"
android:textColor="#android:color/black"
android:textSize="16sp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_brief"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_notifi"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_marginRight="10dp"
android:textColor="#android:color/black"
android:textSize="16sp"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_brief"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
android:layout_marginRight="10dp"
android:maxLines="1"
android:text="hey i am date"
android:textColor="#color/inactive_text"
android:textSize="14sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="#+id/rl_newsdate"
android:layout_weight="0.7"
android:padding="5dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_notifi"
android:layout_width="match_parent"
android:layout_height="75dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="#drawable/mer" />
</RelativeLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.cardview.widget.CardView>
</layout>
I was able to do the binding for the fragment but for adapter I am unsure how to proceed.
If I understand your question correctly, you want to pass data to your adapter inside xml. For this, you will need to write custom binding adapter for your RecyclerView.
This link has all you need.
https://android.jlelse.eu/how-to-bind-a-list-of-items-to-a-recyclerview-with-android-data-binding-1bd08b4796b4

how to binding correctly recyclerview items in kotlin?

I am developing a news app and I am following MVVM with data binding in recycler view I am trying to bind items but I am just stuck below my recyclerview items xml file
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="article"
type="yodgorbek.komilov.musobaqayangiliklari.model.Article">
</variable>
</data>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp">
<ImageView
android:text="#{article.urlToImage}"
android:id="#+id/imageView"
android:layout_width="100dp"
android:layout_height="85dp"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:contentDescription="bbc"
tools:background="#color/colorPrimary" />
<TextView
android:id="#+id/articleTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="#id/imageView"
android:layout_toRightOf="#id/imageView"
android:ellipsize="end"
android:lines="3"
android:maxLines="3"
android:text="#{article.title}" />
<ImageView
android:id="#+id/imageCategory"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_toEndOf="#id/imageView"
android:layout_toRightOf="#id/imageView"
android:src="#drawable/ic_espn"
tools:background="#color/colorPrimary" />
<TextView
android:id="#+id/articleSourceName"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_toEndOf="#id/imageCategory"
android:layout_toRightOf="#id/imageCategory"
android:gravity="center|start"
android:text="#{article.source.name}" />
<TextView
android:id="#+id/articleTime"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_below="#id/articleTitle"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="#id/articleSourceName"
android:layout_toRightOf="#id/articleSourceName"
android:gravity="center|start"
android:text="#{article.publishedAt}"
android:textColor="#android:color/darker_gray"
tools:ignore="NotSibling" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</layout>
below TopHeadlinesAdapter.kt where I am trying to implement data binding logic
#Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
class TopHeadlinesAdapter(val context: Context, private val article: List<Article>) :
RecyclerView.Adapter<TopHeadlinesAdapter.MyViewHolder>() {
private var articleList: List<Article> by Delegates.observable(emptyList()) { _, _, _ ->
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater =
LayoutInflater.from(parent.context)//.inflate(R.layout.news_list, parent, false)
// val binding = Article.inflate(inflater)
return MyViewHolder(article, parent)
}
override fun getItemCount(): Int {
return articleList.size
}
#SuppressLint("NewApi")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) =
holder.bind(articleList[position])
//holder.articleTitle.text = articleList.get(position).title
//holder.articleSourceName.text = articleList.get(position).source.name
//Picasso.get().load(articleList.get(position).urlToImage).into(holder.image)
// val input = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")
// val output = SimpleDateFormat("dd/MM/yyyy")
// var d = Date()
// try {
// d = input.parse(articleList[5].publishedAt)
// } catch (e: ParseException) {
// try {
// val fallback = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
// fallback.timeZone = TimeZone.getTimeZone("UTC")
// d = fallback.parse(articleList[5].publishedAt)
// } catch (e2: ParseException) {
// // TODO handle error
// val formatted = output.format(d)
// val timelinePoint = LocalDateTime.parse(formatted)
// val now = LocalDateTime.now()
//
// var elapsedTime = Duration.between(timelinePoint, now)
//
// println(timelinePoint)
// println(now)
// elapsedTime.toMinutes()
//
// holder.articleTime.text = "${elapsedTime.toMinutes()}"
//
//
fun updateData(newList: List<Article>) {
articleList = newList
Log.e("articleListSize", articleList?.size.toString())
}
inner class MyViewHolder(private val binding: Article) : RecyclerView.ViewHolder(binding.root) {
fun bind(article: Article) {
binding.article = article
// val image: ImageView = itemView!!.findViewById(R.id.imageView)
// val articleTitle: TextView = itemView!!.findViewById(R.id.articleTitle)
// val articleSourceName: TextView = itemView!!.findViewById(R.id.articleSourceName)
// val imageCategory: ImageView = itemView!!.findViewById(R.id.imageCategory)
// val articleTime: TextView = itemView!!.findViewById(R.id.articleTime)
}
}
}
below my Article.kt data class
data class Article(
val author: String,
val content: String,
val description: String,
val publishedAt: String,
val source: Source,
val title: String,
val url: String,
val urlToImage: String
)
below fragment_top_headlines.xml where I am hosting recyclerview
<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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="#+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
below TopHeadlinesFragment.kt
class TopHeadlinesFragment : Fragment() {
private val viewModel by viewModel<MainViewModel>()
private lateinit var topHeadlinesAdapter: TopHeadlinesAdapter
// private val newsRepository: NewsRepository by inject()
private lateinit var article:List<Article>
//3
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(
R.layout.fragment_top_headlines
, container, false
)
val recyclerView = view.findViewById(R.id.recyclerView) as RecyclerView
val pb = view.findViewById(R.id.pb) as ProgressBar
topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context, article)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()
return view
}
private fun initViewModel() {
viewModel?.sportList?.observe(this, Observer { newList ->
topHeadlinesAdapter.updateData(newList)
})
viewModel?.showLoading?.observe(this, Observer { showLoading ->
pb.visibility = if (showLoading) View.VISIBLE else View.GONE
})
viewModel?.showError?.observe(this, Observer { showError ->
(showError)
})
viewModel?.loadNews()
}
}
I want to know what I have to do in order to bind correctly recyclerview items and show correctly in my app?
try this in
class yourAdapterName(val context: Context, var listData:
MutableList<Article>) :
RecyclerView.Adapter<LanguageAdapter.ViewHolder>() {
lateinit var bindind: YourBinding
fun onRefresh(listData: MutableList<Article>) {
this.listData = listData
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
ViewHolder {
bindind = YourBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(bindind)
}
override fun getItemCount(): Int {
return listData.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.setData(listData[position])
}
inner class ViewHolder(private val binding: LangugaeItemBinding) : RecyclerView.ViewHolder(binding.getRoot()) {
fun setData(model: Article) {
with(binding) {
artical = model
executePendingBindings()
}
}
}
}
and call these into onViewCreated in fragment
val recyclerView = view.findViewById(R.id.recyclerView) as
RecyclerView
val pb = view.findViewById(R.id.pb) as ProgressBar
topHeadlinesAdapter = TopHeadlinesAdapter(recyclerView.context,
article)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = topHeadlinesAdapter
initViewModel()

RecyclerView doesn't appear when back pressed

I see the problem with a RecyclerView in viewpager fragment and I use bottom naviagation bar. When I tab on the navigation bar to change a screen and then pressed on back button it will go to the first screen which have the viewpager but the recyclerview doesn't appear and when I tab the naviagtion bar to reselect this screen it appear.
The recyclerview is show the list from firestore database and using ViewModel.
I try to set retainInstance to true but it doesn't work. And try to setup the recyclerview in onCreate and onCreateView it doesn't work too.
Edit: this is my code.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
navView.setupWithNavController(navController)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId){
android.R.id.home -> {
onBackPressed()
return true
}
}
return super.onOptionsItemSelected(item)
}
}
activity_main.xml
<?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:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="#drawable/bg_nav"
app:itemIconTint="#color/navbar_color"
app:itemTextColor="#color/navbar_color"
app:itemTextAppearanceActive="#style/navbar_font"
app:itemTextAppearanceInactive="#style/navbar_font"
app:itemBackground="#drawable/nav_ripple"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu"/>
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
HomeFragement.kt (it contain tablayout and viewpager)
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
homeViewModel =
ViewModelProviders.of(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
return root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupTab()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
private fun setupTab(){
Log.d("SET UP TAB", "SETTING UP")
val pageTabAdapter = PageTabAdapter(activity!!.supportFragmentManager, activity_tab.tabCount)
view_activity.adapter = pageTabAdapter
view_activity.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(activity_tab))
activity_tab.setOnTabSelectedListener(object : TabLayout.OnTabSelectedListener{
override fun onTabReselected(p0: TabLayout.Tab?) {
}
override fun onTabUnselected(p0: TabLayout.Tab?) {
}
override fun onTabSelected(p0: TabLayout.Tab?) {
view_activity.currentItem = p0!!.position
}
})
}
}
fragment_home.xml
<?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"
android:background="#android:color/white"
android:orientation="vertical">
<TextView
android:id="#+id/home_header"
style="#style/page_header"
android:text="#string/title_home"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="#+id/calendar_btn"
android:layout_width="60dp"
android:layout_height="40dp"
android:background="#drawable/btn_calendar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#id/home_header"
app:layout_constraintBottom_toBottomOf="#id/home_header"
android:layout_marginEnd="10dp"/>
<com.google.android.material.tabs.TabLayout
android:id="#+id/activity_tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabRippleColor="#color/colorPrimary"
app:tabTextAppearance="#style/tab_font"
app:tabIndicatorHeight="2.5dp"
app:tabGravity="fill"
app:layout_constraintTop_toBottomOf="#id/home_header"
app:layout_constraintStart_toStartOf="parent"
android:background="#android:color/white">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="กิจกรรมในเดือนนี้" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="กิจกรรมที่ผ่านไปแล้ว" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager.widget.ViewPager
android:id="#+id/view_activity"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/activity_tab"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
PageAdapter.kt
class PageTabAdapter(private val fragmentManager: FragmentManager, private val anInt: Int) :
FragmentStatePagerAdapter(fragmentManager) {
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> {
NewActivity.newInstance()
}
1 -> {
PastActivity.newInstance()
}
else -> {
null!!
}
}
}
override fun getCount(): Int {
return anInt
}
}
ActivityAdapter.kt (this is a recyclerview adapter)
class ActivityAdapter(
private val mContext: Context, private val mItems: List<ActivityModel>
): RecyclerView.Adapter<ActivityAdapter.Holder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val inflater = LayoutInflater.from(this.mContext)
val view = inflater.inflate(R.layout.activity_list, parent, false)
return Holder(view)
}
override fun getItemCount(): Int {
return this.mItems.size
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val item = this.mItems[position]
holder.setName(item.name)
val date = item.date?.toDate()
val dateFormat: DateFormat = SimpleDateFormat("dd-MMM-yyyy")
val newDate = dateFormat.format(date).toString()
holder.setDate(newDate)
val period = item.start + " - " +item.end
holder.setPeriod(period)
holder.setButton(item)
}
inner class Holder(view: View): RecyclerView.ViewHolder(view){
private var activityName: TextView = view.activity_list_name
private var activityDate: TextView = view.activity_list_date
private var activityPeriod: TextView = view.activity_list_period
private var activityPicture: ImageView = view.activity_list_pic
private var activityButton: Button = view.activity_list_btn
fun setName(name: String?){
this.activityName.text = name
}
fun setDate(date: String?){
this.activityDate.text = date
}
fun setPeriod(period: String?){
this.activityPeriod.text = period
}
fun setPicture(picpath: String?){
}
fun setButton(data: ActivityModel){
}
}
}
}
PS.: I'm using Kotlin in my project
Thank you

RecyclerView Items doesn't appear until i scroll it

I'm using Recyclerview inside a Fragment Following Google's sample of MVP Android architecture and I tried to make the View part passive as possible following this article , which makes the whole Recyclerview Adapter passive of the Data Models and the presenter handles it.
Here is my code of the Fragment:
class OrderHistoryFragment : Fragment(), OrderHistoryContract.View {
lateinit var mPresenter: OrderHistoryContract.Presenter
lateinit var rvOrderHistory: RecyclerView
lateinit var orderHistoryAdapter : OrderHistoryAdapter
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val root = inflater!!.inflate(R.layout.order_history_fragment, container, false)
rvOrderHistory = root.findViewById<RecyclerView>(R.id.rvOrderHistory)
rvOrderHistory.layoutManager = LinearLayoutManager(context, LinearLayout.VERTICAL, false)
orderHistoryAdapter = OrderHistoryAdapter(mPresenter, object : HistoryItemListener {
override fun onReorder(orderHistory: OrderHistory) {
}
override fun onOpenOrder(orderHistory: OrderHistory) {
val orderIntent = Intent(activity, OrderDetailActivity::class.java)
orderIntent.putExtra("orderId", orderHistory.id)
startActivity(orderIntent)
}
})
rvOrderHistory.adapter = orderHistoryAdapter
return root
}
override fun onResume() {
super.onResume()
mPresenter.start()
}
override fun setPresenter(presenter: OrderHistoryContract.Presenter) {
mPresenter = checkNotNull<OrderHistoryContract.Presenter>(presenter)
}
override fun showLoadingIndicator(load: Boolean?) {
}
override fun updateOrdersAdapter() {
orderHistoryAdapter.notifyDataSetChanged()
}
override fun showSnackBar(Message: String) {
val parentLayout = activity.findViewById<View>(android.R.id.content)
val snackBar = Snackbar
.make(parentLayout, Message, Snackbar.LENGTH_INDEFINITE)
snackBar.setAction("Dismiss") { snackBar.dismiss() }
snackBar.setActionTextColor(Color.RED)
snackBar.show()
}
interface HistoryItemListener {
fun onReorder(orderHistory: OrderHistory)
fun onOpenOrder(orderHistory: OrderHistory)
}
companion object {
fun newInstance(): OrderHistoryFragment {
return OrderHistoryFragment()
}
}
fun OrderHistoryFragment() {
}
}
And this is my RecyclerView Adapters code
class OrderHistoryAdapter(internal var orderHistoryPresenter: OrderHistoryContract.Presenter, private val listener: OrderHistoryFragment.HistoryItemListener) : RecyclerView.Adapter<OrderHistoryAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.order_history_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
orderHistoryPresenter.onBindOrdersRow(position, holder)
holder.bReOrder!!.setOnClickListener { v -> listener.onReorder(orderHistoryPresenter.getOrderHistoryItem(position)) }
holder.cvOrderItem!!.setOnClickListener { v -> listener.onOpenOrder(orderHistoryPresenter.getOrderHistoryItem(position)) }
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return orderHistoryPresenter.getOrdersCount()
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), OrderHistoryContract.orderRowView {
internal var ivOrderVendor: ImageView? = null
internal var tvOrderId: TextView? = null
internal var tvOrderItems: TextView? = null
internal var tvOrderDate: TextView? = null
internal var tvOrderPrice: TextView? = null
internal var bReOrder: Button? = null
internal var cvOrderItem: CardView? = null
init {
ivOrderVendor = itemView.findViewById<ImageView>(R.id.ivOrderVendor)
tvOrderId = itemView.findViewById<TextView>(R.id.tvOrderId)
tvOrderItems = itemView.findViewById<TextView>(R.id.tvOrderItems)
tvOrderDate = itemView.findViewById<TextView>(R.id.tvOrderDate)
tvOrderPrice = itemView.findViewById<TextView>(R.id.tvOrderPrice)
bReOrder = itemView.findViewById<Button>(R.id.bReOrder)
cvOrderItem = itemView.findViewById<CardView>(R.id.cvOrderItem)
}
override fun setOrderImage(url: String) {
Glide.with(itemView.context).load(url).into(ivOrderVendor!!)
}
override fun setOrderDate(orderDate: String) {
tvOrderDate!!.text = orderDate
}
override fun setOrderId(orderId: String) {
tvOrderId!!.text = orderId
}
override fun setOrderItems(orderItems: ArrayList<String>) {
val stringBuilder = StringBuilder()
for (item in orderItems) {
stringBuilder.append(item)
}
tvOrderItems!!.text = stringBuilder.toString()
}
override fun setOrderPrice(orderPrice: String) {
tvOrderPrice!!.text = R.string.price.toString() + " " + orderPrice + " " + R.string.egp
}
}
}
And Here is code of the presenter which handles the Adapter data and it's binding to the ViewHolder
class OrderHistoryPresenter internal constructor(mDataRepository: DataRepository, mOrdeHistoryView: OrderHistoryContract.View) : OrderHistoryContract.Presenter {
private val mDataRepository: DataRepository
//refrence of the View to trigger the functions after proccessing the task
private val mOrdeHistoryView: OrderHistoryContract.View
private var orderHistoryItems = ArrayList<OrderHistory>()
init {
this.mDataRepository = checkNotNull(mDataRepository, "tasksRepository cannot be null")
this.mOrdeHistoryView = checkNotNull<OrderHistoryContract.View>(mOrdeHistoryView, "tasksView cannot be null!")
mOrdeHistoryView.setPresenter(this)
}
override fun start() {
mOrdeHistoryView.showLoadingIndicator(true)
mDataRepository.getCurrentUser(object : LocalDataSource.userRequestCallback {
override fun onUserRequestSuccess(botitUser: BotitUser) {
val urlParams = HashMap<String, String>()
urlParams.put(Endpoints.USER_ID_KEY, botitUser.userId!!)
val url = Endpoints.getUrl(Endpoints.urls.ORDER_HISTORY, urlParams)
mDataRepository.buildEndPointRequest(url, " ", Endpoints.requestsType.GET, object : EndpointDataSource.RequestCallback {
override fun onRequestSuccess(Body: String) {
try {
mOrdeHistoryView.showLoadingIndicator(false)
orderHistoryItems = JSONParser.parseData(JSONParser.parsers.ORDER_HISTORY, JSONObject(Body)) as ArrayList<OrderHistory>
mOrdeHistoryView.updateOrdersAdapter()
} catch (e: JSONException) {
e.printStackTrace()
}
}
override fun onRequestError(Body: String) {
mOrdeHistoryView.showLoadingIndicator(false)
mOrdeHistoryView.showSnackBar("Cannot load data")
}
})
}
override fun onUserRequestError(Body: String) {
}
})
}
override fun refreshData() {
}
override fun getOrdersCount(): Int {
return orderHistoryItems.size
}
override fun onBindOrdersRow(position: Int, orderViewHolder: OrderHistoryContract.orderRowView) {
if (orderHistoryItems.isNotEmpty()) {
val orderHistory = orderHistoryItems[position]
// orderViewHolder.setOrderDate(orderHistory.orderDate!!)
orderViewHolder.setOrderId(orderHistory.orderId!!)
orderViewHolder.setOrderImage(orderHistory.orderImage!!)
orderViewHolder.setOrderItems(orderHistory.orderItems)
orderViewHolder.setOrderPrice(orderHistory.orderPrice!!)
}
}
override fun getOrderHistoryItem(position: Int): OrderHistory {
return orderHistoryItems[position]
}
override fun actionReOrder(ordreId: String) {
}
}
Here is the Fragment XML
<android.support.v7.widget.RecyclerView android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/rvOrderHistory"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
</android.support.v7.widget.RecyclerView>
and Here is the RecyclerView Item XML order_history_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/cvOrderItem"
android:layout_margin="4dp"
android:orientation="vertical">
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp">
<ImageView
android:id="#+id/ivOrderVendor"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/mac" />
<TextView
android:id="#+id/tvOrderId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Order #2123"
android:textAppearance="#style/TextAppearance.AppCompat.Body2"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toRightOf="#+id/ivOrderVendor"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tvOrderItems"
android:layout_width="242dp"
android:layout_height="35dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="MacDonald’s: Big Mac Beef, Big Tasty Beef. El Ezaby: Signal 2, Pantene Shampoo"
android:textAppearance="#style/TextAppearance.AppCompat.Small"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/tvOrderId"
app:layout_constraintLeft_toRightOf="#+id/ivOrderVendor"
android:layout_marginLeft="8dp"
app:layout_constraintHorizontal_bias="0.0" />
<TextView
android:id="#+id/tvOrderDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:text="03:22 PM 23/2/2017"
app:layout_constraintBottom_toTopOf="#+id/tvOrderItems"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintHorizontal_bias="1.0" />
<TextView
android:id="#+id/tvOrderPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:text="Price: 225.50 LE"
android:textAppearance="#style/TextAppearance.AppCompat.Body2"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvOrderItems" />
<Button
android:id="#+id/bReOrder"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_margin="4dp"
android:background="#drawable/chip_accent"
android:foreground="?attr/selectableItemBackground"
android:orientation="vertical"
android:padding="8dp"
android:text="Order Again"
android:textAllCaps="false"
android:textColor="#color/colorAccent"
android:textSize="15sp"
app:layout_constraintHorizontal_bias="0.937"
app:layout_constraintLeft_toRightOf="#+id/tvOrderPrice"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvOrderItems"
tools:layout_editor_absoluteY="74dp" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
The issue is that when I start the Activity showing the Fragment, the RecyclerView doesn't show the Item. It only appears if I scrolled the empty RecyclerView or by leaving the App on the foreground and go back to it again.
At the initialization the data of the Adapter is empty but onResume() I make a request which updates the data at the Presenter, Then I notifyDataChange() on the Adapter but nothing updates.
When I debugged I found that onBindViewHolder() isn't called after notifyDataChange() on the Adapter so i don't know why the notifyDataChange() doesn't notify the Adapter that the data is changed.
Anyone has an idea or any solution that might fix this issue?
You need to use runOnUiThread.
if(activity != null) {
activity!!.runOnUiThread {
root.Recycleview.adapter = Adapter(Array)
Adapter(Array).notifyDataSetChanged()
}
}
have a look at this answer
As stupid as it might sound, calling this line of code after setting data for recyclerView, helped me for this issue:
recyclerView.smoothScrollToPosition(0)
PS: technologies that I was using that may have something to do with this were: RJava, Retrofit2, NavigationUI, Fragments, LiveData, and Databinding.
EDIT:
I followed #SudoPlz comment and answer on another question and it also worked too, you have to extend RecyclerView and override requestLayout:
private boolean mRequestedLayout = false;
#SuppressLint("WrongCall")
#Override
public void requestLayout() {
super.requestLayout();
// We need to intercept this method because if we don't our children will never update
// Check https://stackoverflow.com/questions/49371866/recyclerview-wont-update-child-until-i-scroll
if (!mRequestedLayout) {
mRequestedLayout = true;
this.post(() -> {
mRequestedLayout = false;
layout(getLeft(), getTop(), getRight(), getBottom());
onLayout(false, getLeft(), getTop(), getRight(), getBottom());
});
}
}
while still, I would have preferred this to fixed after 4, 5 years, however, this was a good workaround, and you won't forget about them in your view.
I noticed in your item xml your constraintLayout height is match_parent right?
I recommend you to use it as wrap_content

Categories

Resources