Recycler view is not scrolling smoothly - Cardview - android

Hi I am using a recycler view with a card view, I have no idea why it is not scrolling smoothly. it stuck for a few seconds and shows the next content.
This is my xml for list item
<?xml version="1.0" encoding="utf-8"?>
<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/colorAccent"
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" />
<TextView
android:id="#+id/product_end_date_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="End Date"
android:textColor="#FFFFFF"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
this is my recyclerViewAdapter
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: ProductsListItemBinding =
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: ProductsListItemBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(product: Product, clickListener: (Product) -> Unit) {
binding.productNameTextView.text = product.name
binding.productCatagoryTextView.text = product.catagory
binding.productEndDateTextView.text = convertLongToTime(product.end_date)
binding.productListItemLayout.setOnClickListener {
clickListener(product)
}
}
fun convertLongToTime(time: Long): String {
val date = Date(time)
val format = SimpleDateFormat("dd MMM yyyy")
return format.format(date)
}
}
This is my fragment where i initialise the ProductsRecyclerViewAdapter
private fun initRecyclerView() {
binding.productsRecyclerView.layoutManager = LinearLayoutManager(context)
adapter = ProductsRecyclerViewAdapter ({ selectedItem: Product -> listItemClicked(selectedItem)})
binding.productsRecyclerView.adapter = adapter
displayProductssList()
}
private fun displayProductssList() {
productsViewModel.products.observe(viewLifecycleOwner, Observer {
Log.i("MYTAG", it.toString())
adapter.setList(it)
adapter.notifyDataSetChanged()
})
}
any thoughts what I might be doing wrong here
your suggestion would be very helpful
thanks in advance
R

The only thing I see in your code is creating new SimpleDateFormat on every bind, it is definitely not free. Create single instance in adapter.
Also, maybe your products observer executes too often?

Related

How to get currently selected item position in RecyclerView?

How can i get item position of item selected with checkBox in RecyclerView ? here you can see how it looks like
TaskRecyclerAdapter.kt
class TaskRecycleAdapter: RecyclerView.Adapter<TaskRecycleAdapter.MyViewHolder>() {
private var list = emptyList<Data>()
private lateinit var mListener : OnItemClickListener
class MyViewHolder(binding: TaskHolderBinding, listener: OnItemClickListener): RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
listener.onItemClick(adapterPosition)
}
}
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: OnItemClickListener) {
mListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(TaskHolderBinding.inflate(LayoutInflater.from(parent.context), parent, false), mListener)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = list[position]
holder.itemView.findViewById<TextView>(R.id.task).text = item.task
holder.itemView.findViewById<TextView>(R.id.taskDescription).text = item.task_Details
}
override fun getItemCount(): Int {
return list.size
}
fun setData(newList: List<Data>) {
val diffUtil = DiffUtil(list, newList )
val diffResults = calculateDiff(diffUtil)
list = newList
diffResults.dispatchUpdatesTo(this)
}
fun getTaskAt(position: Int): Data {
return list[position]
}
}
task_holder.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/taskHolder"
app:cardCornerRadius="15dp"
android:backgroundTint="#color/card"
app:cardElevation="3dp"
app:contentPadding="3dp"
android:layout_marginTop="7dp"
android:layout_marginBottom="7dp"
android:layout_marginEnd="3dp"
android:layout_marginStart="3dp" >
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="88dp">
<ImageView
android:id="#+id/imageView"
android:layout_width="80dp"
android:layout_height="80dp"
android:contentDescription="#string/task_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/task"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginStart="98dp"
android:layout_marginTop="4dp"
android:contentDescription="#string/task"
android:textAlignment="center"
android:textSize="30sp"
android:textStyle="bold|italic"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="#+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/taskDescription"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginEnd="100dp"
android:layout_marginBottom="4dp"
android:contentDescription="#string/task"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="#+id/imageView"
app:layout_constraintTop_toBottomOf="#+id/task"
app:layout_constraintVertical_bias="1.0" />
<CheckBox
android:id="#+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.924"
app:layout_constraintStart_toEndOf="#+id/task"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
I'm Java Developer so I cannot code with Kotlin but I'll show you how to get any view's position in RecyclerView
holder.itemView.setOnClickListener(view -> {
Toast.makeText(context, "This is position of your Every ItemViews", Toast.LENGTH_SHORT).show();
});
In your onBindViewHolder use this code holder.itemView.setOnClickListener and create new click listener. Here you can get the position of your items by coding and you can check by adding Toast.
your code lookalike below:-
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = list[position]
holder.itemView.findViewById<TextView>(R.id.task).text = item.task
holder.itemView.findViewById<TextView>(R.id.taskDescription).text = item.task_Details
holder.itemView.setOnClickListener(view -> {
Toast.makeText(context, "This is position of your Every ItemViews", Toast.LENGTH_SHORT).show();
});
}
Make sure to change JAVA to Kotlin of my code. If you any problem is causing, Let me know

Fetching all edit text from all input box of nested recylerview is fetching latest recylerview item instead all data

I am adding code here where i am trying to get all input field value (all nested recyler view items) on textview click . So if I have 2 parent recylerview and inside first parent 3 child recyler item and inside second parent recylerview 4 child item. then how i can fetch all 7 item details on textview click of parent or from mainactivity. I am only getting last added 4 item while first parent items are not fetched. what i am missing? Thanks in advance.
//Main class
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
createMockData()
}
#SuppressLint("WrongConstant")
private fun bindDataWithUi(itemData: ArrayList<HashMap<String, String>>) {
// Create vertical Layout Manager
val locationDatesList = findViewById<RecyclerView>(R.id.locationDatesList)
val tv = findViewById<TextView>(R.id.tv)
locationDatesList.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
// Access RecyclerView Adapter and load the data
val adapter = MainAdapter(itemData)
locationDatesList.adapter = adapter
tv.setOnClickListener(View.OnClickListener {
try {
var data = adapter.getAllItemDetails()
for(result in data){
System.out.println(result)
}
}catch (e:Exception){
e.printStackTrace()
}
})
}
private fun createMockData() {
// Initialize test locations
val items: ArrayList<HashMap<String, String>> = ArrayList()
val time = System.currentTimeMillis()
// Load items into ArrayList
items.add(hashMapOf("A" to "B", "C" to "D", "time" to time.toString()))
val tomorrowTime = time + (1000 * 60 * 60 * 24 * 3)
items.add(hashMapOf("E" to "F", "G" to "H", "time" to tomorrowTime.toString()))
// Bind items to RecyclerView
bindDataWithUi(items)
}
}
//parent adapter
class MainAdapter(var locationList: ArrayList<HashMap<String, String>>):
RecyclerView.Adapter<MainAdapter.ViewHolder>() {
val byDates = locationList.groupBy { it["time"] }
lateinit var childAdapter : PropertyAdapter
lateinit var parentItemHolder: MainAdapter.ViewHolder
#SuppressLint("WrongConstant")
override fun onBindViewHolder(holder: MainAdapter.ViewHolder, position: Int) {
// Update date label
parentItemHolder = holder
val sdf = SimpleDateFormat("MM/dd/yyyy")
val dateList = byDates.values.toMutableList()
holder.date?.text = sdf.format(dateList[position][0].get("time")?.toLong())
holder.childRv?.layoutManager = LinearLayoutManager(holder.childRv.context, LinearLayout.VERTICAL, false)
var properyDataList:ArrayList<propertyDataClass> = ArrayList<propertyDataClass>()
// Create vertical Layout Manager
holder.date?.setOnClickListener(View.OnClickListener {
// Access RecyclerView Adapter and load the data
properyDataList.add(propertyDataClass())
childAdapter = PropertyAdapter(properyDataList)
holder.childRv?.adapter = childAdapter
childAdapter.updateList(propertyDataClass())
})
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item_dates, parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return byDates.count()
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val date = itemView.findViewById<TextView>(R.id.locationDate)
val childRv = itemView.findViewById<RecyclerView>(R.id.childList)
}
interface onClickItemAction{
public fun productItemList() {
}
}
fun getAllItemDetails():ArrayList<String>{
var stringList = ArrayList<String>()
try {
for (i in 0 until locationList.size) {
//val view = parentItemHolder.childRv.findViewHolderForLayoutPosition(i)//locationDatesList
//val view = parentItemHolder.childRv.findViewHolderForAdapterPosition(i);
val view = parentItemHolder.childRv.findViewHolderForLayoutPosition(i)
val etLabel: EditText? = view?.itemView?.findViewById(R.id.etLabel)
val etValue: EditText? = view?.itemView?.findViewById(R.id.etValue)
//stringList.add(etLabel?.text.toString())
stringList.add(etValue?.text.toString())
}
}catch (e:Exception){
e.printStackTrace()
}
return stringList
}
}
//child adaoter
class PropertyAdapter(
val propertyList: ArrayList<propertyDataClass>,
): RecyclerView.Adapter<PropertyAdapter.ViewHolder>() {
lateinit var vHolder :ViewHolder
override fun onBindViewHolder(holder: PropertyAdapter.ViewHolder, position: Int) {
vHolder = holder
val propertyItem = propertyList[position]
holder.etLabel?.text = propertyItem.inputName
holder.etValue?.text = propertyItem.inputValue
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PropertyAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item_locations, parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return propertyList.size
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val etLabel = itemView.findViewById<TextView>(R.id.etLabel)
val etValue = itemView.findViewById<TextView>(R.id.etValue)
}
fun updateList(propertyItem: propertyDataClass){
//propertyList.add(propertyDataClass())
notifyDataSetChanged()
}
fun getAllItemDetails(pos:Int){
val propertyListValue = ArrayList<propertyDataClass>()
for (i in 0 until propertyList.size){
var propertyDataClass = propertyDataClass()
}
}
fun getItemDetailsbyView(rootView: View){
var stringList = ArrayList<String>()
for (i in propertyList){
//val view = vHolder.findViewHolderForLayoutPosition(i)
//val etLabel: EditText? = view?.itemView?.findViewById(R.id.etLabel)
//val etValue: EditText? = view?.itemView?.findViewById(R.id.etValue)
//stringList.add(etLabel?.text.toString())
//stringList.add(etValue?.text.toString())
}
}
}
//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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/tv"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="#string/app_name"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/locationDatesList"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="8dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/locationDatesList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp" >
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
//parent recylerview
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/locationDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:gravity="center_vertical"
android:text="Date"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Large"
android:textColor="#009688"
android:textSize="18sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/childList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
//child recylerview
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/locationBadge"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#drawable/ic_launcher_background"
android:gravity="center_vertical|center_horizontal"
android:text="✓"
android:textColor="#fff"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<EditText
android:id="#+id/etLabel"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="Name"
android:maxLines="1"
android:editable="false"
android:focusable="false"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Large"
android:textSize="18sp" />
<EditText
android:id="#+id/etValue"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="Address"
android:maxLines="1"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Medium"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
just needed to hold the view holder in seprate arraylist then finding viewholder by position solved my problem.
in main adapter I added and fetched after click.
viewHolderList.add(holder)
fun getAllItemDetails():ArrayList<String>{
var stringList = ArrayList<String>()
try {
for (i in 0 until locationList.size) {
//val view = parentItemHolder.childRv.findViewHolderForLayoutPosition(i)//locationDatesList
var viewHolder = viewHolderList.get(i)
for (i in 0 until 3) {
val view = viewHolder.childRv.findViewHolderForAdapterPosition(i);
//val view = parentItemHolder.childRv.findViewHolderForLayoutPosition(i)
val etLabel: EditText? = view?.itemView?.findViewById(R.id.etLabel)
val etValue: EditText? = view?.itemView?.findViewById(R.id.etValue)
//stringList.add(etLabel?.text.toString())
stringList.add(etValue?.text.toString())
}
}
}catch (e:Exception){
e.printStackTrace()
}
return stringList
}

Why onClick method don't call for ratingBar when i'm using viewmodel?

I have a recyclerView and every elements from it has a ratingBar. I try to take the rating the user will give for every elements.
I am using MVVM architecture.
This is my code now:
<RatingBar
android:id="#+id/rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5"
android:stepSize="1"
android:onClick="#{(view) -> listener.rateMovie(view, model)}"
app:layout_constraintTop_toBottomOf="#id/movie_poster"
app:layout_constraintEnd_toEndOf="#id/movie_poster"
app:layout_constraintStart_toStartOf="#id/movie_poster"
/>
override fun rateMovie(
ratingBar: View,
pickedMovieItemViewModel: PickedMovieItemViewModel
) {
ratingBar as RatingBar
val stars: Int = ratingBar.numStars
pickedMovieItemViewModel.rating.set(stars)
}
This is where I initilized the listener:
#BindingAdapter("rated_movies", "listener")
#JvmStatic
fun setItems(
recyclerView: RecyclerView,
items: List<PickedMovieItemViewModel>?,
rateListener: RateListener?
) {
var adapter = recyclerView.adapter
if (adapter == null) {
adapter = RateMoviesAdapter()
recyclerView.adapter = adapter
val layoutManager: RecyclerView.LayoutManager =
LinearLayoutManager(recyclerView.context, LinearLayoutManager.HORIZONTAL, false)
recyclerView.layoutManager = layoutManager
}
if (items != null) {
adapter as RateMoviesAdapter
adapter.updateItems(items)
}
if (items != null && rateListener != null) {
adapter as RateMoviesAdapter
adapter.updateListener(rateListener)
}
}
And this is the adapter for recycler-view
class RateMoviesAdapter : RecyclerView.Adapter<RateMoviesAdapter.RateMoviesViewHolder>() {
private lateinit var movies: List<PickedMovieItemViewModel>
private lateinit var listener: RateListener
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RateMoviesViewHolder {
val binding: ItemMovieRatedBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_movie_rated,
parent,
false
)
return RateMoviesViewHolder(binding)
}
override fun onBindViewHolder(holder: RateMoviesViewHolder, position: Int) {
val item = movies[position]
holder.bind(item, listener)
}
override fun getItemCount(): Int {
return movies.size
}
fun update(
movies: List<PickedMovieItemViewModel>,
rateListener: RateListener
) {
this.movies = movies
this.listener = rateListener
notifyDataSetChanged()
}
class RateMoviesViewHolder(private val binding: ItemMovieRatedBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: PickedMovieItemViewModel, rateListener: RateListener) {
binding.model = item
binding.listener = rateListener
}
}
}
This is the layout where recycler view is
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="viewModel"
type="com.example.moviepicker.presentation.viewmodel.RateMoviesViewModel" />
</data>
<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"
tools:context=".presentation.activity.RateMoviesActivity">
<TextView
android:id="#+id/rate_textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/margin"
android:gravity="center"
android:text="#string/please_give_us_a_review_for_movies_you_have_picked"
android:textColor="#color/red_dark"
android:textSize="#dimen/title_size"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_pickedMovies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/top_margin"
app:listener="#{viewModel::rateMovie}"
app:rated_movies="#{viewModel.pickedMovies}"
android:foregroundGravity="center"
app:layout_constraintTop_toBottomOf="#id/rate_textView" />
<Button
android:id="#+id/login_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/top_margin"
android:layout_marginTop="#dimen/button_top_margin"
android:layout_marginEnd="#dimen/top_margin"
android:layout_marginBottom="#dimen/top_margin"
android:background="#drawable/ic_red_button"
android:onClick="#{() -> viewModel.goMainPage()}"
android:shadowColor="#color/black"
android:shadowDx="1.5"
android:shadowDy="1.3"
android:shadowRadius="10"
android:text="#string/next"
android:textAllCaps="false"
android:textColor="#color/white"
android:textSize="#dimen/text_size"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#id/rv_pickedMovies" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
The method rateMovie is never called.
Where am I wrong?
OnClick event is not getting fired on RatingBar view, so I have used setOnRatingBarChangeListener() method to get callback from RatingBar. And also remove android:onClick="#{(view) -> listener.rateMovie(view,model)}" from ItemViewRateBinding. Like:
fun bind(item: PickedMovieItemViewModel, rateListener: RateListener) {
binding.model = item
//binding.listener = rateListener
// Click listener is not working on Rating Bar.
binding.rating.setOnRatingBarChangeListener { ratingBar, rating, fromUser ->
rateListener.rateMovie(ratingBar,item)
}
}
item_view_rate.xml
<layout>
<data>
<variable
name="model"
type="com.adhoc.testapplication.PickedMovieItemViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RatingBar
android:id="#+id/rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5"
android:stepSize="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<!--
android:onClick="#{(view) -> listener.rateMovie(view,model)}"
-->
You are defining different listener in recycler's adapter , you need to pass listener from your fragment/videmodel which is implemented and overridden there.
YourFragment : Fragment(), RateListener {
val yourAdapter = RateMoviesAdapter(this)
}
where "this" represents implemented listener(Interface), and apply that listener in adapter.
Should work then

Binding data inside an Adapter Kotlin

I have created the adapter below which shows two different data models in the recycler view.
However, I am not sure how to do the bindings in the bind functions written in the ViewHolders. I have two seperate xml files which I would like to bind when this "bind" function is called but how do I set the data?
My code is as follows:
class HomeAdapter(
private val context: Context
) :
RecyclerView.Adapter<HomeAdapter.BaseViewHolder<*>>() {
private var homeList: List<Any> = emptyList()
companion object {
private const val TYPE_VISIT = 0
private const val TYPE_WASH = 1
}
abstract class BaseViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(item: T)
}
inner class VisitViewHolder(itemView: View) : BaseViewHolder<HomeVisitLabel>(itemView) {
override fun bind(item: HomeVisitLabel) {
//Do your view assignment here from the data model
}
}
inner class WashViewHolder(itemView: View) : BaseViewHolder<HomeWashLabel>(itemView) {
override fun bind(item: HomeWashLabel) {
//Do your view assignment here from the data model
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
return when (viewType) {
TYPE_VISIT -> {
val view = LayoutInflater.from(context)
.inflate(R.layout.reward_label_visit_card, parent, false)
VisitViewHolder(view)
}
TYPE_WASH -> {
val view = LayoutInflater.from(context)
.inflate(R.layout.reward_label_wash_card, parent, false)
WashViewHolder(view)
}
else -> throw IllegalArgumentException("Invalid view type")
}
}
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
val element = homeList[position]
when (holder) {
is VisitViewHolder -> holder.bind(element as HomeVisitLabel)
is WashViewHolder -> holder.bind(element as HomeWashLabel)
else -> throw IllegalArgumentException()
}
}
override fun getItemViewType(position: Int): Int {
val comparable = homeList[position]
return when (comparable) {
is HomeVisitLabel -> TYPE_VISIT
is HomeWashLabel -> TYPE_WASH
else -> throw IllegalArgumentException("Invalid type of data " + position)
}
}
override fun getItemCount(): Int {
return homeList.size
}
}
One of the two XML files
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="VisitLabel"
type="com.modelz.HomeVisitLabel" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp"
android:layout_margin="5dp"
app:cardBackgroundColor="#color/colorPrimary">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="Visit:"
android:gravity="left"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="#{VisitLabel.Name}"
android:gravity="left"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:textStyle="bold"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Redeem"
android:visibility="invisible"/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_weight="0.5"
android:gravity="left"
android:text="#{VisitLabel.descript}"
android:textSize="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_weight="0.5"
android:gravity="left"
android:text="Progress:"
android:textSize="16dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_weight="0.9"
android:text="#{VisitLabel.countUser}"
android:gravity="right"
android:textSize="16sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_weight="0.5"
android:text="#{VisitLabel.countSet}"
android:gravity="left"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</layout>
if you want to use data binding, you need to inflate your layouts from Binding class as follow:
val binding = RewardLabelVisitCardBinding.inflate(layoutInflater, parent, false)
and same for the other layout.
You can get layoutInflator using:
val layoutInflater = LayoutInflater.from(parent.context)
And then in the bind function you need to use that binding variable to assign attributes. like,
binding.textView.text = item.name
The overall code for better understanding is given below:
class VisitViewHolder(private val binding: RewardLabelVisitCardBinding) : BaseViewHolder<HomeVisitLabel>(binding.root) {
override fun bind(item: HomeVisitLabel) {
binding.VisitLabel = item
binding.executePendingBindings()
}
}
Just pass binding variable to ViewHolder after inflating the layout in onCreateViewHolder
return VisitViewHolder(binding)
Hope, that answers your question!
You will need to complete the bind functions for both the viewholders:
override fun bind(item: HomeVisitLabel) {
// textView.text = item.name // example
}
You should be able to assign the values in the function above as demonstrated by the example, this should work as in onBindViewHolder, the bind function is called depending on the type of data at the position in the array, allowing the data to be bound to different xml layout files.
You will have to complete the bind functions for both viewholders, setting text views to item data strings for example or setting images resources for image views, corresponding to the correct xml layout file for the data type.

how can I sort company list based on Name ascending, descending order in my adapter?

I am developing new app and how can I sort the company list based on Name ascending, descending order in my adapter?.
below my Adapter class
class SpectrumAdapter(
private val spectrumResponse: ArrayList <SpectrumResponseItem>
) : RecyclerView.Adapter<SpectrumViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SpectrumViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.spectrum_list, parent, false)
return SpectrumViewHolder(view)
}
override fun getItemCount(): Int {
return spectrumResponse.size
}
override fun onBindViewHolder(holder: SpectrumViewHolder, position: Int) {
return holder.bind(spectrumResponse[position])
}
}
the adapter class.
class SpectrumViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val companyLogo: ImageView = itemView.findViewById(R.id.companyLogo)
private val companyName: TextView = itemView.findViewById(R.id.companyName)
private val companyWebsite: TextView = itemView.findViewById(R.id.companyWebsite)
fun bind(spectrum: SpectrumResponseItem) {
Glide.with(itemView.context).load(R.drawable.placehold).into(companyLogo)
companyName.text = spectrum.company
companyWebsite.text = spectrum.website
}
}
below list XML layout
<androidx.cardview.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">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/cardview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/companyLogo"
android:layout_width="45dp"
android:layout_height="26dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="11dp"
android:layout_marginRight="11dp"
android:padding="4dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="#+id/companyWebsite"
app:layout_constraintEnd_toStartOf="#+id/companyName"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<TextView
android:id="#+id/companyWebsite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="56dp"
android:layout_marginLeft="56dp"
android:layout_marginTop="40dp"
android:text="Company Website"
android:textSize="8sp"
app:layout_constraintBottom_toBottomOf="#id/companyLogo"
app:layout_constraintStart_toStartOf="#+id/companyLogo"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="#+id/companyName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginEnd="286dp"
android:layout_marginRight="286dp"
android:text="Company Name"
android:textSize="8sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/companyLogo"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
I want to implement sorting function where user can sort companyName ascending and descending order how can I implement it?
You can add method sortAscending and sortDescending in your adapter. But you need to change your private val spectrumResponse: ArrayList to var because the data can be changed while sorting. here is full code example for adapter.
class SpectrumAdapter(
private var spectrumResponse: ArrayList <SpectrumResponseItem>
) : RecyclerView.Adapter<SpectrumViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SpectrumViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.spectrum_list, parent, false)
return SpectrumViewHolder(view)
}
override fun getItemCount(): Int {
return spectrumResponse.size
}
override fun onBindViewHolder(holder: SpectrumViewHolder, position: Int) {
return holder.bind(spectrumResponse[position])
}
fun sortAscending() {
spectrumResponse = spectrumResponse.sortedBy { it.name }
notifyDataSetChanged()
}
fun sortDescending() {
spectrumResponse = spectrumResponse.sortedByDescending { it.name }
notifyDataSetChanged()
}
}
then you can call it in your button on you activity like this.
btn_sort_ascending.setOnClickListener {
adapter.sortAscending()
}
the btn_sort_ascending is the view id of your button.
the adapter is your created adapter.

Categories

Resources