I am developing an App for Android TV via Nexus Player. In my App, I use the VerticalGridFragment to show TV shows. For shows' number is 2000+, I want to use page loading when the VerticalGridView scrolls to its bottom.
However, I get stuck on how to judge the VerticalGridView has scrolled to bottom.
I use reflect to get the VerticalGridView,and addScrollListener() to it.Here the codes:
private void addGridScrollListener() {
try {
Class<VerticalGridFragment> VerticalGridFragmentClass = VerticalGridFragment.class;
Field verticalGridViewHolder = VerticalGridFragmentClass.getDeclaredField("mGridViewHolder");
verticalGridViewHolder.setAccessible(true);
VerticalGridPresenter.ViewHolder viewHolder = (VerticalGridPresenter.ViewHolder) verticalGridViewHolder.get(this);
VerticalGridView gridView = viewHolder.getGridView();
gridView.addOnScrollListener(scrollListener);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
};
RecyclerView doesn't has the function such as getLastVisiableItemPosition, so I can't know if the VerticalGridView has scrolled to bottom or not.
What should I do in the scrollListener ? Or is there any other solutions?
Thanks!
I found the solution:
private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState != RecyclerView.SCROLL_STATE_IDLE) {
return;
}
//get current last child View
View lastChildView = recyclerView.getLayoutManager().getChildAt(recyclerView.getLayoutManager().getChildCount() - 1);
//get the bottom
int lastChildBottom = lastChildView.getBottom();
// get recyclerView's bottom
int recyclerBottom = recyclerView.getBottom() - recyclerView.getPaddingBottom();
//get last childview's position
int lastPosition = recyclerView.getLayoutManager().getPosition(lastChildView);
if (lastChildBottom == recyclerBottom && lastPosition == recyclerView.getLayoutManager().getItemCount() - 1) {
// yes to bottom.
}
}
};
Related
I have a horizontal Recycler view implemented with SnapHelper to get a carousal effect. In my ViewHolder I have a function to animate the views inside Recycler item. So whenever an item is scrolled to center (selected) in SnapHelper I need to start the animation. I have tried following code but its not working. Sometimes I am getting viewholder as null.
widgetScrollView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(final RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
int pos = ((LinearLayoutManager) widgetScrollView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
ResultTallyAdapter.ViewHolder viewHolder = (ResultTallyAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(pos);
viewHolder.startAnimation(MainActivity.this, pos);
}
}, 1000);
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
I was trying same thing like you. But code is different. I use SnapHelper to find current view and get position from this view and start operation on adapter by this position. You can get idea following code, may help you
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
synchronized (this) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
//calculatePositionAndScroll(recyclerView);
View view = snapHelper.findSnapView(recyclerView.getLayoutManager());
if (view != null) {
int pos = recyclerView.getLayoutManager().getPosition(view);
Log.d("RecyclerViewTest", "position: " + pos + " item: " + itemList.get(pos).getName());
makeSelectedItem(pos); // do your stuff on this view
// adapter.notifyDataSetChanged();
}
}
}
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
I have a recycleView that I need to observe when the last item is reached but I have notice the it always indicate that I reached the last item even if I haven't scrolled yet.
My code for setting up the recycler:
newsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
newsRecyclerView.setFocusable(false);
newsRecyclerView.setNestedScrollingEnabled(false);
newsAdapter = new NewsAdapter(getContext(), newsDetails, categoryNumber);
newsRecyclerView.setAdapter(newsAdapter);
layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
My xml code is:
<android.support.v7.widget.RecyclerView
android:id="#+id/news_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/news_top_stories_title_text_view" />
This is my code that I put in my Util, and use anywhere.
Util.setRecyclerViewLastPositionListner(rv, linearLayoutManager , new UtilitiesV2.OnLastPositionReached() {
#Override
public void onReached() {
// last position reached
}
});
Put this in Util.
private boolean userScrolled = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
public interface OnLastPositionReached {
void onReached();
}
public void setRecyclerViewLastPositionListner(RecyclerView rvBooksMockTest, final LinearLayoutManager mLayoutManager, final OnLastPositionReached onLastPositionReached) {
rvBooksMockTest.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
userScrolled = true;
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// Here get the child count, item count and visibleitems
// from layout manager
visibleItemCount = mLayoutManager.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
// Now check if userScrolled is true and also check if
// the item is end then update recycler view and set
// userScrolled to false
if (userScrolled && (visibleItemCount + pastVisiblesItems) == totalItemCount) {
userScrolled = false;
if (onLastPositionReached != null) onLastPositionReached.onReached();
}
}
});
}
Update
According to your requirement, here is NestedScrollView bottom reach listener.
nestedScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
if (nestedScrollView != null) {
if (nestedScrollView.getChildAt(0).getBottom() <= (nestedScrollView.getHeight() + nestedScrollView.getScrollY())) {
//scroll view is at bottom
} else {
//scroll view is not at bottom
}
}
}
});
Thanks to Khemraj who give the tip for solution
because I have a recyclerview inside NestedScrollView and coordinator layout as parent for them
I have solved my problem like this:
public Disposable observeNestedScroll(LoadMoreListener listener) {
return RxNestedScrollView.scrollChangeEvents(nestedScrollView)
.subscribe(
viewScrollChangeEvent -> {
NestedScrollView nestedScrollView =(NestedScrollView) viewScrollChangeEvent.view();
if(nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1) != null) {
if ((viewScrollChangeEvent.scrollY() >= (nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1).getMeasuredHeight() - nestedScrollView.getMeasuredHeight())) &&
viewScrollChangeEvent.scrollY() > viewScrollChangeEvent.oldScrollY()) {
listener.onLoadMore();
}
}
});
}
I've seen to many responses for this question and I stand that all of them don't give accurate behavior as an outcome. However if you follow this approach I'm positive you'll get the best behavior.
rvCategories is your RecyclerView
categoriesList is the list passed to your adapter
binding.rvCategories.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val position = (recyclerView.layoutManager as LinearLayoutManager).findLastCompletelyVisibleItemPosition()
if (position + 1 == categoriesList.size) {
// END OF RECYCLERVIEW IS REACHED
} else {
// END OF RECYCLERVIEW IS NOT REACHED
}
}
})
How can I get object from recyclerview when it leaves screen while scrolling? For example I have note with id 11 how can I get this note id whenever it leaves screen?
One way you could do it:
Boolean upScrolling;
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
LinearLayoutManager mgr = (LinearLayoutManager) recyclerView.getLayoutManager();
int topPosition = mgr.findFirstVisibleItemPosition();
/// your code
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy < 0) {
upScrolling = true;
} else if (dy > 0) {
upScrolling = false;
}
}
});
After scrolling up, in the listener you get the 1st visible item. At the position your code you write the code to check if the 1st visible item is the one below the item you want. If this is the case then you get the time it went off the screen.
Edited to check if it scrolls up or down.
my app has 3 fragments that are being inflated from mainActivity with BottomNavigationView. One of the fragments uses RecyclerView and is scrollable, with BottomNavigationView the last item in recyclerView is being covered up. According to Google's guidelines bottomNavigationView should hide on scroll down and show on scroll up. It's easy enough to make it hide from the mainAcitivity but it doesn't work when Im trying to hide it from the other fragment. How do I do this properly?
This code from recyclerView checks for the scroll state:
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {
//mainActivity.setNavigationVisibility(false);
} else if (dy < 0 ) {
//mainActivity.setNavigationVisibility(true);
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
And should call this method in the mainActivity that hides the bottomNavigationView:
public void setNavigationVisibility(boolean visible) {
if (navigation.isShown() && !visible) {
navigation.setVisibility(View.GONE);
}
else if (!navigation.isShown() && visible){
navigation.setVisibility(View.VISIBLE);
}
}
try this
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) {
((MainActivity)getActivity()).setNavigationVisibility(false);
} else if (dy < 0 ) {
((MainActivity)getActivity()).setNavigationVisibility(true);
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});
When you use activity method from fragment then you have to create your method public and you can access that method by typecasting.
Basically I want my recyclerview to automatically scroll to a position where the item is not half shown. Like the one in googleplay.
I have written a code
public void scrollToVisible(){
int firstVisibleItemPosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
View view = recyclerView.getLayoutManager().getChildAt(0);
if (firstVisibleItemPosition > 0 && view != null) {
int offsetTop = view.getTop();
if (firstVisibleItemPosition - 1 >= 0 && adapter.getItemCount() > 0) {
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(firstVisibleItemPosition - 1, offsetTop);
}
}
}
The problem comes next. I dont know where to put this code. I have a vague idea to put it when the recyclerview stops on scrolling but I've been searching for quite some time now and i cant find such a method. when i put it on the onScroll some unexpected behavior comes out
You may create a CustomRecyclerView extending RecyclerView
public class CustomRecyclerView extends RecyclerView {
#Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
// check if scrolling has stopped
if (state == SCROLL_STATE_IDLE) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager();
// use code here
}
}
If it maybe of any help to someone:
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Log.d("y value",String.valueOf(dy));
if (dy > 0) {
//scrolling up
} else {
// Scrolling down
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
// Do something
Log.e("SCROLL_STATE_FLING","SCROLL_STATE_FLING");
} else if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
Log.e("SCROLLTOUCH_SCROLL","SCROLL_STATE_TOUCH_SCROLL");
//slideUp(party_view);
// Do something
} else if (newState==AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
// Do something
//slideDown(party_view);
Log.e("SCROLL_STATE_IDLE","SCROLL_STATE_IDLE");
}
}
});
complete example with UI synchronization
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
Handler handler = new Handler(getMainLooper());
if (newState == 0) {
handler.removeCallbacks(MainActivity.this::hideFab);
handler.postDelayed(MainActivity.this::showFab, 400);
} else {
handler.removeCallbacks(MainActivity.this::showFab);
handler.postDelayed(MainActivity.this::hideFab, 100);
}
}
});
private void hideFab() {
addFile.hide();
addText.hide();
camera.hide();
}
private void showFab() {
addFile.show();
addText.show();
camera.show();
}