I have a recycler view and I am passing data to the recyclerviewadapter but recycler view does not show any data.
ProdyctsRecyclerViewAdapter - it is getting the data in setList but does not display it
Where am I doing it wrong please
Thanks
R
class ProductsRecyclerViewAdapter(private val clickListener: (Product) -> Unit): RecyclerView.Adapter<ProductsMyViewHolder>() {
private val productsList = ArrayList<Product>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductsMyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding: ListItemBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.products_list_item, parent, false)
return ProductsMyViewHolder(binding)
}
override fun getItemCount(): Int {
return productsList.size
}
override fun onBindViewHolder(holder: ProductsMyViewHolder, position: Int) {
holder.bind(productsList[position], clickListener)
}
fun setList(products: List<Product>) {
productsList.clear()
productsList.addAll(products)
}
}
class ProductsMyViewHolder(val binding: ListItemBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(product: Product, clickListener: (Product) -> Unit) {
binding.nameTextView.text = product.name
binding.emailTextView.text = product.catagory
binding.listItemLayout.setOnClickListener {
clickListener(product)
}
}
}
ProductsFragment
class ProductsFragment: Fragment() {
private lateinit var binding: ProductsBinding
private lateinit var navController: NavController
private lateinit var productsViewModel: ProductsViewModel
private lateinit var adapter: ProductsRecyclerViewAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.products, container, false)
val dao = SubscriberDatabase.getInstance(requireActivity().applicationContext).productDAO
val repository = ProductRepository(dao)
val factory = ProductsViewModelFactory(repository, requireActivity().applicationContext)
productsViewModel = ViewModelProvider(this, factory).get(ProductsViewModel::class.java)
binding.productsViewModel = productsViewModel
binding.lifecycleOwner = this
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
initRecyclerView()
productsViewModel.navigateScreen.observe(viewLifecycleOwner, EventObserver {
navController.navigate(it)
})
}
private fun initRecyclerView() {
binding.productsRecyclerView.layoutManager = LinearLayoutManager(context)
adapter = ProductsRecyclerViewAdapter ({ selectedItem: Product -> listItemClicked(selectedItem)})
displayProductssList()
}
private fun displayProductssList() {
productsViewModel.products.observe(viewLifecycleOwner, Observer {
Log.i("MYTAG", it.toString())
adapter.setList(it)
adapter.notifyDataSetChanged()
})
}
private fun listItemClicked(product: Product) {
Toast.makeText(context, "Selected name is ${product.name}", Toast.LENGTH_LONG).show()
//productsViewModel.initUpdateAndDelete(subscriber)
}
}
ProductsViewModel
class ProductsViewModel (
private val repository: ProductRepository,
private val context: Context
): ViewModel() {
val products = repository.products
}
Products_list_item
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="#color/colorPrimary"
app:cardCornerRadius="10dp"
app:cardElevation="10dp" >
<LinearLayout
android:id="#+id/product_list_item_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/product_name_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="name"
android:textColor="#FFFFFF"
android:textSize="30sp"
android:textStyle="bold" />
<TextView
android:id="#+id/product_catagory_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="catagory"
android:textColor="#FFFFFF"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</layout>
Thank you Parag Pawar for the answer
For reference made the followingn changes
private fun initRecyclerView() {
binding.productsRecyclerView.layoutManager = LinearLayoutManager(context)
adapter = ProductsRecyclerViewAdapter ({ selectedItem: Product -> listItemClicked(selectedItem)})
binding.productsRecyclerView.adapter = adapter //ADDED THIS LINE
displayProductssList()
}
In productsRecyclerView, had the wrong binding it should be ProductsListItemBinding
val binding: ProductsListItemBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.products_list_item, parent, false)
ProductsMyViewHolder(val binding: ProductsListItemBinding):
You've not set the adapter to recycler view. In your initRecyclerView() set the adapter after initializing it.
private fun initRecyclerView() {
binding.productsRecyclerView.layoutManager = LinearLayoutManager(context)
adapter = ProductsRecyclerViewAdapter ({ selectedItem: Product -> listItemClicked(selectedItem)})
//notice this
binding.productsRecyclerView.adapter = adapter
displayProductssList()
}
add notifyDataSetChanged() method in your Adapter's setList() method
I don't understand why are you clearing your list and adding them at the same time.
fun setList(products: List<Product>) {
productsList.clear()
productsList.addAll(products)
}
if you want to first clear your list you should do it outside the loop
Related
I am new to mobile development with kotlin and I am trying to add a new item to the recyclerview.
The item is added to the recycler view but the text is not visible. I don't get what it's wrong.
I am using Kotlin in Android Studio 4.2
The class AgentAttributes only contains two properties:val Name:String ,val Value:String
**activity_main.xml**
<EditText
android:id="#+id/etSearch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="No emp"
app:layout_constraintEnd_toStartOf="#+id/btnSearch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/btnSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#android:string/search_go"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvAgentAttributes"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/etSearch" />
**MainActivity.kt**
private lateinit var agentAttributesAdapter:AgentAttributesAdapter
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//setContentView(R.layout.activity_main)
//constructor
agentAttributesAdapter = AgentAttributesAdapter(mutableListOf())
//Definition of Recycler view adapter
binding.rvAgentAttributes.adapter = agentAttributesAdapter
binding.rvAgentAttributes.layoutManager = LinearLayoutManager(this)
//THIS IS THE WAY THAT I AM TRYING TO ADD A NEW ITEM
val agentAttr = AgentAttributes("Nombre","JOEL ROMUALDO LOPEZ SALIDO")
agentAttributesAdapter.addAttributes(agentAttr)
**AgentAttributesAdapter**
class AgentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AgentViewHolder {
return AgentViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.agent_details,
parent,
false
)
)
override fun onBindViewHolder(holder: AgentViewHolder, position: Int) {
val layoutInflater = LayoutInflater.from(holder.itemView.context)
binding = AgentDetailsBinding.inflate(layoutInflater)
//val view = binding.root
val currentAgent = agent_attributes[position]
holder.itemView.apply {
binding.tvAttribute.text = currentAgent.Name.toString()
}
}
override fun getItemCount(): Int {
return agent_attributes.size
}
fun addAttributes(attr:AgentAttributes){
agent_attributes.add(attr)
notifyItemInserted(agent_attributes.size - 1)
}
There's no need to inflate your layout in the "binding" step:
override fun onBindViewHolder(viewHolder: ChoicesViewHolder, pos: Int) {
AgentDetailsBinding.bind(viewHolder.itemView).apply {
val currentAgent = agent_attributes[position]
tvAttribute.text = currentAgent.Name.toString()
}
}
PS: variables should be lower case, so currentAgent should have a name, no Name
I'm using MVVC pattern and I'm populating a recyclerView with data from database using Room. At the Logcat, data return correctly and is looped correctly, but recyclerview display seven elements and in the eighth starts to overwrite it with the nineth and tenth elements e after that create 2 more elements with the first 2 elements from list.
I'm coudn't find what is wrong with my code.
So, I'm asking for some help.
AvaliacaoFragment.kt:
class AvaliacaoFragment : Fragment() {
private lateinit var ctx: Context
private var _binding: FragmentAvaliacaoBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private lateinit var textoSemSecoes: TextView
private lateinit var nomeAvaliacao: TextView
private lateinit var dataAvaliacao: TextView
private val args by navArgs<AvaliacaoFragmentArgs>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
_binding = FragmentAvaliacaoBinding.inflate(inflater, container, false)
return binding.root
// return inflater.inflate(R.layout.fragment_avaliacoes, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ctx = view.context
// dialogNovaAvaliacao = MaterialAlertDialogBuilder(ctx, android.R.style.Theme_DeviceDefault_Light_NoActionBar_Fullscreen)
// dialogNovaAvaliacao = MaterialAlertDialogBuilder(ctx,R.style.AlertDialogTheme)
// val builder = MaterialDatePicker.Builder.datePicker()
textoSemSecoes = view.findViewById(R.id.texto_sem_secoes)
nomeAvaliacao = view.findViewById(R.id.nome_avaliacao)
dataAvaliacao = view.findViewById(R.id.data_avaliacao)
nomeAvaliacao.text = args.currentAvaliacao.nome
dataAvaliacao.text = args.currentAvaliacao.dataCriacao
/*
btnAddAvaliacao = view.findViewById(R.id.btn_add_avaliacao)
btnAddAvaliacao.setOnClickListener {
findNavController().navigate(R.id.action_navigation_avaliacoes_to_addAvaliacaoFragment)
}
*/
// Recycler
val recyclerAdapter = SecaoAdapter()
val recyclerView = binding.secaoRecyclerView
recyclerView.adapter = recyclerAdapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
// ViewModelFactory para passar argumentos para a ViewModel
val factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return SecaoViewModel(Application(), args.currentAvaliacao.id) as T
}
}
// ViewModel
mSecaoViewModel = ViewModelProvider(this, factory).get(SecaoViewModel::class.java)
mSecaoViewModel.readAllData.observe(viewLifecycleOwner, Observer { secaoList ->
if(secaoList.isNotEmpty()){
Log.d(TAG, "secaoList: ${secaoList.toString()}")
Log.d(TAG, "secaoList.size: ${secaoList.size}")
textoSemSecoes.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
recyclerAdapter.setData(secaoList.sortedBy { it.codigo.toInt() })
} else {
textoSemSecoes.visibility = View.VISIBLE
recyclerView.visibility = View.GONE
}
})
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
private val TAG: String = AvaliacaoFragment::class.java.name
lateinit var mSecaoViewModel: SecaoViewModel
private lateinit var btnAddSecao: FloatingActionButton
private lateinit var dialogNovaSecao: MaterialAlertDialogBuilder
}
}
SecaoAdapter.kt:
class SecaoAdapter: RecyclerView.Adapter<SecaoAdapter.SecaoViewHolder>() {
private val TAG: String = SecaoAdapter::class.java.name
private var secaoList = emptyList<Secao>()
private lateinit var binding: ItemSecaoBinding
class SecaoViewHolder(itemBinding: ItemSecaoBinding): RecyclerView.ViewHolder(itemBinding.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SecaoViewHolder {
binding = ItemSecaoBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SecaoViewHolder(binding)
}
override fun getItemCount(): Int {
return secaoList.size
}
override fun onBindViewHolder(holder: SecaoViewHolder, position: Int) {
Log.d(TAG, "position: $position")
val currentItem = secaoList[position]
Log.d(TAG, "currentItem: ${currentItem.toString()}")
binding.secaoCodigo.text = currentItem.codigo
binding.secaoNome.text = currentItem.nome
binding.secaoMediaTotal.text = currentItem.mediaPositivo.toString()
binding.secaoPerguntasNaoAplicaveis.text = currentItem.qdePerguntasNaoAplicaveis.toString()
binding.secaoPerguntasRespondidas.text = currentItem.qdePerguntasRespondidas.toString()
binding.secaoPerguntasTotais.text = currentItem.qdePerguntas.toString()
binding.cardSecao.setOnClickListener {
val action = AvaliacaoFragmentDirections.actionAvaliacaoFragmentToSecaoFragment(currentItem)
holder.itemView.findNavController().navigate(action)
}
}
fun setData(secao: List<Secao>){
this.secaoList = secao
notifyDataSetChanged()
}
}
fragment_avaliacao.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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimaryDark"
tools:context=".ui.secoes.AvaliacaoFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/container_titulo_avaliacao"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
>
<TextView
android:id="#+id/nome_avaliacao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="#string/padrao_avaliacao_sem_nome"
android:textSize="24sp"
android:textColor="#color/colorIcons"
android:textAlignment="center"
/>
<TextView
android:id="#+id/data_avaliacao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="#string/padrao_formato_data_hora"
android:textSize="14sp"
android:textColor="#color/colorPrimaryLight"
android:textAlignment="center"
/>
</LinearLayout>
<TextView
android:id="#+id/texto_sem_secoes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/container_titulo_avaliacao"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="16sp"
android:text="#string/nenhuma_secao_criada"
android:textColor="#color/colorPrimaryLight"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/secaoRecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#+id/container_titulo_avaliacao"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout></androidx.constraintlayout.widget.ConstraintLayout>
Logcat loop from secaoList at the SecaoAdapter (is looping correctly to all elements):
I create a gif to show what is happening with the elements on recyclerView:
Here you can see that it displays seventh element then eighth is replaced by nineth and subsequently replaced by tenth. And final two elements (that should be ninth and tenth) is constructed with first and second list elements.
Thanks for the help in advance.
So the mistake here is that you are referencing only one binding in your adapter which is getting overwritten. Every time you call onCreateViewHolder you are changing the binding reference. The reason this looks okay to start with is that the onCreateViewHolder calls are followed by the onBindViewHolder calls for items visible on the screen. However as you scroll, just onBindViewHolder is called in order to rebind the recycled views.
What you should be doing is using your ViewHolder to store the individual bindings and then obtaining a reference in onBindViewHolder with something like holder.binding.
I would recommend you have a read into the view holder pattern and how to implement it!
I want to bind data to my recylerview adapter. This is my current code following the MVVM pattern
Fragment
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) {
val notificationAdapter = NotificationAdapter(response.dataList as List<Data>)
notificationAdapter.notifyDataSetChanged()
binding.notification.adapter = notificationAdapter
}
})
}
}
View model
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 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 binding = ElementListBinding.inflate(inflater)
// val view = inflater.inflate(R.layout.element_list, parent, false)
return ViewHolder(binding)
}
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(binding: ElementListBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(movie: Data) {
binding.item = movie
}
}
}
unable to find binding object
the recylerview element list xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="data"
type="com.mountmeru.model.Data" />
</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="#{data.displayName}"
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>
Can someone confirm me is my implementation of MVVM correct or it needs some refactoring?
How do I make of data binding in my recyclerview list element xml?
You have already used <layout> as parent tag in element_list.xml. Now you can inflate it in the adapter class using DataBinding. See the example below:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ElementListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(Binding)
}
You have to modify your ViewHolder class as well as shown below:
inner class ViewHolder(val binding: ElementListBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(movie: Data) {
with(itemView) {
binding.tvNotifi.text = movie.displayName
binding.tvDate.text = movie.UpdatedDate
if (movie.description != null) {
binding.tvBrief.text = movie.description
binding.tvBrief.visibility = View.VISIBLE
}
}
}
}
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
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()