I wanna when listview 1 is scrolled by user, listview 2 also is scroll like listview 1 (exactly in pixel).
I tried to scroll listview 2 by smoothScrollByOffset(offset), but it scrolls more than listview 1.
I dont know why?
What happened with smoothScrollByOffset(offset) or I miss something?
Further information:
Code to scroll listview 2 (in onScroll callback of listview 1):
int previsous = 0;
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
//get current position in pixel of listview 1
View c = lv1.getChildAt(0);
int scrolly = -c.getTop() + lv1.getFirstVisiblePosition() * c.getHeight();
//Difference in pixel with the last scroll
int delta = scrolly - previsous;
Log.d("scroll", "listview 1 has scrolled: " +
scrolly + "| Difference with the last " + delta);
//scroll listview 2 by delta
lv2.smoothScrollByOffset(delta);
previsous = scrolly;
}
Screen shot for diagnostic:
Try to use smoothScrollToPosition (int position) instead of smoothScrollByOffset (int offset), I hope this will help you, Let me know what happened.
You are using the wrong method. The smoothScrollByOffset() doesn't accept pixels, but number of positions. Use smoothScrollBy(int distance, int duration)
Related
I want to implement the google photos application timeline feature in the recycler view in which we can show the year of images as a label while scrolling as shown in below image.
Can anyone help in this or implemented something like this.
I have solved this problem by simply adding a new Linear-layout on top of the recyclerview with a child textview in the layout and then by calculating the height ratio(as per the count of images per year) with respect to the total height available.Also considering the minimum height of textview to 100 if the ratio is too small.
private float getEffectiveHeight(float totalHeight, float count, float totalCount) {
if (count * (totalHeight / totalCount) < 100)
return 100;
else
return count * (totalHeight / totalCount);
}
I'm doing almost the same as here (https://github.com/kmshack/Android-ParallaxHeaderViewPager), but with RecyclerView in tabs.
RecyclerView in one tab has items with big height and the first one is not always fully visible under header.
At some point I call RecyclerView's computeVerticalScrollOffset() (that calls StaggeredGridLayoutManager's method) and it returns completely wrong values. If I change header height to make first item fully visible I'm getting right values.
Is there any known solution/fix for this?
P.S. I also use LinearLayoutManager and always get right values even if first item is not fully visible under header
I believe the error is with the findFirstVisiblePosition() and findLastVisiblePosition() used in the computeScrollOffset() function arguments. Even if I use almost equal row height computeVerticalScrollOffset() returns really weird numbers.
If we replace them with the recyclerView.getChildAt(0) and recyclerView.getChildAt(recyclerView.getChildCount() - 1) that actually works.
So we can write our own function:
View firstItemView = recyclerView.getChildAt(0);
View lastItemView = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
int firstItem = recyclerView.getChildLayoutPosition(firstItemView);
int lastItem = recyclerView.getChildLayoutPosition(lastItemView);
int itemsBefore = firstItem;
int laidOutArea = recyclerView.getLayoutManager().getDecoratedBottom(lastItemView) - recyclerView.getLayoutManager().getDecoratedTop(firstItemView);
int itemRange = lastItem - firstItem + 1;
float avgSizePerRow = (float) laidOutArea / itemRange;
int offset = (int) (itemsBefore * avgSizePerRow + recyclerView.getLayoutManager().getPaddingTop() - recyclerView.getLayoutManager().getDecoratedTop(firstItemView));
Let's follow the source code of RecyclerView:
#Override
public int computeVerticalScrollOffset() {
return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
}
mLayout is an instance of LayoutManager. In our case, it should belong to StaggeredGridLayoutManager. Let's check there:
#Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
return computeScrollOffset(state);
}
then here:
private int computeScrollOffset(RecyclerView.State state) {
if (getChildCount() == 0) {
return 0;
}
ensureOrientationHelper();
return ScrollbarHelper.computeScrollOffset(state, mPrimaryOrientation,
findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true)
, findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true),
this, mSmoothScrollbarEnabled, mShouldReverseLayout);
}
And finally we go to ScrollbarHelper.computeScrollOffset:
/**
* #param startChild View closest to start of the list. (top or left)
* #param endChild View closest to end of the list (bottom or right)
*/
static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
View startChild, View endChild, RecyclerView.LayoutManager lm,
boolean smoothScrollbarEnabled, boolean reverseLayout) {
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
endChild == null) {
return 0;
}
final int minPosition = Math.min(lm.getPosition(startChild),
lm.getPosition(endChild));
final int maxPosition = Math.max(lm.getPosition(startChild),
lm.getPosition(endChild));
final int itemsBefore = reverseLayout
? Math.max(0, state.getItemCount() - maxPosition - 1)
: Math.max(0, minPosition);
if (!smoothScrollbarEnabled) {
return itemsBefore;
}
final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) -
orientation.getDecoratedStart(startChild));
final int itemRange = Math.abs(lm.getPosition(startChild) -
lm.getPosition(endChild)) + 1;
final float avgSizePerRow = (float) laidOutArea / itemRange;
return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
- orientation.getDecoratedStart(startChild)));
}
And we should focus on last statement. This method returns the offset calculated by items * avgHeight, which isn't accurate when our items have variety of height.
As a result, when we use a LinearLayoutManager of GridLayoutManager, since height of each row is confirmed, computeVerticalScrollOffset() will return correct distance. However, unfortunately, when we use StaggeredGridLayoutManager, we cannot get accurate scroll offset by it.
P.S I know the reason why it isn't correct, but I don't know how to get correct scroll distance. If anyone knows, please visit: How to get correct scroll distance of RecyclerView when using a StaggeredGridLayoutManager? and post your answer.
Is there any way similar like using smoothScrollToPosition with control over speed or the time it takes to scroll smoothly?
Now i am using below snippet to scrool to pos
int firstVisible = lv.getFirstVisiblePosition();
int lastVisible = lv.getLastVisiblePosition();
if (firstVisible > pos)
lv.smoothScrollToPosition(pos, pos - firstVisible - 2);
if (pos > lastVisible)
lv.smoothScrollToPosition(pos, pos + lastVisible + 3);
From this i modified to aabove snippet as below
int childheight = lv.getChildAt(firstVisible).getHeight();
if (firstVisible > pos)
lv.smoothScrollBy(childheight*pos, 800);
if (pos > lastVisible)
lv.smoothScrollBy(childheight*pos, 1200);
But, i get nullpointer exceptions at
int childheight = lv.getChildAt(firstVisible).getHeight();
Edit
i am in need of scrooling automatically to a childView by a button press by user,say MENU button. I am not facing the problem of list scrolling slowly because of doing resource intensive task to populate listview. All i want is the functionality of smoothScrollToPosition with smoothscrooltoPosition with velocity control
I am trying to build my own grid view functions - extending on the GridView.
The only thing I cannot solve is how to get the current scroll position of the GridView.
getScrollY() does always return 0, and the onScrollListener's parameters are just a range of visible child views, not the actual scroll position.
This does not seem very difficult, but I just can't find a solution in the web.
Anybody here who have an idea?
I did not find any good solution,
but this one is at least able to maintain the scroll position kind of pixel-perfectly:
int offset = (int)(<your vertical spacing in dp> * getResources().getDisplayMetrics().density);
int index = mGrid.getFirstVisiblePosition();
final View first = container.getChildAt(0);
if (null != first) {
offset -= first.getTop();
}
// Destroy the position through rotation or whatever here!
mGrid.setSelection(index);
mGrid.scrollBy(0, offset);
By that you can not get an absolute scroll position, but a visible item + displacement pair.
NOTES:
This is meant for API 8+.
You can get with mGrid.getVerticalSpacing() in API 16+.
You can use mGrid.smoothScrollToPositionFromTop(index, offset) in API 11+ instead of the last two lines.
Hope that helps and gives you an idea.
On Gingerbread, GridView getScrollY() works in some situations, and in some doesn't. Here is an alternative based on the first answer. The row height and the number of columns have to be known (and all rows must have equal height):
public int getGridScrollY()
{
int pos, itemY = 0;
View view;
pos = getFirstVisiblePosition();
view = getChildAt(0);
if(view != null)
itemY = view.getTop();
return YFromPos(pos) - itemY;
}
private int YFromPos(int pos)
{
int row = pos / m_numColumns;
if(pos - row * m_numColumns > 0)
++row;
return row * m_rowHeight;
}
The first answer also gives a good clue on how to pixel-scroll a GridView. Here is a generalized solution, which will scroll a GridView equivalent to scrollTo(0, scrollY):
public void scrollGridToY(int scrollY)
{
int row, off, oldOff, oldY, item;
// calc old offset:
oldY = getScrollY(); // getGridScrollY() will not work here
row = oldY / m_rowHeight;
oldOff = oldY - row * m_rowHeight;
// calc new offset and item:
row = scrollY / m_rowHeight;
off = scrollY - row * m_rowHeight;
item = row * m_numColumns;
setSelection(item);
scrollBy(0, off - oldOff);
}
The functions are implemented inside a subclassed GridView, but they can be easily recoded as external.
I am trying to build my own grid view functions - extending on the GridView.
The only thing I cannot solve is how to get the current scroll position of the GridView.
getScrollY() does always return 0, and the onScrollListener's parameters are just a range of visible child views, not the actual scroll position.
This does not seem very difficult, but I just can't find a solution in the web.
Anybody here who have an idea?
I did not find any good solution,
but this one is at least able to maintain the scroll position kind of pixel-perfectly:
int offset = (int)(<your vertical spacing in dp> * getResources().getDisplayMetrics().density);
int index = mGrid.getFirstVisiblePosition();
final View first = container.getChildAt(0);
if (null != first) {
offset -= first.getTop();
}
// Destroy the position through rotation or whatever here!
mGrid.setSelection(index);
mGrid.scrollBy(0, offset);
By that you can not get an absolute scroll position, but a visible item + displacement pair.
NOTES:
This is meant for API 8+.
You can get with mGrid.getVerticalSpacing() in API 16+.
You can use mGrid.smoothScrollToPositionFromTop(index, offset) in API 11+ instead of the last two lines.
Hope that helps and gives you an idea.
On Gingerbread, GridView getScrollY() works in some situations, and in some doesn't. Here is an alternative based on the first answer. The row height and the number of columns have to be known (and all rows must have equal height):
public int getGridScrollY()
{
int pos, itemY = 0;
View view;
pos = getFirstVisiblePosition();
view = getChildAt(0);
if(view != null)
itemY = view.getTop();
return YFromPos(pos) - itemY;
}
private int YFromPos(int pos)
{
int row = pos / m_numColumns;
if(pos - row * m_numColumns > 0)
++row;
return row * m_rowHeight;
}
The first answer also gives a good clue on how to pixel-scroll a GridView. Here is a generalized solution, which will scroll a GridView equivalent to scrollTo(0, scrollY):
public void scrollGridToY(int scrollY)
{
int row, off, oldOff, oldY, item;
// calc old offset:
oldY = getScrollY(); // getGridScrollY() will not work here
row = oldY / m_rowHeight;
oldOff = oldY - row * m_rowHeight;
// calc new offset and item:
row = scrollY / m_rowHeight;
off = scrollY - row * m_rowHeight;
item = row * m_numColumns;
setSelection(item);
scrollBy(0, off - oldOff);
}
The functions are implemented inside a subclassed GridView, but they can be easily recoded as external.