I have a RecyclerView (and some other views) in a ScrollView. Currently the RecyclerView is laid out as very small (it shows 2 items out of 5 that it contains) and it scrolls independently of the ScrollView, which is obviously not great UX. I would like to get the RecyclerView to not scroll and to extend so that all its items are visible.
(I know it's stupid to use a RecyclerView in this case. I'm only doing this because somewhere else in the app I need a normal RecyclerView with scrolling etc. but the same kind of content, and I don't want to duplicate code).
It’s pretty simple, simply set the RecyclerView’s height to wrap_content.
You might also benefit from disabling nested scrolling on the recycler view, like so:
RecyclerView recycler = (RecyclerView) findViewById(R.id.recycler);
recycler.setNestedScrollingEnabled(false);
The solution of setNestedScrollingEnabled(false) isn't as full as it should: you need to use NestedScrollView instead of ScrollViewfocusableInTouchMode="true" to the child of the NestedScrollView .
If you insist on using ScrollView, you should also set minHeight to the RecyclerView, and also set overScrollMode="never" . In this case, it still isn't a good solution because the minHeight might not be enough in some cases
Other alternative solutions that you should consider:
Replace the ScrollView&RecyclerView with a single RecyclerView, which has views with additional view type for what you had in the ScrollView
Use GridLayout or another layout instead.
Maybe it is not completely clear at first sight what to do with all these answers.
I just tried them and the working one is:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/person_properties"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
...
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
No need to change anything programmatically.
In your activity.xml file
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ActivityName">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/RecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false">
</androidx.recyclerview.widget.RecyclerView>
</androidx.core.widget.NestedScrollView>
In RecyclerView use android:nestedSrollingEnabled="false" and use NestedScrollView as a parent Scroll View.
If you are using RecyclerView inside ScrollView then Replace ScrollView with NestedScrollView and enable the nested scrolling
android:nestedScrollingEnabled="false"
This Solved my problem
An easy way is to use in your Custom Adapter, inside the onBindViewHolder method this line: holder.setIsRecyclable(false);
I realised that I use BottomNavigationView which blocked my recycler view from displaying the last item. I fixed it by adding paddingBottom to it:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recipient_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="70dp"
app:layout_constraintTop_toBottomOf="#id/view"
/>
Also try to play with:
android:overScrollMode
You should replace your scrollView for androidx.core.widget.NestedScrollView with matchparent, its simple and work fine.
Following is the code for disabling scroll in the recycler-view, and showing all the items in your layout. It might work:
public class NoScrollRecycler extends RecyclerView {
public NoScrollRecycler(Context context){
super(context);
}
public NoScrollRecycler(Context context, AttributeSet attrs){
super(context, attrs);
}
public NoScrollRecycler(Context context, AttributeSet attrs, int style){
super(context, attrs, style);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev){
//Ignore scroll events.
if(ev.getAction() == MotionEvent.ACTION_MOVE)
return true;
//Dispatch event for non-scroll actions, namely clicks!
return super.dispatchTouchEvent(ev);
}
}
use like this way:
<com.example.custom.NoScrollRecycler
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/color_white"/>
probably parent of recyclerView is a constraintLayout changed it to RelativeLayout
I also had a recycler view inside a scrollview.
Using NestedScrollView, "height=wrap content" on recycler view, and "nestedScrollingEnabled=false" on recycler view worked.
However I had to go a step further since my recycler view data and height changed after layout:
recylerview.measure(View.MeasureSpec.makeMeasureSpec(recylerview.width,View.MeasureSpec.EXACTLY), View.MeasureSpec.UNSPECIFIED)
val height = recylerview.measuredHeight
recylerview.layoutParams.height = height
Related
How to trigger a recyclerview scroll listener when it is inside a scroll view?
Here the scroll view listener is alone triggering
1. Set nested scrolling enabled false of recycler view.
recyclerView.setNestedScrollingEnabled(false);
2. Add scroll listner to nested scrollview.
mScrollView.getViewTreeObserver().addOnScrollChangedListener(new
ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged()
{
View view = (View)mScrollView.getChildAt(mScrollView.getChildCount() - 1);
int diff = (view.getBottom() - (mScrollView.getHeight() + mScrollView
.getScrollY()));
if (diff == 0) {
// your pagination code
}
}
});
Try with NestedScrollView instead of ScrollView.
NestedScrollView is just like ScrollView, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.
Note: You have to set setNestedScrollingEnable(false) to your recylerview.
in xml add android:nestedScrollingEnabled="false" in recyclerview
OR
programmatically yourRecylerview.setNestedScrollingEnabled(false);
See this article.
You can try this one, but still I assuming you may have the same code like mine as you didnot post your needed code into your question.
Try this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.widget.NestedScrollView
android:id="#+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
Use NestedScrollView with <android.support.v7.widget.RecyclerView as I have mention above.
Its ScrollListener will not work as all the rows gets layed when recyclerview is inside scrollview whole purpose of recyclerview gets destroyed when you put it inside scroll view.
For Example as the recyclerview will stretch to full fill its parent ,when you will put it inside scroll view , in case of scroll view it will stretch to its fullest. if there are 1000 items in recyclerview all it will draw 1000 different rows which is also a performance issue.
You should remove it from a scroll view to make its scroll listener working
I am using a RecyclerView to show a list of videos.
Each item in the list holds Video and SeekBar (and more stuff actually but not relevant here) in a RelativeLayout, as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/performance"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.my.company.VideoView
android:id="#+id/the_video"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:keepScreenOn="true"
android:scaleType="fitCenter" />
<SeekBar
android:id="#+id/the_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:alpha="1.0"
android:maxHeight="#dimen/seekbar_height"
android:minHeight="#dimen/seekbar_height"
android:padding="0dp"
android:progressDrawable="#drawable/my_progressbar"
android:thumb="#drawable/my_progressbar_circle"
android:translationY="-5dp" />
</RelativeLayout>
As you can see I added a android:translationY property that brings the SeekBar up a little so it would be partially positioned on top of the previous cell, i.e. the previous Video.
However it remains partially hidden. I can only see the part that is in the RelativeLayout in which is it declared.
I tried calling bringToFront() on the seekbar and on the RelativeLayout (performance) itself - but that did not help.
Probably the question is not relevant to RecyclerView only. Being somewhat new in android dev I am not sure if I can place a view that is declared inside a RelativeLayout to show up outside of its borders.
Hope I was clear, need your help. Tx.
By default, every view is clipped to its parent size.
You could try to disable this clipping, by adding this in your RelativeLayout XML attributes:
android:clipChildren="false"
android:clipToPadding="false"
or in code
viewGroup.setClipChildren(false);
viewGroup.setClipToPadding(false);
In your case, it seems that either RecyclerView or LinearLayoutManager consider that previous items should be displayed over following ones. One way could be to use RecycleView decoration to overlap :
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
private final static int vertOverlap = -10;// TODO : do not forget to convert dp in pixels
#Override
public void getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(0, vertOverlap, 0, 0);
}
});
So, you would not need to use translationY on your SeekBar, but rather to add some paddingTop to your VideoView :
android:paddingTop="5dp"
That way, I think you could hide the SeekBar if needed, and cell overlapping would not be visible.
Follow this answer with same case only the difference is it is overlapping next item of Recycleview https://stackoverflow.com/a/40603773/3839498
I'm getting inconsistent behavior with SwipeRefreshLayout.setVisibility(View.GONE). Sometimes, my swipe refresh layouts hide the child view, but some times they don't. Since SwipeRefreshLayout subclasses ViewGroup, I'm expecting it to always hide the child view whenever it's visibility is .GONE, but that isn't happening.
Any insights are appreciated.
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mySwipeRefreshLayout">
<View
android:background="#FF0000"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
Update: After spending the afternoon stripping everything out of the fragment, I confirmed that it's our setup that is causing the problem. Even so, it's still strange that setting the child view to VIEW.Gone works, but doing so to the swipe refresh layout doesn't hide the child.
I feel dumb, but my problem ended up being this: SwipeRefreshLayout doesn't hide while it is animating the refresh logo. You have to setRefreshing(false) in addition to setVisibility(View.GONE). Even after setRefreshing(false), there is an exit animation that happens after you use both of these methods.
Here's the solution I used to fix this. It not only handles calling setRefreshing(false) when you want to hide it, but it also sets the alpha to zero so it hides immediately without having to wait for the refresh animation to wind down.
public class HideableSwipeRefreshLayout extends SwipeRefreshLayout {
public HideableSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setVisibility(int visibility) {
if ((visibility == View.INVISIBLE) || (visibility == View.GONE)) {
setAlpha(0);
setRefreshing(false);
} else {
setAlpha(1);
}
super.setVisibility(visibility);
}
}
If you can confirm that this is not caused by your setup, you should report this as a bug, see https://source.android.com/source/report-bugs.html.
As a workaround I recommend to wrap your layout into a FrameLayout and set the visibility on it instead of the SwipeRefreshLayout.
In your xml file provide id to SwipeRefreshLayout and view
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mySwipeRefreshLayout">
<View
android:background="#FF0000"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
then use id of both in .java file and use their objects.
if(swipeRefreshLayout.isRefreshing()){
view.setVisibility(View.VISIBLE);
}else{
view.setVisibility(View.INVISIBLE);
}
I have a vertical recyclerview (with a GridLayoutManager) inside another recyclerview (with LinearLayoutManager). The problem I am facing right now is that, the inner recyclerview (with GridLayoutManager) binds all of it's items at the same time, even the views that are not on the screen at the moment (onBindViewHolder() gets called for all of its items).
To give you more information, in my layout file, I put height of my recycler view as wrap_content.
I think the problem is, since there are 2 nested vertically recyclerviews, when the parent RV wants to measure its children and the children is another RV, in onMeasure() it computes the size needed for the entire RV, not just the portion that it wants to bind on the screen.
Any idea how to solve this?
Here is the layout file for my outer recyclerview:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/component_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
And here is the code for my inner recyclerview:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/gutter"
android:paddingBottom="#dimen/gutter">
<TextView
android:id="#+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/gutter"
android:textSize="30sp"
android:textColor="#android:color/white"
android:fontFamily="sans-serif-thin"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/my_slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
P.S.: I'm using this adapter delegate for my outer recyclerview:
https://github.com/sockeqwe/AdapterDelegates
I think nested recyclerviews are a very bad idea. When i try to scroll, which recyclerview has to respond the the scolling, the parrent or child.
That is why I think you are looking for the ExpandableListView? That's limited to only two levels of listings, but that sounds like it would work for your needs). It also solves the soling issue.
It would look something like this:
EDIT: even nested ExpandableListViews are possible:
EDIT: check this lib for horizontal scroling
This is a known bug.
You should not put a RecyclerView inside another RecyclerView because RecyclerView gives its children infinite space. Hence the inner RecyclerView keeps measuring till the dataset is exhausted. Try setting setAutoMeasureEnabled(false) to false on layout manager or you can solve this problem by using a wrapper adapter instead of inner recycler view.
The first thing you need to know is that, when you nest scrolling layouts, the inner ones will get infinity allowed height, effectively making them wrap_content. There is in fact a relatively easy way to fix this problem.
Say I had two nested RecyclerViews such as these, in this case vertically oriented.
<RecyclerView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical>
<View .../>
<!-- other stuff -->
<RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
</RecyclerView>
The inner recyclerView here will bind all of it's children immediately every time because, from it's position, your screen will have infinite height.
The solution is to set the height of your inner recyclerview to some static value, not wrap_content or match parent, as either of those will simply fill up the outer recyclerview with one view that will all be bound at once due to it's large height. If you make the height of the inner recyclerview the same as the display's height, you should see your problem go away.
Here is an implementation that will not bind all children at once:
<RecyclerView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical>
<View .../>
<!-- other stuff -->
<RecyclerView
android:layout_width="match_parent"
android:layout_height="#dimen/screen_height"
android:orientation="vertical"/>
</RecyclerView>
Note the layout_height of the inner RecyclerView is now a fixed value pulled from the dimensions file. You yourself will have to come up with a reasonable value to put there.
Side Note: In order to make all of this work and for scrolling to work properly, you may have to play around with the parameter: NestedScrollingEnabled in your RecyclerViews - there are several known bugs relating to this that you may need to work around.
i.e.: innerRecyclerView.setHasFixedSize(true); and innerRecyclerView.setNestedScrollingEnabled(false).
so what happens here when you place a scrollview(no fixed size because of wrap content) inside another scrollview(again no fixed size because of wrap content),both nested scroll view fails to render.
So there is two solutions--
1- Either you will have to think of alternative solution for nested scrollviews
2- You can give outside recyclerview cell fixed height so that inside recycler view can get some fixed layout to render itself.
I could solve my issue by using only one Recyclerview, where it has a grid layout, and based on the component items i'm adding into it, i change the spancount for that. Basically instead of adding the inner recyclerview, i add the items that were supposed to go to the inner recyclerview, to the outer recyclerview.
I am using RecyclerView inside NestedScrollView and it works. But when I use RecyclerView inside LinearLayout or something, it scroll in various speed depending on gesture. The scroll listen to gesture and if I slide up only a bit, then it scroll a little bit while if I slide up really fast, then it scroll really fast. Now my problem is that RecyclerView inside NestedScrollView certainly scroll but fast scroll does not work. However I slide up fast or slow, RecyclerView or NestedScrollView only scroll a little bit.
How can I make my NestedScrollView or RecyclerView inside that scroll view scroll in various speed?
try
recyclerView.setNestedScrollingEnabled(false);
By default setNestedScrollingEnabled works only after API-21.
You can use ViewCompat.setNestedScrollingEnabled(recyclerView, false); to disable nested scrolling for before and after API-21(Lollipop). Link to documentation.
I was working on android 16 where this was not possible to use setNestedSCrollEnabled method,
What I end up doing to stop RecyclerView from handling Scrolls.
Like in LinerLayoutManager i made canScrollHorizontally, canScrollVertically to return false by default.
myRecyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false){
#Override
public boolean canScrollHorizontally() {
return false;
}
#Override
public boolean canScrollVertically() {
return false;
}
});
After several iterations, I came up with a solution.
If you are using RecyclerView, then:
recyclerView.setNestedScrollingEnabled(false);
If you are using LinearLayout inside NestedScrollingView, take the LinearLayout inside a normal ScrollView and then set its scrolling to
scrollView.setNestedScrollingEnabled(false);
android:overScrollMode="never
<android.support.v4.widget.NestedScrollView
android:id="#+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
You can use ScrollView with ExtendRecyclerView class that overrides the onMeasure method. That works for me!
#Override
protected void onMeasure(int widthSpec, int heightSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthSpec, expandSpec);
}
recyclerView.setNestedScrollingEnabled(false);
Will be useful sometimes.But it is not advisable for all the times.because it disables view recycling feature in recylcer view.
Alternatives:
Try CollapsiveToolbarLayout with Recycler view.
put other views in collapsiveTollbar layout.
I also met this problem.
And upgrade to 26.1.0 fix it.
In My Case i placed all images in drawable folder insted of drawable-xxxhdpi folder thats why my screen UI is lagging.
This is WAI. The NestedScrollView measures its children with the Spec "Unspecified". The child can grow as much as it wants too.
This essentially equates the height of NSV and RV. So as far as the RV is concerned, it believes that it is completely displayed.
Wrap your RV with an LL and give your RV a height. The LL would not set the measure spec to be UNSPECIFIED so the RV would correctly scroll within its set height of whatever DPs you provide.
The only downside of this method is that you will not be able to do a match parent on your RV.
You should wrap recycler view in any layout like LinearLayout and set RecyclerView size to constant, like 800dp. This will enable smooth scroll and recycler view will still recycler views during scroll.
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="800dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</LinearLayout>