I have linear layout at the bottom and I want to hide that view on scroll up and show on scroll down. I was able to achieve that with scroll listener on recycler view . But there is one problem, when you are scrolling slow view is flickering (showing and hiding fast).
This is my code
bottom = (LinearLayout) getActivity().findViewById(R.id.linerabottom);
recycleList.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
bottom.setVisibility(View.GONE);
} else {
bottom.setVisibility(View.VISIBLE);
}
}
});
Here is a video of the issue https://goo.gl/photos/TwUJjmPUA4kJCsaR8 .
Can you help me to find out what is the issue ?
Thank you.
This is normal, because your dy at some point of time fluctuates between dy >= 0 and dy < 0. If you want to achieve sort of quick return view, you should bound it to something like this:
recycleList.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mTotalDy += dy;
if (dy > 0 && mTotalDy >= bottom.getHeight()) {
bottom.setVisibility(View.GONE);
} else if(recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE && bottom.getVisiblity() == View.GONE) {
bottom.setVisibility(View.VISIBLE);
mTotalDy = 0;
}
}
});
Related
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.
I am using RecyclerView with CarouselLayoutManager library and i want to detect the most elevated item.
I have the method below to know if the user scrolled the recyclerview and change the current most elevated recyclerview item.
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int overallXScroll = 0;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
overallXScroll = overallXScroll + dx;
//if the scroll amount will change the recyclerView item order :
if(overallXScroll > 100){
Toast.makeText(MainActivity.this, "123", Toast.LENGTH_SHORT).show();
overallXScroll = 0;
}
Log.i("check","overallXScroll->" + overallXScroll);
}
});
This method works very slow and with a delay , what can i do ?
I'm trying to implement endless scrolling with recyclerview and it works fine. However, I noticed when the content size is smaller than the screen height, onScrolled is never called. I want to load more when scrolling down but I can't detect a scroll down gesture as onScrolled is never called and thus I can't get the dy value. I was wondering:
1) How can I get the scroll direction in this case
2) What's the best practice for situations like this? I am getting a set number of items per service call. What happens if the number of items returned does not fill the screen?
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
{
int lastVisibleItemPosition, visibleItemCount, totalItemCount;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
if(dy>0)
{
visibleItemCount = recyclerLayoutManager.getChildCount();
totalItemCount = recyclerLayoutManager.getItemCount();
lastVisibleItemPosition = recyclerLayoutManager.findLastVisibleItemPosition();
if (lastVisibleItemPosition >= totalItemCount)
{
if (!loading && dy>0 && moreToload)
{
loadMore();
}
}
}
}
});
Thanks!
onScrolled callback will also be called if visible item range changes after a layout calculation. please check link here.
This mean its always called once after finished load the items in to the recycler view
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerLayoutManager.getChildCount();
totalItemCount = recyclerLayoutManager.getItemCount();
lastVisibleItemPosition = recyclerLayoutManager.findLastVisibleItemPosition();
if ((totalItemCount == visibleItemCount) || //This mean you still have space in your screen
(dy > 0 && !loading && lastVisibleItemPosition >= totalItemCount)) {
// Call load more here
}
}
If server does not return any items, the recyclerView will not update and by default onScroll method will not called again until you reach the end of the recyclerView.
You can try the same but with RecyclerView.LayoutManager customization and handling overscroll
#Override
public int scrollVerticallyBy ( int dx, RecyclerView.Recycler recycler,
RecyclerView.State state ) {
int scrollRange = super.scrollVerticallyBy(dx, recycler, state);
int overscroll = dx - scrollRange;
if (overscroll > 0) {
// bottom overscroll
} else if (overscroll < 0) {
// top overscroll
}
return scrollRange;
}
Create a subclass of LinearLayoutManager which lets you listen to the onLayoutCompleted() event:
/**
* This class calls [mCallback] (instance of [OnLayoutCompleteCallback]) when all layout
* calculations are complete, e.g. following a call to
* [RecyclerView.Adapter.notifyDataSetChanged()] (or related methods).
*
* In a paginated listing, we will decide if load more needs to be called in the said callback.
*/
class NotifyingLinearLayoutManager(context: Context) : LinearLayoutManager(context, VERTICAL, false) {
var mCallback: OnLayoutCompleteCallback? = null
override fun onLayoutCompleted(state: RecyclerView.State?) {
super.onLayoutCompleted(state)
mCallback?.onLayoutComplete()
}
fun isLastItemCompletelyVisible() = findLastCompletelyVisibleItemPosition() == itemCount - 1
interface OnLayoutCompleteCallback {
fun onLayoutComplete()
}
}
In you listener, you can decide if a call to load more is needed or not:
mLayoutManager.mCallback = object : NotifyingLinearLayoutManager.OnLayoutCompleteCallback {
override fun onLayoutComplete() {
// here we know that the view has been updated.
loadMoreIfNeeded()
}
}
My loadMoreIfNeeded() looks like following:
private fun loadMoreIfNeeded() {
if (isAppropriateToLoadMoreItems() && !mLoadMoreRequestFired.getAndSet(true)) {
mListener.onLoadMore()
}
}
private fun isAppropriateToLoadMoreItems() =
mLoadMoreEnabled && itemCount > 1 && mLayoutManager.isLastItemCompletelyVisible()
These are following fields used in the previous step:
private var mLoadMoreRequestFired = AtomicBoolean(false)
// makes sure that we fire a single load more call at a time
private var mLoadMoreEnabled = false
// this flag is used by the footer (containing the ProgressBar) to show or hide it.
Just remove the if(dy > 0) on your code.
Final snippet
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener()
{
int lastVisibleItemPosition, visibleItemCount, totalItemCount;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerLayoutManager.getChildCount();
totalItemCount = recyclerLayoutManager.getItemCount();
lastVisibleItemPosition = recyclerLayoutManager.findLastVisibleItemPosition();
if (lastVisibleItemPosition >= totalItemCount)
{
if (!loading && dy>0 && moreToload)
{
loadMore();
}
}
}
});
Want to get scroll coordinates of a RecycleView like this:
int scrollX = recycleView.getScrollX();
But it always returns 0, even though view is already scrolled. Why it does not help? How is it possible get the shifted distance of the content?
Try with this
int scrollX = recycleView.computeHorizontalScrollOffset();
You can use OnScrollListener to achieve this:
private int xScroll = 0;
...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
xScroll += dx;
}
});
I am working on android app and using recyclerview and scrollview in some activities. Now I want to show some layout when recyclerview/scrollview scrolls in up direction and hide the layout when it scrolls in down direction.
I want a function which can tell that recyclerview/scrollview is scrolling in up or down direction.
Please help if anyone know how to do this for both recyclerview and scrollview.
Thanks a lot in advanced.
Try this way:
private static int firstVisibleInListview;
firstVisibleInListview = yourLayoutManager.findFirstVisibleItemPosition();
In your scroll listener:
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
int currentFirstVisible = yourLayoutManager.findFirstVisibleItemPosition();
if(currentFirstVisible > firstVisibleInListview)
Log.i("RecyclerView scrolled: ", "scroll up!");
else
Log.i("RecyclerView scrolled: ", "scroll down!");
firstVisibleInListview = currentFirstVisible;
}
A sure short answer for this issue
scroll.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
int x = scrollY - oldScrollY;
if (x > 0) {
//scroll up
} else if (x < 0) {
//scroll down
} else {
}
}
});