I created a ViewPager2 view with a DiffUtil adapter, that is updated when I get data from several APIs.
However, the problem is that the views overlap when I update the adapter.
As you can see in this video.
Click to view the video on Vimeo
//viewpager
binding?.realtimeTrending?.apply {
orientation = ViewPager2.ORIENTATION_HORIZONTAL
adapter = adapter
offscreenPageLimit = OFFSCREEN_PAGE_LIMIT_DEFAULT
isNestedScrollingEnabled = true
}
// adding value to viewpager
adapter?.updateRealtime(it.value)
fun updateRealtime(value: List){
// Update the adapter when new data is available
val callback = DiffUtil.calculateDiff(DiffCallback(list, newlist))
list.clear()
list.addAll(newlist)
callback.dispatchUpdatesTo(this)
}
Please help me with the issue. I am stuck with this for a while now.
Related
I am trying to display data from my Room database on RecyclerView, it works fine except when I fill in new data and try to update my RecyclerView it doesn't update. Here's part of the code that I tried using:
MainActivity.kt
val adapter = MainAdapter(myData)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
MainAdapter.kt
fun refresh(data: List<NewList>) {
val currentList: MutableList<MyList> = mutableListOf()
currentList.clear()
currentList.addAll(data)
notifyDataSetChanged()
}
I am trying to call this function from Fragment file so it can pass updated list from Room Database (It updates list once someone opens that fragment)
Fragment.kt
var myData = App.instance.db.getAllDao().getAll()
val adapter = MainAdapter(myData)
adapter.refresh(myData)
I printed updated list in the updateData() function and it does show updated list, as well as tried printing ItemCount and it also shows updated ItemCount, but does not update actual display with new data.
The adapter attached to the RecyclerView in MainActivity and in fragment are not same.
just delete adapter creation in Fragment and pass RecyclerView instance to Fragment and then set layout manager and adapter there instead of activity. lastly call updateData method in order to be able to populate adapter.
I have RecyclerView and ViewHolder holding items. Each item has list of other items. These are just inflated in for loop to container within RecyclerView items.
This inflated View is ViewPager with custom Adapter. I want to achieve swiping effect inside Recyclerview item's children.
But no content is visible.
Is this possible to do inside RecyclerView?
So in general RecyclerView item has its children and each children will load its children from API call on swipe.
Update#2:
ViewPager is working as intended if I add pages to list before adapter initialization, but if I rewrite pages from API call, it will add them but swipePager.adapter?.notifyDataSetChanged() will not refresh pages list. List has more than 1 child but I cant swipe because ViewPagerAdapter still thinks it has only 1 page.
This would be last thing I need to fix. I've read that notifyDataSetChanged() is bugged in ViewPager and it has different behaviour than for RecyclerViewAdapter. Anyone has some solution how to refresh adapter?
Custom ViewPager:
<?xml version="1.0" encoding="utf-8"?>
<com.my.project.SwipeViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="110dp"
android:overScrollMode="never"
xmlns:android="http://schemas.android.com/apk/res/android" />
Code inside onBind for single RecyclerView item:
parts.removeAllViews()
val li = LayoutInflater.from(root.context)
j.parts.forEachIndexed { i, p ->
val v = li.inflate(R.layout.pager_layout, parts, false).apply {
val swipePager = findViewById<SwipeViewPager>(R.id.pager)
var nextParts = emptyList<PageItem>()
val pages = mutableListOf<PageScreen>()
var alreadySwiped = false
val swipeListener = object : OnSwipeOutListener{
override fun onSwipeOutAtEnd(){
//call API on swipe
if (!alreadySwiped){
getUpcomingData(p.itemId){ list->
alreadySwiped = true
nextParts = list
pages.clear()
nextParts.forEach { np->
pages.add(
PageScreen(
a,
app,
map,
np,
onClick = {
onItemClicked(np.nextId)
}
)
)
}
swipePager.adapter?.notifyDataSetChanged()
}
}
}
override fun onSwipeOutAtStart(){
}
}
//add first item which is known
pages.add(PageScreen(
a,
app,
map,
p,
onClick = {
onItemClicked(p.nextId)
}
))
swipePager.apply {
setOnSwipeOutListener(swipeListener)
adapter = SwipePagerAdapter(pages.toList()).also { it.notifyDataSetChanged() }
}
}
parts += v
}
override fun setAdapter() {
adapter = WaybillAdapter(items, activity!!, this, recyclerView)
recyclerView.adapter = adapter
}
I am updating my adapter when I should add items for my recycler like
override fun addWaybills(list: ArrayList<Data>) {
items.addAll(list)
adapter.setLoaded(true)
}
Add my position of an recycler is jumping to start.
My question is how should update adapter by not changing current position?
In your activitie's onCreate or fragment's onCreateView create initialize your adapter with empty ArrayList and later when you fetch your data add items to your arraylist and call adapter.notifyDataSetChanged()
I am trying to retrieve data from firebase then display it in recycler view. I set the layout to be reverselayout = true. However, when I run the activity, the view starts at the bottom. I tried to change the initial position when the application is run using scrollToPosition as commented below, but still nothing changes. Does anyone have a solution related to this problem?
I've tried this: https://stackoverflow.com/a/26876044/7825519
But still start from bottom.
mRecyclerView!!.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
(mRecyclerView!!.layoutManager as LinearLayoutManager).reverseLayout = true
(mRecyclerView!!.layoutManager as LinearLayoutManager).stackFromEnd = true
mRecyclerView?.scrollToPosition(3)
// mRecyclerView?.smoothScrollToPosition(3)
// (mRecyclerView!!.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(3,0)
// mNestedScrollView?.scrollTo(0,3)
mAdapter = DiscoverAdapter(mItems, mPostKey, Static.mLikedPosts, this)
mRecyclerView!!.adapter = mAdapter
val spacingInPixels = resources.getDimensionPixelSize(R.dimen.margin_between_card)
mRecyclerView!!.addItemDecoration(SpacesItemDecoration(spacingInPixels))
First of all delete the both lines that make the recyclerview start from the botom.
Set the scrolling after you set the adapter:
mRecyclerView!!.adapter = mAdapter
mRecyclerView?.scrollToPosition(3)
Try to set scrollToPosition using post method of View
How can I get a current item in RecycerView? In my use-case RV item takes all screen space, so there is only one item at a time.
I've tried to google before, but nothing helpful was found.
The whole use case:
UI. The bottom navigation has a button that should call a certain method (let's say a foo() method) in fragment
this foo() method should get a visible item from the adapter and call a specific method in a presenter.
use this in your code
LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
and get your visible item using below code
mLayoutManager.findFirstVisibleItemPosition();
You could also use an OnItemClickListener on your RecyclerView's ListAdapter to determine the current item. In that case the item needs to be clicked though.
My solution
val position = (recyclerView?.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
val product = adapter?.getItemByPosition(position)
FirebaseCrash.logcat(Log.DEBUG, TAG, "onNavClickEvent = $product")
product?.let { productListViewModel?.order(it.id) }
The result is:
onNavClickEvent = Product(id=1, title='Cap', description='some descr.', price=10, photo='http://lorempixel.com/200/200/sports/1/')