android: RecyclerView inside a ScrollView - android

I have a RecyclerView wrapped in a LinearLayout and it works perfectly as expected. I can see all the data in the RecyclerView as populated. So far so good.
When I wrap the LinearLayout in a ScrollView, the RecyclerView goes blank. I do not see anything inside RecyclerView. Why? How to make this work.
The page is one of the tabs in a ViewPagerIndicator, so everything in that tab needs to be in a ScrollView.
Thanks for all the help.

Set this property for the ScrollView,
android:fillViewport="true"
ScrollView will extend itself to fill the contents

After checking implementation, the reason appears to be the following. If RecyclerView gets put into a ScrollView, then during measure step its height is unspecified (because ScrollView allows any height) and, as a result, gets equal to minimum height (as per implementation) which is apparently zero.
You have couple of options for fixing this:
Set a certain height to RecyclerView
Set ScrollView.fillViewport to true
Or keep RecyclerView outside of ScrollView. I my opinion, this is the best option by far. If RecyclerView height is not limited - which is the case when it's put into ScrollView - then all Adapter's views have enough place vertically and get created all at once. There is no view recycling anymore which kinda breaks the purpose of RecyclerView.

Nothing helped me except this:
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
rv.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
I got this answer there. Thank you Piyush Gupta for that.

Hope this helps :
Add this line to your recyclerView xml :
android:nestedScrollingEnabled="false"
Try it ,recyclerview will be smoothly scrolled with flexible height inside scrollview .

Related

notifyItemChanged() make the RecyclerView scroll and jump to UP

i have a RecycleView with an adapter that show a list of servers
and the user must select one server.
when i call notifyItemChanged(previousPosition) inside the onClick() method
to make the old server unselected and the new server selected,
that's make the RecycleView list jump to up exactly in the middle of list.
and this problem happen just when i click on one of the last 2 or 3 servers inside the RecycleView list
here is the code of my RecyclerView.Adapter :
public class ServerAdapter extends RecyclerView.Adapter<ServerAdapter.ServerViewHolder> {
private List<Server> listServers = new ArrayList<>();
private int[] icons = new int[]{R.drawable.server1,R.drawable.server2,R.drawable.server3,R.drawable.server4,R.drawable.server5,R.drawable.server6,R.drawable.offline};
private int selected = 0;
private int previousSelected = 0;
public ServerAdapter(List<Server> listServers){
this.listServers = listServers;
}
#Override
public ServerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.server_relative_layout,parent,false);
return new ServerViewHolder(view);
}
#Override
public void onBindViewHolder(final ServerViewHolder holder, final int position) {
if(position == selected){
holder.getBackground().setSelected(true);
}else{
holder.getBackground().setSelected(false);
}
holder.getBackground().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(position != selected){
previousSelected = selected;
selected = position;
holder.getBackground().setSelected(true);
notifyItemChanged(previousSelected);
}
}
});
holder.getImageServer().setImageResource(icons[position%6]);
holder.getTextNameServer().setText(listServers.get(position).getName());
holder.getTextConnected().setText(listServers.get(position).getUrl());
}
#Override
public int getItemCount() {
return listServers.size();
}
public class ServerViewHolder extends RecyclerView.ViewHolder{
private ImageView imageServer;
private TextView textNameServer;
private TextView textConnected;
private View background;
public ServerViewHolder(View itemView) {
super(itemView);
imageServer = (ImageView)itemView.findViewById(R.id.imageServer);
textNameServer = (TextView)itemView.findViewById(R.id.textNameServer);
textConnected = (TextView)itemView.findViewById(R.id.textConnected);
background = itemView;
}
public ImageView getImageServer() {
return imageServer;
}
public TextView getTextConnected() {
return textConnected;
}
public TextView getTextNameServer() {
return textNameServer;
}
public View getBackground() {
return background;
}
}
}
any solutions to solve this problem ? thanks.
The problem happened exactly when i specify the layout height and do not let it to wrap_content
<android.support.v7.widget.RecyclerView
android:layout_width="wrap_content"
android:layout_height="400dp"
android:id="#+id/serverRecyclerView"
android:layout_margin="10dp"
/>
or when i put it below something for expample like that :
<android.support.v7.widget.RecyclerView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/serverRecyclerView"
android:layout_margin="10dp"
android:layout_below="#+id/image"/>
my code exactly is :
<android.support.v7.widget.RecyclerView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/serverRecyclerView"
android:layout_margin="10dp"
android:layout_alignTop="#+id/imageBall"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_toRightOf="#+id/camera"
android:layout_toEndOf="#+id/camera"/>
Looks like this is a bug: https://code.google.com/p/android/issues/detail?id=203574
The best workaround seems to be Bart's answer to set the RecyclerView's LinearLayoutManager's AutoMeasure property to false.
LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setAutoMeasureEnabled(false);
recyclerView.setLayoutManager(llm);
The set FixedSize to true solution had way too many side-effects...
RecyclerView.setHasFixedSize(true)
I don't know why, but I used:
RecyclerView.setHasFixedSize(true)
This worked for me. I hope it can help.
android:descendantFocusability="blocksDescendants"
android:descendantFocusability="blocksDescendants"
this attr solve my bug
RecyclerView.ItemAnimator animator = myRecyclerListView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator)animator).setSupportsChangeAnimations(false);
}
My RecyclerView was inside ConstraintLayout, and I also had such problem and calling setAutoMeasureEnabled(false) of RecyclerView's LayoutManager did not fix the issue for me, furthermore this method is deprecated in 28.0.0 version. What I did is that, I wrapped my RecyclerView with RelativeLayout and now it works like a charm. As mentioned in bugtracker, this "issue" is intented behaviour in LinearLayout and is not going to be fixed. So if it is possible, just wrap your RecyclerView something like this:
<RelativeLayout
android:id="#+id/container_messages_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#drawable/chat_back_pattern"
app:layout_constraintBottom_toTopOf="#+id/bottom_view"
app:layout_constraintTop_toBottomOf="#+id/toolbar">
<android.support.v7.widget.RecyclerView
android:id="#+id/messages_list"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
for anyone who stumbles upon this issue, try using
yourRecyclerView.notifyItemChanged(int position, Object payload);
This one did the trick for me.
Using
setAutoMeasureEnabled(false);
also worked but in some edge cases recycler view was acting weird. Good luck!
RecyclerView can perform several optimizations if it can know in advance that RecyclerView's size is not affected by the adapter contents. RecyclerView can still change its size based on other factors (e.g. its parent's size) but this size calculation cannot depend on the size of its children or contents of its adapter (except the number of items in the adapter).
If your use of RecyclerView falls into this category, set this to true. It will allow RecyclerView to avoid invalidating the whole layout when its adapter contents change.
If we have a RecyclerView with match_parent as height/width, we should add setHasFixedSize(true) since the size of the RecyclerView itself does not change inserting or deleting items into it.
setHasFixedSize should be false if we have a RecyclerView with wrap_content as height/width because each element inserted by the adapter could change the size of the RecyclerView depending on the items inserted/deleted, so, the size of the RecyclerView will be different each time we add/delete items.
recyclerView.setHasFixedSize(true);
true if adapter changes cannot affect the size of the RecyclerView.
References
Android Developers Reference - RecyclerView
Understanding RecyclerView setHasFixedSize - Gastón Saillén
I came across the similar problem, just take care of the xml layout file.
Do not use the layout_below , layout_above or others similar properties in RecyclerView or RecyclerView's parent view.
You can use LinearLayout weight , layout_marginBottom or sth to achieve
layout_below or other.
The late answer better than nothing, if you're using NestedScrollView as the parent view of RecyclerView you should delete it.
I had a similar problem and I tryed all solutions listed above, but noone worked.
I was already padding the "Payloads" to "notifyItemChanged(position, payloads)" because I just needed to "upload" a checkbox value so I was passing the value inside "Payloads" without recalling the update of the entire viewholder.
This solution worked for all view holders in my recycler view except for the last one (and probably for all "recycled" ones, I mean those who recall the "onBindViewHolder" by "recycling" an existing view).
I think using "notifyItemChanged" will works if you have only the recyclerview and I also think that this problem of "auto-scrolling" is raised by nested scroll views & recycler views.
I was in the case exposed by "raed", so "ScroolView -> RecyclerView -> "n" x RecyclerView". I have a scroolview wich contains a recyclerview whose viewholders can contains a recycler views.
Delete the parent ScrollView is a really weird solution and I couldn't use it, so I setted the "onStopNestedScroll" inside the "ScrollView" and not inside the RecyclerView.
Personally I used it programmatically before the code part which calls the "notifyItemChanged" method by doing:
msvContainer.onStopNestedScroll(mRecyclerView);
Where "msvContainer" is my ScrollView which contains the RecyclerView, and "mRecyclerView" is my RecyclerView contained by the ScrollView.
This way worked 99% because the first time I call "notifyItemChanged" the view scroll up only for the ScrollView, so it hides a button inside my ScrollView which is below my RecyclerView but it doesn't scroll the RecyclerView items. After the first call "notifyItemChanged" works properly.
I found that calling:
msvContainer.stopNestedScroll();
works too. But i suggest to use the first method with the target view if you have multiple nested scroll views.
Anyway you should call "startNestedScroll" after you ran out of the critical part of re-updating your view holder because the targeted view, so in my case the RecyclerView, won't scroll until you call this method so it won't recycler his view holders too.
(In my case that I have multiple Recycler View inside a parent Recycler View inside a parent Scroll View if I was in need to call "notifyItemChanged" inside the most inner Recycler View i would use the "stopNestedScroll" method for every parent view and then re-activated the scroll after the scroll-critical part)
Hope this is helpful, have a nice coding!
Bye
In my case, all I did was to set the height of the recyclerview to "match_parent". Then in your MainActivity, do;
recyclerView.smoothScrollToPosition(yourAdapter.getItemCount()-1);
Thats all...

Create a scrollable LinearLayout without a ScrollView

I spent a good part of a day looking into scrolling content in Android. However, in my app I need to apply a custom type of scrolling that contains among other things a listview. A listview has its own scrolling and a scrollview should never be used with a list view together.
But there must be some intrinsic scroll functionality for views. The documentation on View does indicate support but the methods provided do not seem to provide any listeners to detect scrolling.
A scrollview also is built on a FrameLayout which would also be problematic in my app.
You can make a LinearLayout scrollable with:
android:isScrollContainer="true"
but this doesn't seem to do anything.
In fact you can even set the scrollbars for a LinearLayout:
android:scrollbars="vertical"
but that also doesn't seem to do anything.
I don't want to control scrolling but rather have a listener that detects scrolling on the screen regardless whether a listview, framelayout or any other control is visible. I guess what I am looking for is writing my own custom ScrollView control but without the limitations of its FrameLayout and all the unnecessary overhead that I don't need. Any suggestions?
On way to resolve above problem is to have 2 ScrollView, one is the outer ScrollView that contain all the view and other inner Scrollview instead of listview and adding the items inside LinearLayout dynamically(Inside inner ScrollView)
Actually after doing some research, I come up with a solution for this problem:
At first I want to explain the problem in a very simple way.
1.LinearLayout will be scrollable.
2.To do this we can use ScrollView but sometimes we need to use ListView inside LinearLayout.
3.We know that inside ScrollView we cannot use another scrollview like ListView
How to solve that?
ListView is scrollable inherently so we can add header and footer in the ListView. As a conclusion:
1.Create Layout header.xml and footer.xml and list.xml
2.Find the ListView reference from list.xml in the main activity and dynamically add header and footer in the ListView reference.
Here is what seems like a viable solution which I tested out but not thoroughly. Create a custom control that extends a LinearLayout and then override the dispatchTouchEvent:
#Override
public boolean dispatchTouchEvent(MotionEvent event)
{
MotionEvent vtev = MotionEvent.obtain(event);
final int actionMasked = event.getActionMasked();
float x = event.getRawX();
float y = event.getRawY();
switch (actionMasked)
{
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int z = 0;
z++;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_POINTER_DOWN:
{
final int index = event.getActionIndex();
break;
}
case MotionEvent.ACTION_POINTER_UP:
break;
}
boolean ret = super.dispatchTouchEvent(event);
return ret;
}
}
You can then place child controls inside this custom LinearLayout and detect motion events, even when ListViews and ScrollViews are present. This doesn't mess with their own scrolling.

ListView inside ScrollView for big List Item Android

I am using ListView inside ScrollView. But as we all know, It creates scrolling problems.
As per this link : Android list view inside a scroll view
I used that code. No doubt it is working fine. But now, problem occurs when any List Item has big content which will take 2-3 lines.Check out image below for wide view : It has actually 4 options but because of scroll problem, it is showing 2 options.
I have also tried below extra code for height but It also dint work. What can be the solution for this ? I don't want to use header and footer as it is not suitable for our application. I want to prevent this Scroll problem and I want to display options perfectly.
listView.getPaddingTop() + listView.getPaddingBottom(); -> gives 0
out this code for your child listview . It will solve the scrolling issue.
mListView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mScrollView.requestDisallowInterceptTouchEvent(true);
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_UP:
mScrollView.requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
});

android: listview inside a scrollView doesn't work?

I am having a listview inside a scrollview, but the problem is that the scrollview is scrolling but listview is not scrolling. I think this is due to that scrollView.
Can somebody who has a working solution post it here as reference?
Generally, you cannot put scrollable things inside other scrollable things, where they scroll in the same direction, and have the results be reliable. Occasionally this works (e.g., WebViews in a ViewPager), but that is the exception, not the norm.
Either:
Move the ListView out of the ScrollView, or
Move all the rest of the contents of the ScrollView into the ListView, whether using things like addHeaderView() or my MergeAdapter
If you put your ListView/any scrollable View inside the scrollView it will not work properly because when you touch the screen ,main focus of your touch is on parent view(scrollView ) not the child View (ListView).
ListView must have fixed height as below in your XML file
<ListView android:id="#+id/lv"
android:listSelector="#0f0"
android:layout_width="fill_parent"
android:layout_height="500px" />
In Java file, write below code after setContentView()
lv = (ListView)findViewById(R.id.lv);
lv.setAdapter(your adapter here); // you have to add your adapter here
lv.setOnTouchListener(new ListView.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE)
{
lv.scrollBy(0, 1);
}
return false;
}
});
Make these changes to your code and test it. After too many experiments i written this code. It is working 100% fine.

Scrolling issues with a ListView inside a ScrollView

Here's the scenario conceptually (excluding linearlayouts)
ScrollView
Button
Checkboxes
Spinner
ListView (full-size, non-scrolling)
AdMob advert
i.e. a scrolling pane, which has a filtering UI at the top, followed by results, but the advert must always remain visible, and when scrolling down the filter UI must scroll away, leaving maximum space for results.
I'm aware there are issues with a ListView inside a ScrollView, though for me it is working well in many ways (I'm fixing the length of the ListView to stop it collapsing). So the screen scrolls nicely, the ad stays put at the bottom, and it looks good.
But the problem I'm seeing is, inexplicably, when the activity opens, the ScrollView is scrolled down a bit, so the ListView is at the top of the screen. I assume this is a default behaviour, so I set about trying to force the scroll position of the ScrollView to the top, but I've tried various methods, and see no effect:
scrollview.scrollTo(0, 1000/-1000);
scrollview.smoothScrollBy(0, 1000/-1000);
scrollview.fullScroll(View.FOCUS_UP);
Is there any way to force the ScrollView to start with the scroll position at the top?
If not, how can I have an ad that doesn't scroll off the bottom, but a filter UI that always scrolls off the top? Using ListView seems overkill as I don't need scrolling but it does provide many benefits so would be nice to avoid starting from scratch and rendering everything myself.
Use the following method and enjoy!
private void setListViewScrollable(final ListView list) {
list.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
listViewTouchAction = event.getAction();
if (listViewTouchAction == MotionEvent.ACTION_MOVE)
{
list.scrollBy(0, 1);
}
return false;
}
});
list.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view,
int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE)
{
list.scrollBy(0, -1);
}
}
});
}
listViewTouchAction is a global integer value.
If you can replace the line
list.scrollBy(0, 1);
with something else please share it with us.
Why are you using a listview if you're not scrolling? Why can't you just use a linearlayout or something more fit to this situation? You mention a filter, you could very easily roll your own filter especially since apparently you just have a few items in your listview.
Use something other that a ListView, you can dinamically generate linearLayouts to show the data you want. You should never use a listview inside a scrollView, it doesnt work for a simple reason, when you scroll, what should scroll, the listview or the scroll view. A couple of people from google have stated not to do this.
I've experienced the issue of a ScrollView starting off scrolled down slightly, the solution was to post a runnable which called the animateTo(0,0) method to get the list to scroll to the top. I found this only worked using anitmatTo(0,0) scrollTo(0,0) didn't seem to work.
Something along the lines of:
mListView.post(new Runnable(){ public void run() { mListView.animateScrollTo(0,0) });
Now as everyone has already stated you shouldn't do the whole ListView inside a ScrollView, but this may be a fix for the problem you had.
I have:
ScrollView
TextView
Button
TextView
ListView
and this work good for me:
scrollView.smoothScrollBy(0, 0);
without this view start from position of listview, after that it start from top
The solution for this issue is to make a request focus to an object in the top of the ScrollView. For example you can use a table layout wrapping the button and request focus to the table layout (If you focus the button it will change its color).
// onCreate method
TableLayout tablelayout = (TableLayout) findViewById(R.id.tablelayout);
tablelayout.setFocusableInTouchMode(true);
tablelayout.requestFocus();

Categories

Resources