I am using groupie recycler view to display 4 items. I want the layout to be a 2x2 instead of all of them under one another. This is what I have so far and the way it displays:
Module Class:
data class Module ( val Code: String,
val Desc: String){
constructor(): this("", "")
}
Module Fragment code snippet:
private fun updateRecyclerView(items: List<Item>) {
fun init() {
recycler_view_mod.apply {
layoutManager = LinearLayoutManager(this#ModuleFragment.context)
adapter = GroupAdapter<ViewHolder>().apply {
moduleSection = Section(items)
add(moduleSection)
//setOnItemClickListener(onItemClick)
}
}
shouldInitRecyclerView = false
}
fun updateItems() = moduleSection.update(items)
if (shouldInitRecyclerView)
init()
else
updateItems()
}
ModuleItem:
class ModuleItem (val module: Module,
val modId: String,
private val context: Context)
: Item(){
override fun bind(viewHolder: ViewHolder, position: Int) {
viewHolder.textView_Code.text = module.Code
viewHolder.textView_Description.text = module.Desc
}
override fun getLayout() = R.layout.item_module
}
Module Fragment layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.ModuleFragment">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view_mod"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
ModuleItem Layout:
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/cardView_item_module"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_margin="4dp"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="#android:color/white"
android:clickable="true"
android:focusable="true"
app:cardCornerRadius="4dp"
app:cardElevation="4dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView_Code"
android:textColor="#color/colorPrimaryDark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="#id/imageView_profile_picture"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:textSize="25sp"
android:textAppearance="#style/Base.TextAppearance.AppCompat"
tools:text="Code" />
<TextView
android:id="#+id/textView_Description"
android:textColor="#color/colorPrimaryDark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="#+id/textView_Code"
android:layout_below="#+id/textView_Code"
android:layout_marginStart="2dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Small"
tools:text="Description" />
</RelativeLayout>
</android.support.v7.widget.CardView>
ScreenShot:
Any help would be appreciated, let me know if you need any more information
So all you have to do is adjust the layoutmanager, like this:
private fun updateRecyclerView(items: List<Item>) {
fun init() {
recycler_view_mod.apply {
layoutManager = GridLayoutManager(context, 2, GridLayoutManager.VERTICAL, false)
adapter = GroupAdapter<ViewHolder>().apply {
moduleSection = Section(items)
add(moduleSection)
//setOnItemClickListener(onItemClick)
}
}
shouldInitRecyclerView = false
}
fun updateItems() = moduleSection.update(items)
if (shouldInitRecyclerView)
init()
else
updateItems()
}
You just need to use the right layout manager, it would look something to this effect, just set the span to 2
val mLayoutManager = GridLayoutManager(context, 2, GridLayoutManager.VERTICAL, false)
recyclerView.setLayoutManager(mLayoutManager)
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 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>
I've been scratching my head about this problem. I have a recyclerview inside a recyclerview that shows a grid layout where you can scroll both vertically and horizontally.
I decided to enlarge the width and height of each item. Previously, width and height were: 80 x 100
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/book_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/inner_dropshadow"
android:layout_width="80dp"
android:layout_height="100dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/bookdropshadow" />
</android.support.constraint.ConstraintLayout>
And then I enlarged it to 150x 180. And then Level 4-6 row completely disappeared no matter how much I scroll up. I only expected the recyclerview to display larger images. Why does this happen?
[EDIT] I ran a debug and it seems only 3 positions are bound in the outer adapter when the app is run on a phone but there are 4 positions when run on a tablet.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/book_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/inner_dropshadow"
android:layout_width="150dp"
android:layout_height="180dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/bookdropshadow" />
</android.support.constraint.ConstraintLayout>
Please note that this also happens when I run the app on a device. In a tablet, it shows all the Levels, while on a phone, it only shows Levels 1-3.
Outer recyclerview layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/mainlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/color_white">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/swiperefresh"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/toolbar">
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="always">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/browsebooksheader_layout"
android:id="#+id/bb_header"
app:layout_constraintBottom_toTopOf="#+id/guidelinetop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"/>
<android.support.constraint.Guideline
android:id="#+id/guidelinetop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.30"/>
<Button
android:id="#+id/btn_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="#drawable/search_btn_border"
android:padding="8dp"
android:text="Search"
android:textSize="#dimen/browse_search_textsize"
android:textColor="#android:color/darker_gray"
app:layout_constraintBottom_toTopOf="#+id/recyclerview_main"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/bb_header" />
<ProgressBar
android:id="#+id/main_progressbar"
style="?android:attr/progressBarStyleSmall"
android:layout_marginTop="16dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:indeterminateTint="#color/color_grey"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btn_search"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:nestedScrollingEnabled="false"
android:background="#color/color_white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btn_search"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.constraint.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="#+id/toolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/swiperefresh"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<include layout="#layout/toolbar_layout_wo_learn"/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
Inner recyclerview layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="wrap_content">
<TextView
android:id="#+id/book_category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Category Title"
android:textColor="#color/defaultdark_color"
android:textSize="#dimen/browse_category_textsize"
android:fontFamily="#font/vagroundedstd_bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="#dimen/browse_seeall_imagesize"
android:layout_height="#dimen/browse_seeall_imagesize"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp"
android:id="#+id/see_all_btn"
android:src="#drawable/seeall"
app:layout_constraintBottom_toTopOf="#+id/inner_recyclerview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.v7.widget.RecyclerView
android:id="#+id/inner_recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:background="#color/color_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/book_category"/>
<TextView
android:id="#+id/h_line"
android:layout_width="match_parent"
android:layout_height="0.8dp"
android:background="#color/color_grey"
android:gravity="center"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:alpha="0.5"
app:layout_constraintTop_toBottomOf="#id/inner_recyclerview"/>
</android.support.constraint.ConstraintLayout>
Outer Adapter
class BrowseBooksAdapter : RecyclerView.Adapter<BrowseBooksAdapter.SimpleViewHolder>() {
private var sortedList : SortedMap<String, List<ResourcesList>> = emptyMap<String, List<ResourcesList>>().toSortedMap()
fun setData(sortedList : SortedMap<String, List<ResourcesList>>) {
if (this.sortedList != sortedList) {
this.sortedList = sortedList
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return sortedList.count()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SimpleViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.browsebooks_layout, parent, false)
return SimpleViewHolder(view)
}
override fun onBindViewHolder(holder: SimpleViewHolder, position: Int) {
val dataList = ArrayList(sortedList.values)[position]
holder.category_text.text = ArrayList(sortedList.keys)[position]
holder.booksByCategory = dataList
holder.horizontalAdapter.setData(dataList)
}
class SimpleViewHolder(v: View, var booksByCategory : List<ResourcesList>? = null) : RecyclerView.ViewHolder(v) {
var recyclerView : RecyclerView = v.findViewById(R.id.inner_recyclerview)
var category_text : TextView = v.findViewById(R.id.book_category)
var fragmentCallback: FragmentCallback? = null
val horizontalAdapter: BooksByCategoryAdapter
init {
recyclerView.layoutManager = object : LinearLayoutManager(v.context, LinearLayoutManager.HORIZONTAL, false) {
override fun canScrollHorizontally(): Boolean {
return true
}
override fun canScrollVertically(): Boolean {
return false
}
}
horizontalAdapter = BooksByCategoryAdapter()
recyclerView.adapter = horizontalAdapter
fragmentCallback = v.context as FragmentCallback
var mLastClickTime : Long = 0
v.see_all_btn.setOnClickListener {
val category = v.book_category.text.toString()
// double-clicking prevention, using threshold of 1000 ms
if (!(SystemClock.elapsedRealtime() - mLastClickTime < 1000)){
mLastClickTime = SystemClock.elapsedRealtime()
if(fragmentCallback != null){
fragmentCallback!!.callBackSeeAllFragment(booksByCategory!!, category)
}
}
}
}
}
}
Inner Adapter
class BooksByCategoryAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var booksByCategory: List<ResourcesList> = emptyList()
fun setData(data: List<ResourcesList>) {
if (this.booksByCategory != data) {
this.booksByCategory = data
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return booksByCategory.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.bookcontainer_layout, parent, false)
return CustomVH(cellForRow)
}
override fun onBindViewHolder(rawHolder: RecyclerView.ViewHolder, position: Int) {
val holder = rawHolder as CustomVH
val booksProcessModel = BooksProcessModel()
booksProcessModel.setItem(booksByCategory[position])
holder.booksByCategory = booksByCategory[position]
Picasso.get().load(booksByCategory[position].coverURI).into(holder.inner_bookcover)
}
private inner class CustomVH(v: View, var booksByCategory : ResourcesList? = null): RecyclerView.ViewHolder(v) {
var inner_bookcover : ImageView = v.findViewById(R.id.inner_bookcover)
var fragmentCallback: FragmentCallback? = null
var ctx : Context = v.context
var mLastClickTime : Long = 0
init {
fragmentCallback = ctx as FragmentCallback
v.setOnClickListener {
// double-clicking prevention, using threshold of 1000 ms
if (!(SystemClock.elapsedRealtime() - mLastClickTime < 1000)){
mLastClickTime = SystemClock.elapsedRealtime()
if(fragmentCallback != null){
fragmentCallback!!.callBackDownloadBookFragment(booksByCategory!!)
}
}
}
}
}
class BooksProcessModel {
private var processBooks : ResourcesList? = null
fun setItem(books: ResourcesList) {
this.processBooks = books
}
fun setVisibility(ctx: Context) : Int {
val sp = PreferenceManager.getDefaultSharedPreferences(ctx)
val page = sp.getString(processBooks!!.id.toString(), "")
if (page != "") {
return View.VISIBLE
}
return View.INVISIBLE
}
}
}
You need to change the layout_height of the outer recycler view to wrap_content.
Nevermind, figured it out. I should be using a NestedScrollView instead of a ScrollView. The missing items were still there. It's just that I couldn't scroll the nested recyclerview.
In my case it was because I had the RecyclerView directly and even though it scrolled (horizontally), the same thing happened to me if for example the keyboard appeared.
Solution: Put the RecyclerView inside a NestedScrollView and delegate the scroll to it, like this:
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/item"/>
</androidx.core.widget.NestedScrollView>