I'm having a problem inflating my recyclerView inside a BottomSheetDialog using activites.
I keep getting the error:
java.lang.IllegalStateException: recycler_provider must not be null
my method to open the dialog is:
private fun openOptionsDialog(context: Context) {
val dialog = BottomSheetDialog(context)
val layoutInflater: View =
LayoutInflater.from(context).inflate(R.layout.providers_pop_up, null)
dialog.setContentView(layoutInflater)
dialog.show()
recycler_provider.adapter = ProviderAdapter(getProvidersList())
recycler_provider.layoutManager = LinearLayoutManager(this)
recycler_provider?.setHasFixedSize(true)
layoutInflater.close.setOnClickListener {
dialog.dismiss()
}
}
My adapter class:
class ProviderAdapter(private val items: ArrayList<ProviderModel>) : RecyclerView.Adapter<ProviderAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProviderAdapter.ViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.provider_item, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ProviderAdapter.ViewHolder, position: Int) {
holder.providerTitle.text = items[position].providerName
holder.providerImage.setImageResource(items[position].providerImage)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val providerTitle: TextView = itemView.providerItemName
val providerImage: ImageView = itemView.providerItemImage
}
and the XML code for the provider item:
<?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">
<LinearLayout
android:id="#+id/providerRow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.002">
<ImageView
android:id="#+id/providerItemImage"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_marginLeft="20dp"
android:src="#drawable/meo"></ImageView>
<TextView
android:id="#+id/providerItemName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="MEO"
android:textSize="21sp">
</TextView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Anyone knows where the problem is?
the problem there is you haven't initialize recycler_provider
recycler_provider = layoutInflater.findViewById<RecyclerView>(R.id.your_recyclerview_id)
Related
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
}
As the official material.io documentation says:
Chips are compact elements that represent an input, attribute, or action.
I want to add chips as attributes (tags) for any blog post, a recyclerview is used to show the list of blog posts:
item_post.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/mcv_container_item_blog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:clickable="true"
android:background="?selectableItemBackground"
app:cardUseCompatPadding="true">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/llc_container_item_project"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/item_margin"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/aciv_item_post_image"
android:layout_width="match_parent"
android:layout_height="#dimen/item_project_Image_size"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
app:srcCompat="#drawable/drawer_bg"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/item_margin"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textColorHighlight="#color/design_default_color_background"
android:text="Chameleon UI Kit" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_date"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#color/red_lighten"
android:layout_marginTop="#dimen/user_name_margin_start"
android:layout_marginEnd="#dimen/user_name_margin_start"
android:layout_marginBottom="#dimen/user_name_margin_start"
android:text="10 Sept"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_short_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.AppCompat.Small"
android:text="#string/about_me" />
<include layout="#layout/item_divider"/>
<HorizontalScrollView
android:id="#+id/hsv_item_post_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<com.google.android.material.chip.ChipGroup
android:id="#+id/cg_item_post_tags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleLine="true"/>
</HorizontalScrollView>
</androidx.appcompat.widget.LinearLayoutCompat>
</com.google.android.material.card.MaterialCardView>
then the PostAdapter class:
class PostAdapter(
private val postList: List<Post>
): RecyclerView.Adapter<PostAdapter.PostViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_post, parent, false)
return PostViewHolder(view)
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int) {
val post = postList[position]
holder.postTitle.text = post.postTitle
holder.postDescription.text = post.postDescription
holder.postDate.text = post.postDate
val tagChip = Chip(holder.itemView.context).apply {
id = View.generateViewId()
text = "label"
}
holder.postTagsChipGroup.addView(tagChip)
}
override fun getItemCount(): Int {
return postList.size
}
inner class PostViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val postTitle: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_title)
val postDescription: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_short_description)
val postTagsChipGroup: ChipGroup = itemView.findViewById(R.id.cg_item_post_tags)
val postDate: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_date)
}
}
at this stage a weird thing happens, when I 'scroll recyclerview' other Chips are added from nowhere as you can see this behavior in gif below:
how to avoid such behavior, what changes should I make in adapters onBindViewHolder method?
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
I am facing an strange behaviour now days. I am using view binding for refrencing ids. When RecyclerView height is wrap_content the item display perfectly but when I set recyclerview height match_parent the item width is automatically wrapped (wrap_content) in run time but in xml I have set item width is match_parent.
Please help me to solve this problem.
Please look at my xml and kt files -:
<LinearLayou ------
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/idRvTrainerReview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
row_item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_1sdp"
android:background="#color/colorWhite"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="#dimen/_10sdp">
<TextView
android:id="#+id/idTvDate"
style="#style/MediumText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="14 feb"
android:includeFontPadding="false"
android:textColor="#color/colorBlack"
android:textStyle="bold" />
<TextView
android:id="#+id/idTvStartTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:gravity="center"
android:includeFontPadding="false"
android:text="19:30" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_right_arrow" />
</LinearLayout>
adapter file -:
class FeedbackDataAdapter(
private var mcontext: Context,
private var mItemList: MutableList<ResponseTrainerFeedback.Result.Training>,
var mClickListener: OnClickListener?
) : RecyclerView.Adapter<TrainingDataViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrainingDataViewHolder {
var layoutInflater = mcontext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as (LayoutInflater)
val rowEventMemberBinding =
RowEventDataBinding.inflate(layoutInflater, parent, false)
return TrainingDataViewHolder(rowEventMemberBinding)
}
override fun getItemCount(): Int = mItemList.size
override fun onBindViewHolder(holder: TrainingDataViewHolder, position: Int) {
holder.mBinding.idTvDate.text = mItemList[holder.adapterPosition].trainingName
holder.mBinding.idTvStartTime.invisible()
holder.itemView.setOnClickListener {
if(mItemList.size > 0){
mClickListener?.onItemClick(holder.adapterPosition)
}
}
}
fun refreshList(mFeedbackList: MutableList<ResponseTrainerFeedback.Result.Training>) {
this.mItemList = mFeedbackList
notifyDataSetChanged()
}
}
class TrainingDataViewHolder(var mBinding: RowEventDataBinding) :
RecyclerView.ViewHolder(mBinding.root) {
}
I've tried your code and It's working.
I've only refactored your adapter according to Android training and if you want to do the codelab: https://codelabs.developers.google.com/codelabs/kotlin-android-training-diffutil-databinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrainingDataViewHolder {
return TrainingDataViewHolder.from(parent)
}
override fun getItemCount(): Int = mItemList.size
override fun onBindViewHolder(holder: TrainingDataViewHolder, position: Int) {
holder.bind(mItemList[position])
holder.itemView.setOnClickListener {
if(mItemList.size > 0){
mClickListener?.onItemClick(holder.adapterPosition)
}
}
}
fun refreshList(mFeedbackList: MutableList<ResponseTrainerFeedback.Result.Training>) {
this.mItemList = mFeedbackList
notifyDataSetChanged()
}
}
class TrainingDataViewHolder(var mBinding: RowEventDataBinding) :
RecyclerView.ViewHolder(mBinding.root) {
fun bind(data: YOURDATA) {
mBinding.idTv = data // it'll map automatically all the property, replace idTv by the name of your variable in your layout
mBinding.idTvStartTime.invisible()
}
companion object {
fun from(parent: ViewGroup): TrainingDataViewHolder {
return TrainingDataViewHolder(RowEventDataBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
}
}
row_item
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="idTv"
type="package.name.to.data" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:background="#android:color/white"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/idTvDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.8"
android:text="#{idTv.idTvDate}"
android:includeFontPadding="false"
android:textColor="#android:color/black"
android:textStyle="bold"
tools:text="14 feb"/>
<TextView
android:id="#+id/idTvStartTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.2"
android:gravity="center"
android:includeFontPadding="false"
android:text="19:30" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#android:drawable/arrow_up_float" />
</LinearLayout>
</layout>
PS: I don't see <layout> in your row_item, are you sure you're doing binding correctly ?
I am trying to implement a simple nested recyclerview with a vertical parent and horizontal children.
The layout for the parent is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<include layout="#layout/loading_indicator" />
<include layout="#layout/error_layout" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/podcasts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/podcastsTitle"
android:layout_marginTop="5dp" />
</RelativeLayout>
The layout for the child recyclerview is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"
android:orientation="vertical">
<TextView
android:id="#+id/title"
style="#style/TitleTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:textAppearance="#style/TextAppearance.AppCompat.Large" />
<TextView
android:id="#+id/description"
style="#style/AuxTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:maxLines="2"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/podcastsItems"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
The adapter for the parent is as follows:
class CuratedPodcastsAdapter(val context: Context, private val podcastLists: ArrayList<PodcastList>) : RecyclerView.Adapter<CuratedPodcastsAdapter.ViewHolder>() {
private val viewPool = RecyclerView.RecycledViewPool()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.podcast_section_layout, parent, false))
override fun getItemCount(): Int = podcastLists.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItems(podcastLists[holder.adapterPosition])
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
#BindView(R.id.title)
lateinit var title: TextView
#BindView(R.id.description)
lateinit var description: TextView
#BindView(R.id.podcastsItems)
lateinit var podcastItems: RecyclerView
init {
ButterKnife.bind(this, itemView)
}
fun bindItems(curatedPodcastList: PodcastList) {
title.text = curatedPodcastList.title
description.text = curatedPodcastList.description
title.typeface = FontUtils.boldTypeface
description.typeface = FontUtils.mediumTypeface
val childLayoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
childLayoutManager.initialPrefetchItemCount = 4
podcastItems.apply {
layoutManager = childLayoutManager
adapter = PodcastAdapter(this#CuratedPodcastsAdapter.context, curatedPodcastList.podcasts!!)
setItemViewCacheSize(20)
setRecycledViewPool(viewPool)
}
}
}
}
while the children's adapter is:
class PodcastAdapter(val context: Context, private val episodes: ArrayList<Podcast>): RecyclerView.Adapter<PodcastAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.podcast_item_layout, parent, false))
override fun getItemCount(): Int = episodes.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItems(episodes[holder.adapterPosition])
}
inner class ViewHolder(view: View): RecyclerView.ViewHolder(view) {
#BindView(R.id.art)
lateinit var art: ImageView
#BindView(R.id.title)
lateinit var title: TextView
#BindView(R.id.publisher)
lateinit var publisher: TextView
#BindView(R.id.card)
lateinit var card: CardView
init {
ButterKnife.bind(this, view)
}
fun bindItems(podcast: Podcast){
title.text = podcast.title
publisher.text = podcast.publisher
GlideApp.with(context).load(podcast.image).error(R.drawable.no_cover).into(art)
card.setOnClickListener {
context.startActivity(context.intentFor<PodcastActivity>(PodcastActivity.PODCAST_ID to podcast.id, PodcastActivity.PODCAST_TITLE to podcast.title))
}
}
}
}
When content is loaded for the first time, everything appears correctly like below:
After scrolling downwards then back up again, spaces begin to appear between the content like so:
None of the recyclerviews have any item decoration added to them. My perception was that using a common viewpool would at least improve performance and help with such layout inconsistencies but it hasn't helped.
What is the cause of this problem and how can it be fixed?
You need to change the height of your child layout of recyclerview
to android:layout_height="wrap_content"
Sample code
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="vertical">
<TextView
android:id="#+id/title"
style="#style/TitleTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:textAppearance="#style/TextAppearance.AppCompat.Large" />
<TextView
android:id="#+id/description"
style="#style/AuxTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:maxLines="2"
android:textAppearance="#style/TextAppearance.AppCompat.Medium" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/podcastsItems"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>