findLastCompletelyVisibleItemPosition : Returns the adapter position of the last fully visible view. This position does not include adapter changes that were dispatched after the last layout pass.
findLastVisibleItemPosition: Returns the adapter position of the last visible view. This position does not include adapter changes that were dispatched after the last layout pass.
I have 16 items . So when scrolled till end (when last item is fully visible) both method gives result as 16. But when i scroll till end but last item is half visible findLastCompletelyVisibleItemPosition shows 14 and findLastVisibleItemPosition shows 15.
Can someone explain me why it is displaying 14 ? and what is the exact difference between these two function.
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
RecyclerView.LayoutManager linearLayoutManager = recyclerView.getLayoutManager();
if(linearLayoutManager != null && linearLayoutManager instanceof LinearLayoutManager) {
int position = ((LinearLayoutManager) linearLayoutManager).findLastCompletelyVisibleItemPosition();
int position1 = ((LinearLayoutManager) linearLayoutManager).findLastVisibleItemPosition();
Log.d(TAG, "position: " + position);
Log.d(TAG, "position1: " + position1);
}
}
If you have 16 items it is impossible to return 16 as the visible position since your last position is the 15th.
Secondly it looks pretty straight forward to understand what each method does from their naming. If the last position is the 15th and you can see half of it, findLastCompletelyVisibleItemPosition will return 14 and findLastVisibleItemPosition will return 15.
Related
I'm trying to implement an horizontal RecycleView in my Android app that displays content with never-ending scroll animation. See example of required UI here.
My question is, what would be the best practice on how to achieve this goal? (RecycleView should support scroll gestures from the user).
If I'm correct you're looking for the endless scrolling or infinite scrolling (Not Official names for that)
If it is correct then it shows like say you have 5 images in your recyclerview now when the user reaches the end item then the recycler view starts again from the first item like
item1, item2, item3, item4, item5 and again item5, item1, item2.....
To achieve this
Go to your adapter class and do the following code.
#Override
public int getItemCount() {
return items == null ? 0 : items.size() * 2; //Here instead of items.size(); you have to change like this
}
Now in your OnBindViewHolder method
MODEL_CLASS item = items.get(position%items.size());
Now head over to the activity where you set the adapter to recyclerview and add the following code
YOUR_ADAPTER_NAME.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstItemVisible = linearLayoutManager.findFirstVisibleItemPosition();
if (firstItemVisible != 0 && firstItemVisible % YOUR_LIST_ITEM.size() == 0) {
recyclerView.getLayoutManager().scrollToPosition(0);
}
}
});
By this you can see the items when scrolled to right it continues loops in your list.
How do I get the position of a list item that's on focus after scrolling the RecyclerView? Will this work?
recyclerview1.addOnScrollListener(new RecyclerView.OnScrollListener())
If yes, then how?
Yes there are two ways of doing this.
One using onBindViewHolder , in onBindViewHolder add
Log.d("onBindViewHolder", "position =>"+position);
count++; //for the position
but remember in this way count call several time for each news and you must count only in first call.
Other way around is using layoutManagers i.e LinearLayoutManager or GridLayoutManager by doing this
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
int lastVisiblePosition = layoutManager.findLastVisibleItemPosition();
OR for scrollListener you can do this
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//you can use other methods as per your requirements
Toast.makeText(mContext, "Scrolled to"+layoutManager.findLastCompletelyVisibleItemPosition(), Toast.LENGTH_SHORT).show();
})
Hope this helps you.
I just faced this problem, and i got I put the recyclerView in scrollView So please never put the recyclerview inside the scrollView that may also cause mLayoutManager.findFirstVisibleItemPosition() that always return a fixed value. and also check recyclerview.setNestedScrollingEnabled(false);
I have a vertically scrolling RecyclerView with horizontally scrolling inner RecyclerViews just like this.
With this implementation, users can scroll each horizontal recyclerview synchronously. However, when a user scroll vertically to the parent recyclerView, a new horizontal recyclerview which has just attached on window doesn't display on same scroll x position. This is normal. Because it has just created.
So, I had tried to scroll to the scrolled position before it was displayed. Just like this:
Note: this is in adapter of the parent recyclerview whose orientation is vertical.
#Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder);
CellColumnViewHolder viewHolder = (CellColumnViewHolder) holder;
if (m_nXPosition != 0) {
// this doesn't work properly
viewHolder.m_jRecyclerView.scrollBy(m_nXPosition, 0);
}
}
As you can see, scrollBy doesn't effect for row 10, row 11, row 12 and row 13 After that, I debugged the code to be able find out find out what's happening. When I set scroll position using scrollBy, childCount() return zero for row 10, row 11, row 12 and row 13 So they don't scroll. But why ? and Why others work ?
How can I fix this ?
Is onViewAttachedToWindow right place to scroll new attached recyclervViews ?
Note: I have also test scrollToPosition(), it doesn't get any problem like this. But I can't use it at my case. Because users can scroll to the any x position which may not the exact position. So I need to set scroll position using x value instead of the position.
Edit: You can check The source code
I found a solution that is use scrollToPositionWithOffset method instead using scrollBy. Even if both of two scroll another position, they have really different work process in back side.
For example: if you try to use scrollBy to scroll any pixel position and your recyclerView had not been set any adapter which means there is no any data to display and so it has no any items yet, then scrollBy doesn't work. RecyclerView uses its layoutManager's scrollBy method. So in my case, I am using LinearLayoutManager to the horizontal recyclerViews.
Lets see what it's doing :
int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getChildCount() == 0 || dy == 0) {
return 0;
}
mLayoutState.mRecycle = true;
ensureLayoutState();
final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
final int absDy = Math.abs(dy);
updateLayoutState(layoutDirection, absDy, true, state);
final int consumed = mLayoutState.mScrollingOffset
+ fill(recycler, mLayoutState, state, false);
if (consumed < 0) {
if (DEBUG) {
Log.d(TAG, "Don't have any more elements to scroll");
}
return 0;
}
final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
mOrientationHelper.offsetChildren(-scrolled);
if (DEBUG) {
Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
}
mLayoutState.mLastScrollDelta = scrolled;
return scrolled;
}
As you can see scrollBy ignores the scroll intentions if there is no any child at that time.
if (getChildCount() == 0 || dy == 0) {
return 0;
}
On the other hand scrollToPosition can work perfectly even if there is no any set data yet.
According to the Pro RecyclerView slide, the below sample works perfectly. However you can not do that with scrollBy.
void onCreate(SavedInstanceState state) {
....
mRecyclerView.scrollToPosition(selectedPosition);
mRecyclerView.setAdapter(myAdapter);
}
As a result, I have changed little thing to use scrollToPositionWithOffset().
Before this implementation I was calculating the exact scroll x position as a pixel.
After that, when the scroll came idle state, calculating the first complete visible position to the first parameter of the scrollToPositionWithOffset().
For second parameter which is the offset, I am getting the value using view.getLeft() function which helps to get left position of this view relative to its parent.
And it works perfectly!!
I have a recyclerview in my android project which displays media contents within each view. What I'm trying to achieve is that I'm able to play/pause media as I scroll up and down. I need to get the adapter position of the completely visible view. I'm doing something like this.
In my activity fragment I have this:
layoutmanager = new LinearLayoutManager(Activity);
adapter = new FeedAdapter(vid, userName, this.Context);
feeditem.SetLayoutManager(layoutmanager);
feeditem.SetAdapter(adapter);
var onScrollListener = new XamarinRecyclerViewOnScrollListener(Activity, layoutmanager, adapter);
The scroll listener event looks like this:
public override void OnScrollStateChanged(RecyclerView recyclerView, int newState)
{
base.OnScrollStateChanged(recyclerView, newState);
if (newState == (int)ScrollState.Idle)
{
layoutmanager = (LinearLayoutManager)recyclerView.GetLayoutManager();
int firstVisiblePosition = layoutmanager.FindFirstCompletelyVisibleItemPosition();
int visible = layoutmanager.FindFirstVisibleItemPosition();
int last = layoutmanager.FindLastVisibleItemPosition();
if (firstVisiblePosition >= 0)
{
if (oldFocusedLayout != null)
{
Toast.MakeText(ctx, "Stop Video", ToastLength.Long).Show();
}
}
currentFocusedLayout = layoutmanager.FindViewByPosition(firstVisiblePosition);
Toast.MakeText(ctx, "Play video", ToastLength.Long).Show();
oldFocusedLayout = currentFocusedLayout;
}
}
feeditem.AddOnScrollListener(onScrollListener);
The issue is that the linearlayout manager method FindFirstCompletelyVisibleItemPosition always returns -1 even when the view is completely visible. Other methods like FindFirstVisibleItemPosition and FindLastVisibleItemPosition gives the correct position of the view.
Any idea what might be the issue here?
layoutManager.findFirstCompletelyVisibleItemPosition()
FROM DOCUMENT
Returns the adapter position of the FIRST FULLY VISIBLE view. This position does not include adapter changes that were dispatched after the last layout pass.
It mean that, at least one listitem view should be fully visible otherwise, it give -1 (NO_POSITION)
FROM TESTING
This will work and give correct position...
This won't work and give -1 (NO_POSITION), because two ListItem view is not fully visible.
So I have a ListView and also up and down buttons.
My goal is to scroll the ListView by the it's height everytime I press one of the buttons, simple up / down navigation.
The scrolling does work, sort of. When I press down, the ListView scrolls by the correct distance every time it scrolls.
However, only the up button works as intended. The down button has problems which I believe are due to the ListView's first visible scroll position not updating.
When I press down, the onNavigationDownPressed() method occurs as seen below. This scrolls the correct amount down the ListView with a starting ListView firstVisiblePosition at 0. The next time that method is called, the first visible position is still 0.
But if I manually scroll the ListView down ever so slightly and then press down, it scrolls down correctly and then the same thing, it won't go down any more due to the firstVisiblePostion not changing unless I scroll first to make the position update.
// Scroll up
#Override
public void onNavigationUpPressed()
{
Log.i("MAKE UP, FIRST VISIBLE POSITION: ", String.valueOf(listView.getFirstVisiblePosition()));
listView.setSelectionFromTop(listView.getFirstVisiblePosition(), fragmentHeight);
}
// Scroll down
#Override
public void onNavigationDownPressed()
{
Log.i("MAKE DOWN, FIRST VISIBLE POSITION: ", String.valueOf(listView.getFirstVisiblePosition()));
listView.setSelectionFromTop(listView.getFirstVisiblePosition(), -fragmentHeight);
}
Any ideas?
Cheers
Perhaps you should try to call :
listView.smoothScrollToPosition(yourPosition);
Another route is to manually set the scroll of the items but the first approach should work for you.
Solution:
I set an onScrollListener for the ListView which notified me both when the ListView had scrolled and some other handy parameters such as first visible item index, total number of items visible on screen etc. so I stored these variables.
listView.setOnScrollListener(new OnScrollListener()
{
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
Log.i("MMY: ", "Scroll");
Log.i("MMY: ", "First visible item: " + String.valueOf(firstVisibleItem));
Log.i("MMY: ", "Visible item count: " + String.valueOf(visibleItemCount));
// The variables that belong to this class now equal ->
General_Navigation_Fragment.this.visibleItemCount = visibleItemCount;
General_Navigation_Fragment.this.firstVisibleItem = firstVisibleItem;
totalCount = totalItemCount;
}
});
When the down button was pressed, I got the index of the first visible item and I got the number of visible items (n) on the screen and then scrolled down to current position + n. Simply did the reverse for scrolling up.
// Scroll up
#Override
public void onNavigationUpPressed()
{
currentVisibleItem = firstVisibleItem - visibleItemCount;
if (currentVisibleItem < 0)
{
listView.setSelection(0);
}
else
listView.setSelection(currentVisibleItem - 1);
}
// Scroll down
#Override
public void onNavigationDownPressed()
{
currentVisibleItem = visibleItemCount + firstVisibleItem;
listView.setSelection(currentVisibleItem - 1);
}
Since I have varying size items in my ListView, this way seemed the easiest way to go about it.