Android RecyclerView scroll to programmatically not working - android

I am trying to programmatically scroll to a position in my RecyclerView, this is what I've tried until now nothing worked in my case:
musicList.getLayoutManager().smoothScrollToPosition(musicList, null, position);
musicList.scrollToPosition(position);
mLayoutManager.scrollToPosition(position);
musicList.getLayoutManager().scrollToPosition(position);
Is there something I missed?
I have a LinearLayoutManager mLayoutManager; defined and set it to RecyclerView as this:
mLayoutManager = new LinearLayoutManager(this);
musicList.setLayoutManager(mLayoutManager);
RecyclerView XML:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/musicList"
android:scrollbars="vertical"
android:scrollbarThumbVertical="#drawable/custom_scrollbar_vertical"/>

Did you try with LinearLayoutManager method scrollToPositionWithOffset
//position 2 and second param is offset 20
mLayoutManager.scrollToPositionWithOffset(2, 20);
this works for me. Hope to work and for you !

Addendum:
The proposed scroll functions do not work correctly in a scenario with a collapsing toolbar while the toolbar is expanded.
It seems that the recycler view has a fixed height, and while the toolbar is expanded, the recycler is moved down, partially out of the screen. As a consequence also the results of the functions findLast[Completely]VisibleItemPosition() are too high. Example: The last visible item is told to be #9, but in fact item #7 is the last one that is really on screen.
I finally chose a workaround like this:
if (newPos > 2 /* or other number > 0 */)
{
AppBarLayout l = findViewById(R.id.appbar);
if (l != null)
{
l.setExpanded(false, true); // collapse the bar, with animation
}
}
mRecyclerView.scrollToPosition(newPos);
Any hints for a better solution are appreciated!

Related

scroll recyclerview item to top inside of scroll view

I'm trying to scroll recycler view item to top of screen when user clicks on an item.
My layout is set as following
- <FrameLayout>
-- <ScrollView>
--- <LinearLayout /> // static content
--- <RecyclerView /> // dynamic content
--- <LinearLayout /> // static content
-- </ScrollView>
-- </FrameLayout>
What I want to achieve is when i click on an item of recyclerview. It should scroll up to top of the screen.
I have tried following so far but no luck.
->
// use recycler view's layout manager to scroll.
recyclerView.layoutManager.scrollToPositionWithOffset(position)
->
// use smooth scroll to scroll
recyclerView.smoothScrollToPosition(ItemPos)
->
// use main scroll view to scroll on the screen. This kind of works but does not move item to top of the page.
val y = recyclerView.y + recyclerView.getChildAt(position).y
activity.binding.mainScrollview?.smoothScrollTo(0, y.toInt())
Any help is appreciated. Thank you
Looks like in your case you don't have enough items.
You can't get recyclerView scroll lower than the last Item So it's either you make your own implementation of recyclerView by extending it,get add scroll amounts and when got to the last item you'll add up the translationY of the recyclerView.
Or you can add a few dummy Items.So they can be View with width and height of your normal Items.That should do it.
Have you tried layoutManager's scrollToPositionWithOffset function:
thirdItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView
.getLayoutManager();
layoutManager.scrollToPositionWithOffset(0, 0);
}
});

scroll only next card view in Recyclerview even howmuch scroll amount applied

Scroll in Recyclerview list of card view default when scroll faster than scroll more than one card.I need only shows next of current card when scrolling even user how much scroll amount is applied in android.I Googled but not getting a solution. thanks for helping
my code contained MyRecyclerView and LinearLayoutManager looks following lines
MyRecyclerView = (RecyclerView) view.findViewById(R.id.cardView);
MyRecyclerView.setHasFixedSize(true);
LinearLayoutManager MyLayoutManager = new LinearLayoutManager(getActivity());
MyLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
if (listitems.size() > 0 & MyRecyclerView != null) {
MyRecyclerView.setAdapter(new MyAdapter(listitems));
}
MyRecyclerView.setLayoutManager(MyLayoutManager);
1. You should use smoothScrollToPosition(int position)
Starts a smooth scroll to an adapter position.
recyclerview.smoothScrollToPosition(position);
2. Use scrollToPosition()
void scrollToPosition (int position)
Convenience method to scroll to a certain position. RecyclerView does not implement scrolling logic, rather forwards the call to
RecyclerView.scrollToPosition(postion)
3. rv.getLayoutManager().scrollToPosition(youPositionInTheAdapter).
4. scrollToPositionWithOffset()
void scrollToPositionWithOffset (int position,int offset)
Scroll to the specified adapter position with the given offset from resolved layout start. Resolved layout start depends on getReverseLayout(), getLayoutDirection(android.view.View) and getStackFromEnd().
linearLayoutManager.scrollToPositionWithOffset(1, 10);
You can achieve this by using object of linearLayoutManager.Use this line to move the selected item to show on top of recycler screen.
linearLayoutManager.scrollToPositionWithOffset(YOUR_ITEM_POSITION,OFFSET_YOU_WANT);
For more information on how to use scrollToPositionWithOffset check this link .
https://developer.android.com/reference/android/support/v7/widget/LinearLayoutManager.html#scrollToPositionWithOffset(int,%20int)

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...

Scroll multiple horizontal RecyclerView together

I'm creating an EPG like view for which I have multiple horizontal RecyclerViews (as tv programs) encapsulated inside a LinearLayout. When I scroll one of the RecyclerView, I want the rest of the views to be scrolled together.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
layoutContent.setWeightSum(epg.getChannels().size());
//prepare recycler views and add into layoutContent based on epg channels
for(EPG.Channel ch : epg.getChannels()){
AppLog.error(TAG, "Creating RecyclerView for: " + ch.getDisplayName());
//create new recycler view
final RecyclerView rv = new RecyclerView(layoutContent.getContext());
lstRecyclerViews.add(rv);
//set layout manager
rv.setLayoutManager(new LinearLayoutManager(layoutContent.getContext(), LinearLayoutManager.HORIZONTAL, false));
//create adapter
rv.setAdapter(new MyAdapter(ch.getPrograms()));
rv.setItemAnimator(new DefaultItemAnimator());
//add into parent layout
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
lp.weight = 1;
layoutContent.addView(rv, lp);
}
}
I've tried adding a scroll listener to my views but I'm confused with RecyclerView.OnScrollListener's onScrolled method as i can't figure out how to scroll other views.
Any help/suggestion would be helpful.
HorizontelScrollView
{
Linear Layout
{
Vertical list of horizontal recycler views ,
override horizontal recycler view's LayoutManager's
canScroolhorizontally() method to return false ,so that they
all scroll together according to the Grand Parent ScrollView.
}
Our main focus is to scroll that vertical list of horizontal recycler views horizontally ,
first i tried to keep all of them on a horizontal scroll view ,but that is something ,which the android system clearly rejects ,
so i kept one linear layout (in my case vertically oriented ) as a mediator.
so the epg grid now can scroll in vertically as they are inside one vertical recycler view as well as in horizontally because of the Horizontal scroll view.
and we should not allow horizontal list to scroll independentaly ,so I extended layoutmanager and disallow horizontal scrolling ,now they only scroll under grandParent scroll .
You need to always re-calculate position of current items in horizontal recycler views(next h.r.v.)
After scrolling in one h.r.v. re-calculate positions of others based on small amount of scroll movement occured in touched h.r.v.
Then override onViewAttachedToWindow in adapter and use method scrollToPositionWithOffset from LinearLayoutManager of particularly h.r.v to set it to right position.
Also when calculating movement dx, don't forget to disable onScrolled method when finger is down to avoid multiple handling of the same event.
FlexboxLayout (made by Google) lets you make something like this with a RecyclerView and a FlexboxLayoutManager. Since its version 3.0(June 28th 2017) you can drag horizontally through it.
For your example use it like this:
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(getActivity());
layoutManager.setFlexDirection(FlexDirection.ROW);
layoutManager.setFlexWrap(FlexWrap.WRAP);
recyclerView.setLayoutManager(layoutManager);
And don't forget to set the width of your RecyclerView to a bigger number than the screen size, in order to scroll, do not use match_parent:
<android.support.v7.widget.RecyclerView
android:layout_width="3600dp"
android:layout_height="match_parent"/>
Note: the ViewHolders are recycled by rows, not by columns, so be careful if you have too heavy items on a very long RecyclerView
You need to add textviews in horizontal rows. The length of the textview depends on the duration of the program. I have hosted a sample project on Github. Feel free to clone. https://github.com/xardox69/android_EPG

Android NestedScrollView has wrong size after app:layout_behavior

Since Google has published the design support library for android, there are many nice things that can be done without implementing custom code. While i've tested the custom views in this lib, i have found a worse thing, and i didn't know if this is a bug or not.
I have found the cheesesquare project on github. In the activity_detail.xml(layout file) there are 3 CardViews inside the NestedScrollView. If you delete 2 of them, you can see that the NestedScrollView doesn't have the full size of the parent(match_parent). The NestedScrollView is bound to the bottom of the parent view. http://i.stack.imgur.com/BXl7w.png
The NestedScrollView get's his full size when i remove the app:layout_behavior="#string/appbar_scrolling_view_behavior".
But when i remove the layout behavior, the toolbar is not collapsing.
Is there any fix for this? Example layout file can be found here: https://github.com/Smove/cheesesquare/blob/stackoverflow/app/src/main/res/layout/activity_detail.xml
You can build the cheesesquare apk from my github branch stackoverflow
I had this problem and fixed adding:
android:layout_gravity="fill_vertical"
to the NestedScrollView. Then it starts behaving correctly, as I explained here also. Of course the NestedScrollView needs some kind of child (i.e. it must not be empty), otherwise the toolbar won't collapse.
Update
While this works well with small content, I noticed it has some problems showing longer contents, e.g. like the full activity_detail.xml above. The problem is that you can't scroll to the very bottom part of the content - it is unreachable at the bottom.
From my tests I could find that the missing part is as big as the collapsed toolbar (or at least that's what it looks to me). To fix this is issue, and having a solution reliable for both small and big contents, you should add a layout_marginBottom to the ScrollView, so that it gets measured and releases the missing bottom part. Thus:
android:layout_gravity="fill_vertical"
android:layout_marginBottom="?attr/actionBarSize"
or whatever size you gave to the Toolbar.
But still
Scrolling with small contents with this solution, even if the content is justly aligned at the top, isn't really smooth as scrolling with large contents. I'll use until a library fix comes.
Update2
Looks like this was fixed in v22.2.1 .
using the answer by #natario If you instead set a padding for the child (LinearLayout in my case) it will look better:
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_gravity="fill_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="?attr/actionBarSize">
<!--Rest of the code-->
Or in Kotlin you can do something like this:
coordinator.doOnLayout {
nestedScrollView.minimumHeight = resources.displayMetrics.heightPixels - with(TypedValue().also {theme.resolveAttribute(android.R.attr.actionBarSize, it, true)}) {
TypedValue.complexToDimensionPixelSize(data, resources.displayMetrics)}
}
add android:minHeight="?attr/actionBarSize" to CollapsingToolbarLayout
Workaround
Before showing my NestedScrollView and after binding the data to the NestedScrollView content, I call the method fullScroll(int direction) of my NestedScrollView instance with the View.FOCUS_UP direction as argument.
Code example for a fragment:
NestedScrollView scrollView = (NestedScrollView) getActivity().findViewById(R.id.scroll_view);
scrollView.fullScroll(View.FOCUS_UP);
use RecyclerView replace NestedScrollView fix this bug
set item count 1,that ViewHolder return your real contentView;
my code:
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
// 添加分割线
recyclerView.addItemDecoration(new DividerItemDecoration(getApplicationContext()));
recyclerView.setAdapter(new Adapter<ViewHolder>() {
#Override
public int getItemCount() {
return 1;
}
#Override
public void onBindViewHolder(ViewHolder holder, int arg1) {
WebView view = (WebView) holder.itemView;
view.getSettings().setJavaScriptEnabled(true);
view.loadUrl("http://www.baidu.com");
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup arg0, int arg1) {
return new ViewHolder(inflater.inflate(R.layout.webview, arg0, false)) {
};
}
});

Categories

Resources