How can I use FirestorePagingAdapter for a RecyclerView in NestedScrollView - android

I am using FirestorePagingAdapter to fetch my documents from Firestore. Everything is perfect but The "Load more" is happening automatically without getting to the bottom of the list. This is causing a janky behaviour.
I have placed my RecyclerView(Height: wrap content) in a NestedScrollView(Height: match-parent) which is the content of my main scrolling activity layout.
Is there a way to load more items only when I scroll to the last item.
<android.support.v4.widget.NestedScrollView 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"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".NewLookCode.activities.MainScrollingActivity"
tools:showIn="#layout/activity_main_scrolling">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_transporters"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:padding="10dp"
android:visibility="visible" />
</LinearLayout>
val baseQuery = FirebaseFirestore.getInstance().collection("partners")
val config = PagedList.Config.Builder()
.setEnablePlaceholders(true)
.setPrefetchDistance(2)
.setPageSize(6)
.build()
val options = FirestorePagingOptions.Builder<PartnerInfoPojo>()
.setLifecycleOwner(this)
.setQuery(baseQuery, config, PartnerInfoPojo::class.java)
.build()
adapter = object:FirestorePagingAdapter<PartnerInfoPojo, PartnersViewHolder>(options) {
#NonNull
override fun onCreateViewHolder(#NonNull parent: ViewGroup, viewType:Int):PartnersViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_new_transporter, parent, false)
return PartnersViewHolder(view)
}
override fun onBindViewHolder(#NonNull holder:PartnersViewHolder,
position:Int,
#NonNull model:PartnerInfoPojo) {
holder.mCompany.text = model.getmCompanyName()
if(model!=null){
if(model.getmCompanyAdderss()!=null)
holder.mAddress.text = model.getmCompanyAdderss().city
}
}
}
rv_transporters.layoutManager = LinearLayoutManager(this)
rv_transporters.adapter = adapter

Related

Horizontal RecyclerView with dynamic item height

I am trying to create a horizontal recycler view with dynamic height by following this stack overflow post. The solution seems working. But the recycler view items disappear when I try to remove an item from recycler view.
recyclerview layout:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="13dp"
android:paddingEnd="13dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Recycler view item 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"
android:layout_marginEnd="4dp"
android:layout_marginStart="4dp"
app:cardBackgroundColor="#BCAAAA"
app:cardElevation="2dp">
<TextView
android:padding="36dp"
android:id="#+id/textViewName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="22sp"
tools:text="Test String"/>
</androidx.cardview.widget.CardView>
Activity:
val flexBoxLayoutManager = FlexboxLayoutManager(this, FlexDirection.ROW, FlexWrap.NOWRAP)
with(recyclerView) {
layoutManager = flexBoxLayoutManager
adapter = RecyclerViewAdapter()
setHasFixedSize(false)
}
Adapter:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textViewName: TextView = itemView.findViewById(R.id.textViewName)
init {
updateLayoutParamsToAllowHorizontalScrolling()
}
private fun updateLayoutParamsToAllowHorizontalScrolling() {
(itemView.layoutParams as? FlexboxLayoutManager.LayoutParams)?.let {
it.flexShrink = 0.0f
it.alignSelf = AlignItems.FLEX_START
}
}
}
override fun onBindViewHolder(holder: RecyclerViewAdapter.ViewHolder, position: Int) {
val item = listItems[position]
var msg = "Sample item no:$position "
for(i in 0..position){
msg += "dynamic content \n"
}
holder.textViewName.text = msg
holder.itemView.setOnClickListener {
val pos = listItems.indexOf(item)
listItems.removeAt(pos)
notifyItemRemoved(pos)
}
}
A screen recording of the output:
Is there any way to fix this issue? Or Are there any other approach to implement horizontal recyclerveiw with dynamic height?
I have fixed the issue without using FlexBoxLayoutManger.
Used LinearLayoutManager with horizontal orientation and the following code in onBindViewHolder in RecyclerViewAdapter.
holder.itemView.post {
val wMeasureSpec =
View.MeasureSpec.makeMeasureSpec(holder.itemView.width, View.MeasureSpec.EXACTLY)
val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
holder.itemView.measure(wMeasureSpec, hMeasureSpec)
if (holder.itemView.measuredHeight > holder.itemView.height) {
holder.itemView.layoutParams =
(holder.itemView.layoutParams as ViewGroup.LayoutParams)
.apply {
height = holder.itemView.measuredHeight
}
}
}

RecyclerView data not showing

My ReycyclerView is not showing data, but once i removed .setHasFixedSize(true), then only it will display. However, this causes another problem. On the first time entering the fragment, it works fine, my actionBar is displaying, but on the second time, it somehow overlaps or pushes away my ActionBar.
I guess .sethasFixedSize is necessary but if i keep it, it will not display data. What causes this?
First time entering:
Second time entering (before notifyItemInserted()):
After notifyItemInserted():
fragment.xml:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/cartItemRv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
fragment.kt:
onCreateView() {
binding.cartItemRv.layoutManager = LinearLayoutManager(context)
binding.cartItemRv.setHasFixedSize(true)
cartAdapter = CartAdapter(productNameList, productVariationList, cartItemList)
binding.cartItemRv.adapter = cartAdapter
//code for retrieving data from Firebase
cartAdapter.notifyItemInserted(productNameList.size-1)
}
Adapter:
class CartAdapter(...: RecyclerView.Adapter<CartAdapter.CartViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.cart_item, parent, false)
return CartViewHolder(itemView, mListener)
}
override fun onBindViewHolder(holder: CartViewHolder, position: Int) {
...
}
override fun getItemCount(): Int {
return productNameList.size
}
inner class CartViewHolder(itemView: View, listener: onItemClickListener) : RecyclerView.ViewHolder(itemView){
...
}
}
about sethasFixedSize , if the size of your the RecyclerView depends on the adapter's content you have to set the sethasFixedSize vlaue : false
otherwise set it true .
// for depending on adapter
mRecyclerView.setHasFixedSize(false);
// for doesn't depend on adapter
mRecyclerView.setHasFixedSize(true);
add NestedScrollView on the parent of RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/cartItemRv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"/>
</androidx.core.widget.NestedScrollView>
</RelativeLayout>
Then set RecyclerView
cartItemRv.setHasFixedSize(false);

Error in RecyclerViewAdapter's onBindViewHolder() Method ㅠㅠ [duplicate]

Why do I get a NullPointerException in my ViewHolder's bindItems() method?
I've highlighted the line where I get the NullPointerException. The blogpost_author ID exists, as you can see in the XML, so what's the problem here? How is findViewById<TextView>(R.id.blogpost_author) returning null?
Adapter and ViewHolder code:
class BlogPostAdapter(val blogList: ArrayList<BlogPost>) : RecyclerView.Adapter<BlogPostAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : BlogPostAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.blog_post_list, parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return blogList.size
}
override fun onBindViewHolder(holder: BlogPostAdapter.ViewHolder, position: Int) {
holder.bindItems(blogList[position])
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(blogPost: BlogPost) {
val blogPostAuthor = itemView.findViewById<TextView>(R.id.blogpost_author) // THIS LINE - NULL POINTER EXCEPTION
val blogPostTitle = itemView.findViewById<TextView>(R.id.blogpost_title)
blogPostAuthor.text = blogPost.author
blogPostTitle.text = blogPost.title
}
}
}
Activity code:
class BlogPostListActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.blog_post_list)
// Get the RecyclerView from XML itself
val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
// Add a layout manager - What does a layout manager do?
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
// Create an array list to store blogposts using the the data class blogPost
val blogPosts = ArrayList<BlogPost>()
// Add some dummy data to the list
blogPosts.add(BlogPost(123, "First Blog Post", "John"))
blogPosts.add(BlogPost(456, "Second Blog Post", "Bob"))
blogPosts.add(BlogPost(789, "Third Blog Post", "Mary"))
// Create an adapter
val adapter = BlogPostAdapter(blogPosts)
// Add the adapter to the recyclerview
recyclerView.adapter = adapter
}
}
Kotlin data class:
data class BlogPost(val id: Int, val title: String, val author: String)
XML for RecyclerView:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.topzap.android.kotlinlistapptest.BlogPostListActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
XML for CardView layout:
<?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="match_parent">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/blogpost_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="AuthorPlaceHolder"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Large"
/>
<TextView
android:id="#+id/blogpost_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="TitlePlaceHolder"
android:textAppearance="#style/Base.TextAppearance.AppCompat.Medium"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
You may be inflating the wrong layout within your RecyclerView.
This line within your onCreateViewHolder method:
val v = LayoutInflater.from(parent.context).inflate(R.layout.blog_post_list, parent, false)
You are inflating the blog_post_list.xml, which I'm assuming is the wrong layout file due to the fact you're also inflating that layout within your BlogPostListActivity here:
setContentView(R.layout.blog_post_list)
So when this line is called:
val blogPostAuthor = itemView.findViewById<TextView>(R.id.blogpost_author)
It is looking for the id 'blogpost_author' within R.layout.blog_post_list and as you can see there is no blogpost_author TextView within that layout so it returns null.
To sort it out, it should be straight forward and just change the layout resource that you're assigning to each ViewHolder within your onCreateViewHolder method with the correct layout for your CardView layout.
Which means the line should read something like:
val v = LayoutInflater.from(parent.context).inflate(R.layout.your_card_layout, parent, false)

smoothScrollToPosition does not work in nested RecyclerView

I have a recyclerView and each of it's item is a recyclerView. I want scroll to custom position of inner recyclerView at specific condition.
I used this code at innerRecyclerView but it didn't work:
innerRecyclerView.smoothScrollToPosition(position);
and this:
innerRecyclerView.scrollToPosition(position);
and this one:
layoutManager.scrollToPositionWithOffset(position, offset);
Now I have two question:
1) Is it possible scroll to custom position of inner recyclerView?
2) If that is possible, how?
Here is initialize main recyclerView:
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false);
adapter = new FreightsListRVAdapter(new FreightListCallBack(), new FreightListVHFactory());
rvFreightsList.setLayoutManager(layoutManager);
rvFreightsList.setItemViewCacheSize(30);
rvFreightsList.setNestedScrollingEnabled(true);
rvFreightsList.setAdapter(adapter);
Here is initialize innerRecyclerview:
LinearLayoutManager layoutManager = new LinearLayoutManager(itemView.getContext(), RecyclerView.VERTICAL, false);
layoutManager.setItemPrefetchEnabled(true);
layoutManager.setInitialPrefetchItemCount(7);
rvFreights.setNestedScrollingEnabled(false);
rvFreights.setLayoutManager(layoutManager);
rvFreights.setItemViewCacheSize(20);
adapter = new FreightsRVAdapter(new FreightCallBack(), new FreightSingleVHFactory());
rvFreights.setAdapter(adapter);
Here is my main recyclerView adapter:
public class FreightsListRVAdapter extends ListAdapter<FreightListModel, FreightListVH> {
private final FreightListVHFactory mFactory;
private final PublishSubject<FreightListVHAction> mClickUserPS = PublishSubject.create();
#Inject
public FreightsListRVAdapter(#NonNull FreightListCallBack diffCallback, FreightListVHFactory factory) {
super(diffCallback);
mFactory = factory;
}
#NonNull
#Override
public FreightListVH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return mFactory.create(parent);
}
#Override
public void onBindViewHolder(#NonNull FreightListVH holder, int position) {
holder.getVM().setObject(getItem(position));
holder.bind();
holder.itemOnClick(mClickUserPS);
}
public PublishSubject<FreightListVHAction> getmClickUserPS() {
return mClickUserPS;
}
}
Here is layout of each row of mainRecyclerView:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView 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/txtDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:textColor="#992C2C2C"
android:textSize="11sp"
app:fontFamily="#font/iransans"
tools:ignore="HardcodedText,SmallSp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/txtDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="15dp"
android:gravity="right"
android:textColor="#2C2C2C"
android:textSize="20sp"
app:fontFamily="#font/iransans_medium"
tools:ignore="RtlHardcoded"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvFreights"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:scrollbars="none" />
</androidx.appcompat.widget.LinearLayoutCompat>
In Kotlin-androidX recyclerview solution
val parentViewHolder = parentRecyclerView?.findViewHolderForAdapterPosition(parentPosition) as? ParentViewHolder?
var childY = 0F
if (parentViewHolder != null)
childY = parentViewHolder.childRecyclerView?.getChildAt(lastPosition)?.y ?: 0F
parentRecyclerView.scrollBy(0, childY.toInt())
PrentViewHolder.kt
class PrentViewHolder(
view: InflateBinding
): RecyclerView.ViewHolder(view.root) {
var recyclerView: RecyclerView? = null
fun populateData(data: Data) {
viewDataBinding.recyclerview.adapter = adapter
recyclerView = viewDataBinding.recyclerview
}
}
Can you please try this:
float y = recyclerView.getY() + recyclerView.getChildAt(selectedPosition).getY();
mainRecyclerview.smoothScrollTo(0, (int) y);

Recyclerview only showing first value

i am having an issue with my gson parsing. I have a nested gson and i would like get the value of "name" under attributes. The problem is my recyclerview is only showing the first value(which is bill 1) and not bill 2 or bill 3. here is my code
// MainActivity.kt
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(LineItemDecorator(requireContext()))
val bill = GsonBuilder().create().fromJson(jsonString, Bills::class.java)
val nameList: List<Bill> = Arrays.asList(bill)
val adapter = BillsRecyclerAdapter(nameList)
recycler_view.adapter = adapter
adapter.notifyDataSetChanged()
// Bills.kt
data class Bills(
val data: ArrayList<Data>,
val meta: Meta,
val links: Links
)
// BillsRecyclerAdapter.kt
class BillsRecyclerAdapter(private val items: List<Bill>):
RecyclerView.Adapter<BillsRecyclerAdapter.BillsHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BillsHolder {
return BillsHolder(parent.inflate(R.layout.bills_list_item))
}
override fun onBindViewHolder(holder: BillsHolder, position: Int) {
holder.billName.text = items[position].data[position].attributes.name
}
override fun getItemCount() = items.size
class BillsHolder(view: View): RecyclerView.ViewHolder(view) {
val billName: TextView = view.billName
val billAmount: TextView = view.billAmount
}
}
<!-- recyclerview.xml -->
<?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="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_marginTop="#dimen/materialize_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<!-- bill_list_item.xml-->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/billName"
android:textSize="20sp"
android:layout_marginStart="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:id="#+id/billAmount"
android:textSize="20sp"
android:layout_marginEnd="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
here is my json string. https://pastebin.com/iEqhmB35
thanks for any help provided
Check the following
Check your items.size if it really is not zero,
Also check your bills_list_item.xml try to set the root parent's height to wrap_content
Also check if you have this on you recyclerview
recyclerView.setHasFixedSize(true);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
What's important there is the layout manager
If it doesn't help, can you share your Bills.java class, there might be something wrong with the serialization of JSON raw data.
Hope this helps.
Cheers

Categories

Resources