Android onScrolled not called when View is set to GONE - java

I am trying to hide a RelativeLayout when I scroll up and show it when I scroll down. onScroll works fine and is invoked every time until View is set to GONE.
final RelativeLayout placeHeaderMain = findViewById(R.id.place_header_main);
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
// Scrolling up
placeHeaderMain.setVisibility(View.GONE);
} else {
// Scrolling down
placeHeaderMain.setVisibility(View.VISIBLE);
}
}
});
I want my listener to continue working after setting the View to Gone in order to make it Visible when scrolled down.
Thanks in advance.

Are there enough items to be scrolled?
That code above won't be triggered if dy == 0. It could be not enough items to make the scroll and it will return dy equal to 0, father more it won't to call onScroll(...)
What dy do you have when RelativeLayout has hidden?
Try to check that method below:
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}

Try to set the view to INVISIBLE and not to GONE. when you set any view to View.GONE he is invisible and it doesn't take any space inside your layout , but when you set a view to View.INVISIBLE like before he will be invisible but unlike View.GONE your view still takes up space inside the layout.

Related

RecyclerView get height of first visible item

How can i calculate the height of the first visible item in my recyclerview ?
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
Log.d("scroll_stop","The RecyclerView is not scrolling : ");
View firstItemView = mGLM.findViewByPosition(mGLM.findFirstVisibleItemPosition());
View mVizibleView= mPlm.findViewByPosition(mGLM.findFirstVisibleItemPosition());
}
});
But this give me the full height of the view
Taking the Size of any RecyclerView Item is pointless, If you getHeight() or getWidth() of any item it's just gonna give a Null Error and your app is gonna crash.
What you can do, Is to Use your ViewHolder from recyclerView Adapter and First set a Specific Height to any Item you want and Then use that Height somewhere Else.
Look at the Code Below, Maybe helps:
class myViewHolder extends RecyclerView.ViewHolder {
RemaltiveLayout itemLayout;
public myViewHolder(View itemView) {
super(itemView);
itemLayout = (RemaltiveLayout) itemView.findViewById(R.id.myItemLayout);
itemLayout.getLayoutParams().height = res.getDimensionPixelSize(R.dimen.height_value);
}
You should be able to use item at a specific location position as well
This is not gonna work with Width though, cause items are always being stretched to fill columns of layoutManager
Steps to get first item height:
Get the layout manager from the recyclerview reference in onScrolled()
Get the first the visible item position using layoutManager.findFirstVisibleItemPosition()
If fist visible item position is 0 then get the 0th child height layoutManager.getChildAt(0)
Sample code:
RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (layoutManager != null) {
int child0Height = 0;
int currentFirstVisibleItem = layoutManager.findFirstVisibleItemPosition();
if (currentFirstVisibleItem == 0) {
final View childAt0 = layoutManager.getChildAt(0);
if (childAt0 != null) {
child0Height = childAt0.getHeight();
}
}
}
}
};

OnDraw() animations only run when the item is moving

I want to add animations to the itemView, however they only run if the item is currently moving. For example the item starts to fadeOut until the alpha is 0, but if the item stops moving, the animation pauses and the item only fades out halfway. After that, the item needs to start moving again for the animation to complete.
I know onDraw typically only gets called when something changes, but I guess the OS doesn't realize the style of the View is changed as well, so it doesn't call it.
I tried adding invalidate() at the end so I can force an update but it doesn't do anything.
How can I fix this?
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ACTION_STATE_SWIPE) {
if (optionsState == OptionState.UPVOTE) {
drawUpvoteOption(c, viewHolder.itemView, dX);
} else if (optionsState == OptionState.DOWNVOTE) {
if (!isRightSwipeMaxed(dX)) drawDownvoteOption(c, viewHolder.itemView, dX);
else drawDownvoteOption(c, viewHolder.itemView, MAX_RIGHT_SWIPE_DX);
}
setTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
if (dX == 0) {
//This only works if the item is moving
viewHolder.itemView.animate().alpha(1f);
optionsState = OptionState.NONE;
}
if (!isRightSwipeMaxed(dX)) {
viewHolder.itemView.setTranslationX(dX);
} else {
viewHolder.itemView.setTranslationX(MAX_RIGHT_SWIPE_DX);
//This only works if the item is moving
viewHolder.itemView.animate().alpha(0);
}
//Doesn't do anything
viewHolder.itemView.invalidate();
}
I had the same issue, invalidate() on the itemView did nothing.
You have to do the invalidate() on the parent recyclerView for that to work :
viewHolder.animatedRecyclerView.invalidate()

How to detect when TextView exits/enters screen on Android?

I have this LinearLayout with few elements in it. Since there are many elements I have wrap it inside ScrollView. Inside this LinearLayout I have one TextView element and I won't to detect when it is not longer visible on screen while scrolling.
The thing that I am trying to do is to show this Activity with LinearLayout and make title of toolbar empty, then when user scrolls down, and this TextView exits screen, I want to set title in the toolbar to content of that TextView. And similar when this text view enters screen again to set title in toolbar to be empty.
I have no idea how to implement this and what listeners to use...
EDIT:
This is what I want to create: detect when text view exits or enters screen and update title in toolbar accordingly.
This will work for API23 or higher, but it gives you the basic idea.
ScrollView scroll = (ScrollView) findViewById(R.id.yourScrollView);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
scroll.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
Log.e(TAG, "SCROLL = " + scrollY);
if(scrollY > txtView_Y){
setTitle("Hello");
}
else{
setTitle("I'm not there yet");
}
}
});
}
The value txtView_Y is the y position of your TextView. You will have to get that after the Activity has positioned all the views. You will probably need ViewTreeObserver.OnGlobalLayoutListener for that.

RecyclerView not calling onScrolled at the bottom

I'm trying to create a RecyclerView with pagination and I have a problem with showing progress bar when I try to scroll down being already at the very end of the list. There's a callback RecyclerView.OnScrollListener which has a method onScrolled for handling scroll events, but it's not working when no actual scrolling has happened.
There's onScrollStateChanged method that works when I try to scroll down from the bottom, but my logic requires me to know direction of the gesture (up/down), so this method is not helpfull in my situation.
I currently trying to do it somewhat like this (which is obviously not working):
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
fetchDataFromServer();
}
});
EDIT: I tried to detect if the end is reached in onScrolled and it kinda worked, but sometimes when I try to fetch the data from the server I don't receive anything, so after such a request I'm still at the end of the list and I want to scroll down to try to update the list again, but I can't since onScrolled is not getting called.
Any ideas on how to fix that? Is there another callback that I can use?
I guess to show progress at the end of the recyclerview, you just need to make sure that end has been reached.
This is how it goes -
private int visibleThreshold = 1; // trigger just one item before the end
private int lastVisibleItem, totalItemCount;
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = mLayoutManager.getItemCount();
lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
if (totalItemCount <= (lastVisibleItem + visibleThreshold) {
// ... end has been reached...
//... do your stuff over here...
}
}
});
}
EDIT:
sometimes when I try to fetch the data from the server I don't receive
anything
Simple! If your network request fails, try to catch an exception. If it is SocketTimeOutException, Retry doing the same request again. If there is an another exception(could be from the back-end or front-end), Get it fixed.
Check if your recycler view is nested inside NestedScrollView or something...
Remove the nested scroll view and it will work.
You can achieve any kind of feed by just using recycler view and use the VIEW_TYPE concept of recycler view
If you developing for min sdk level 23 then you can use setOnScrollChangeListener
recyclerView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
}
});

How to get the Scrollposition in the Recyclerview/Layoutmanager?

How to get the scrollposition in Recyclerview or the Layoutmanager?
I can measure the scrollposition by adding an OnScrollListener, but when the Orientation changes scrollDy is 0.
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int scrollDy = 0;
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
scrollDy += dy;
}
});
For future use, If you are switching between Fragments within the same activity and all you want to do is save scroll-position for recyclerview and then restore recyclerview to the same scroll-position, you can do as follows:
In your onStop()/onDestroyView()/onPause() whatever callback is more appropriate, do this:
Parcelable recylerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
And In your onStart()/onCreateView()/onResume() whatever callback is more appropriate, do this:
recyclerView.getLayoutManager().onRestoreInstanceState(recylerViewState);
This way you can successfully keep your recyclerView's state in a parcelable and restore it whenever you want.
You cannot get it because it does not really exist. LayoutManager only knows about the views on screen, it does not know the views before, what their size is etc.
The number you can count using the scroll listener is not reliable because if data changes, RecyclerView will do a fresh layout calculation and will not try to re-calculate a real offset (you'll receive an onScroll(0, 0) if views moved).
RecyclerView estimates this value for scrollbars, you can use the same methods from View class.
computeHorizontalScrollExtent
computeHorizontalScrollRange
computeHorizontalScrollOffset
These methods have their vertical counterparts as well.
I came to the question just wanting to get the item position index that is currently scrolled to. For others who want to do the same, you can use the following:
LinearLayoutManager myLayoutManager = myRecyclerView.getLayoutManager();
int scrollPosition = myLayoutManager.findFirstVisibleItemPosition();
You can also get these other positions:
findLastVisibleItemPosition()
findFirstCompletelyVisibleItemPosition()
findLastCompletelyVisibleItemPosition()
Thanks to this answer for help with this. It also shows how to save and restore the scroll position.
recyclerView.computeVerticalScrollOffset() does the trick.
Found a work around to getting last scrolled position within the RecyclerView with credits to Suragch solution...
Declare a globally accessible LinearLayoutManager so a to be able to access it within inner methods...
When ever the populateChatAdapter method is called, if its the first call the scroll to position will be the last sent message and if the user had scrolled to view previous messages and method is called again to update new messages then they will retain their last scrolled to position...
LinearLayoutManager linearLayoutManager;
int lastScrollPosition = 0;
RecyclerView messages_recycler;
Initiate them...
messages_recycler = findViewById(R.id.messages_recycler);
linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
messages_recycler.setLayoutManager(linearLayoutManager);
On your populate adapter method
private void populateChatAdapter(ObservableList<ChatMessages> msgs) {
if (lastScrollPosition==0) lastScrollPosition = msgs.size()-1;
ChatMessagesRecycler adapter = new ChatMessagesRecycler(msgs);
messages_recycler.setAdapter(adapter);
messages_recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastScrollPosition = linearLayoutManager.findLastCompletelyVisibleItemPosition();
}
});
messages_recycler.scrollToPosition(lastScrollPosition);
}
You have to read scrollDy in
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
then you can get recyclerView's scroll position
To get ScroolPosition try this:
You will need this:
mLayoutManager = new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.HORIZONTAL, false);
And then get the position of the mLayoutManager (like this):
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
int pos = mLayoutManager.getPosition(v);
RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(pos);
}
});
With this "pos" you get the position, startin with the 0.
int pos = mLayoutManager.getPosition(v);

Categories

Resources