I have scrollview inside Recycler view. I am trying to achieve swipeable behaviour like inshorts but the card should scroll to the bottom so user can see all the contents. Once scrollview bottom has been reached then swipe should work to move to next article.
I have implement swipeable behaviour using stacklayoutmanager. The problem is I am not able to scroll to the bottom.
Below is implementation
Recycler view inside main activity
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
MainActivity code, added PagerSnapHelper for fling effect.
adapter = new RecyclerViewAdapter(setData(),this);
layoutManager = new CustomLayoutManager(StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP,1, this);
layoutManager.setPagerFlingVelocity(600);
recyclerView.setLayoutManager(layoutManager);
LinearSnapHelper snapHelper = new CustomSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
recyclerView.setAdapter(adapter);
CustomLayoutManager class which extends the library for swipeable behaviour
class CustomLayoutManager(orientation: ScrollOrientation, count:Int, context: Context) : StackLayoutManager(orientation, count, context){
private var isScrollEnabled = true
fun setScrollEnabled(flag: Boolean) {
isScrollEnabled = flag
}
override fun canScrollVertically(): Boolean {
//Similarly you can customize "canScrollHorizontally()" for managing horizontal scroll
return isScrollEnabled && super.canScrollVertically()
}
}
item view inflated in adapter class
<ScrollView
android:id="#+id/scroll"
android:scrollbars="none"
android:nestedScrollingEnabled="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:text="very very long text will add here"
android:textSize="16sp" />
</ScrollView>
Here in the below image, when trying to scroll to the bottom to read the complete content but recyclerview swipe behaviour becomes active and next item becomes visible.
Update
I have replaced scrollview with nestedscrolledview and found out that
scrollVerticallyBy() method present in RecyclerView.LayoutManager() does not invoke, only scrollHorizontallyBy() is getting called n times.
I have tried so far but not working
ScrollView inside RecyclerView won't scroll
https://stackoverflow.com/a/41139693/7368819
https://stackoverflow.com/a/34060065/7368819
https://stackoverflow.com/a/10334353/7368819
Related
I know how to programmatically scroll a recyclerview to a particular position. But my problem is i have a recyclerview with vertical linearLayout that has another child recyclerview gridlayout with on its each row. Now when i scroll parent recyclerview programmatically it doesn't scroll to given position. But if a remove child recyclerview from it then it scrolls without any issue.
This is what i have tried and everything worked for single recyclerview having no child recyclerview in its rows.
card_recyclerView.getLayoutManager().scrollToPosition(position)
card_recyclerView.smoothScrollToPosition(position);
card_recyclerView.getLayoutManager().scrollToPositionWithOffset(position, 20);
My main recyclerview
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/card_recyclerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:clipToPadding="false"
android:layout_marginLeft="5dp"
android:background="#ffffff"
android:layout_weight="1"
/>
Main recyclerview row item
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/cards_recycler_adapter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/layout_see_more_top"/>
I just created your view without any problem. I managed to scroll with only
card_recyclerView.getLayoutManager().scrollToPosition(position)
card_recyclerView.smoothScrollToPosition(position);
This is my code for viewholder of the first adapter:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ViewHolder(View itemView) {
super(itemView);
RecyclerView recyclerView = itemView.findViewById(R.id.recyclerView2);
recyclerView.setAdapter(new TestAdapter2(itemView.getContext(), mData));
GridLayoutManager linearLayoutManager = new GridLayoutManager(itemView.getContext(), 3);
recyclerView.setLayoutManager(linearLayoutManager);
}
}
If you still have problems please provide the code for adapters and maybe the xml for rows.
This question already has answers here:
How to update RecyclerView Adapter Data
(16 answers)
Closed 3 years ago.
I'm developing a application that the RecyclerView load new data when the user hit the bottom of the scroll. However, the list is already completed and have it's fixed size, I just want to put the content in the Recycler, but when I insert the content in the Recycler, the scroll returns to the top of the list. And then I need to scroll down all over again until I reach the new data that was inserted in to the bottom of the list. All that I want is to maintain the scroll position when the new data is loaded.
I've tried use descendantFocusability = "blocksDescendants", but this didn't work.
This is my XML code where the RecyclerView can be found
<?xmlversion="1.0"encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:headerLayout="#layout/top_navigation_bar"
tools:context=".MainActivity"
android:descendantFocusability="blocksDescendants">
<RelativeLayout
android:id="#+id/productsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:orientation="vertical"
android:background="#EBE7E7"
>
<TextView
android:id="#+id/productsLayoutTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:lineSpacingExtra="0sp"
android:text="#string/produtos_em_destaque"
android:textColor="#000000"
android:textSize="16sp"
android:textStyle="normal"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/productsLayoutTitle"
/>
<ProgressBar
android:id="#+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:layout_alignBottom="#+id/recyclerView"
android:scaleX="1"
android:scaleY="1"
android:progressTint="#1C36FF"
/>
</RelativeLayout>
</RelativeLayout>
The code that is used to add data to the RecyclersView is
fun showData(products:Product,productsListSize:Int){
if(!isStarted){
recyclerView.apply{
layoutManager=LinearLayoutManager(this#MainActivity)
adapter=ViewHolderAdapter(products,productsListSize)
(adapter as ViewHolderAdapter).notifyItemRangeInserted(0,productsListSize)
pageNumber++
}
progressBar.visibility=View.GONE
isStarted=true
}else{
recyclerView.apply{
adapter=ViewHolderAdapter(products,pageNumber*limitPerPage)
(adapter as ViewHolderAdapter).notifyItemRangeInserted(((pageNumber-1)*limitPerPage),pageNumber*limitPerPage)
pageNumber++
}
progressBar.visibility=View.GONE
}
}
My ViewHolderAdapter
packagecom.example.kotlinbasics
importandroid.graphics.Color
importandroid.view.LayoutInflater
importandroid.view.View
importandroid.view.ViewGroup
importandroid.widget.ImageView
importandroid.widget.RatingBar
importandroid.widget.TextView
importandroidx.recyclerview.widget.RecyclerView
importcom.squareup.picasso.Picasso
importkotlinx.android.synthetic.main.recyclerview_layout.view.*
class ViewHolderAdapter(private val products:Product,private val productsListSize:Int):RecyclerView.Adapter<ViewHolderAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent:ViewGroup,viewType:Int):ViewHolder{
val view=LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_layout,parent,false)
return ViewHolder(view)
}
override fun getItemCount()=productsListSize
override fun onBindViewHolder(holder:ViewHolder,position:Int){
holder.productName.text=products.produtos[position].nome
Picasso.get().load(products.produtos[position].img).into(holder.productImage)
}
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
val productName:TextView=itemView.ProductName
val productImage:ImageView=itemView.ProductImage
}
}
All application works really fine, the only problem is with that scroll behavior.
I expected that the scroll position didn't goes to the top of the RecyclersView list when I add new data in him.
As you are creating a new adapter each time, the RecyclerView will be always going back to the start position. You have to update the current adapter, and not to create another adapter with the entire list.
You should have a method on your adapter to manage the item list, such as
fun updateList(product: Product) {
myList.add(product.list)
}
And in your else branch you will need to update the list
recyclerView.apply{
(adapter as? ViewHolderAdapter)?.updateList(products)
}
just use code bellow, and input requared position (in your case - last position)
recyclerView.scrollToPosition()
Create one instance of your recycler view adapter , the pass the orignal list to it . whenever you want to add the any data , add it to the list , and then notify the adapter.
In your case , Just add the product list and notify it
private list<product> list = newArrayList<>();
adapter= new ViewHolderAdapter(list);
OnClick of button when you want to add data , just insert it into the list
list.addAll(productlist);
And now notify it to the adapter
adapter.notifyDataSetChange();
In your case
fun showData(products:Product,productsListSize:Int){
if(!isStarted){
list.addAll(product)
adapter.notifyDataSetChange();
pageNumber++
}
progressBar.visibility=View.GONE
isStarted=true
}
progressBar.visibility=View.GONE
}
}
I have nested RecyclerView and disable click of my child RecyclerView with this way and use this class as my child RecyclerView.
public class MyDisabledRecyclerView extends RecyclerView {
public MyDisabledRecyclerView(Context context) {
super(context);
}
public MyDisabledRecyclerView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyDisabledRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent e) {
return false;
}
My child RecyclerView inside another RecyclerView item and I want when click in child RecyclerView, go to parent RecyclerView item click, so I disable touch event of child RecyclerView but with this way child RecyclerView can not scroll any more, I try put child RecyclerView inside NestedScrollView or recyclerView.setNestedScrollingEnabled(false);
but still not working.
To disable RecyclerView items click :
Add following view into your layout file for masking top of the view hierarchical.
<View
android:id="#+id/recyclerViewDisable"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/transparent"
android:clickable="true"
android:focusable="true"
android:visibility="gone"/>
set this view visibilty View.VISISBLE when you want to disable recyclerview items click.If you want to enable items click then set visiblity View.GONE
To scrollable when Recyclerview items click disable:
recyclerViewDisable.setOnTouchListener((view, motionEvent) -> {
recyclerview.onTouchEvent(motionEvent);
return true;
});
Thats works perfectly. If found workable then vote for correct answer.
Try this:
childRecyclerView.setNestedScrollingEnabled(false);
Wrap most parent RecyclerView inside a NestedScrollView.
<NestedScrollView
...>
<RecyclerView
.../>
</NestedScrollView>
and add recyclerView.setNestedScrollingEnabled(false); on all RecyclerView.
By this you will not have conflicts in Scrolling in Child RecyclerView.
Just a hack for your requirement
Wrap Child RecyclerView with another non clickable View. like
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:nestedScrollingEnabled="false"
android:layout_height="wrap_content" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"/>
</FrameLayout>
Note that.
Parent FrameLayout should be wrap_content.
Child RecyclerView should be wrap_content with nestedScrollingEnabled="false".
Parent RecyclerView should be nestedScrollingEnabled="false".
Overlay View upon child RecyclerView should be match_parent.
Understand
Here we set nestedScrolling false to child RecyclerView. So its scrolling will be disabled and it will take wrap_content height in parent RecyclerView. Now because we added a View upon it with non-clickable so child RecyclerView will not be touchable anymore.
after some research and thinking i do this :
pass all i need in parent RecyclerView to child RecyclerView adapter constructor with position like below :
#Override
public void onBindViewHolder(final ChatList_Adapter.ViewHolder holder, final int position) {
holder.name.setText(chanelsList.get(i).getUser().getFirstname()+" "+chanelsList.get(i).getUser().getLastname());
holder.city_recycler_hosts.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, true);
holder.city_recycler_hosts.setLayoutManager(layoutManager);
holder.city_recycler_hosts.addItemDecoration(new VerticalDividerItemDecoration.Builder(context).color(ContextCompat.getColor(context,R.color.lightergray)).margin(0,20).
build());
CityHosts_Adapter adapter=new CityHosts_Adapter(chanelsList.get(i).getUser().getCities(),chanelsList,file,position,pusher
,auth,message_ist);
holder.city_recycler_hosts.setAdapter(adapter);
}
then in child RecyclerView do what we done in parent RecyclerView click , but we pass position of parent RecyclerView to constructor adapter of child RecyclerView so Instead use getAdapterPosition in child we use position that we got in constructor
I am creating a simple view where on the top I have some elements and below a recyclerView. When I scroll it down, would like to scroll the whole screen, not the only recycler.
I have achieved it with NestedScrollView, however, now the problem appears. Items in the list will be pretty heavy and in this configuration, all the items are bind at the same time(call of onBindViewHolder).
any ideas how to make them recycle and solve this problem?
Here is my xml file
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/darker_gray"
android:orientation="vertical"
tools:context="com.gkuziel.testkotlin.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/ic_available_stores_default" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test text" />
<android.support.v7.widget.RecyclerView
android:id="#+id/list_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isScrollContainer="false"
android:nestedScrollingEnabled="false">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Update:
The found a sweet solution: you add a complex header as ItemDecoration, its great cause your adapter can stay untouched, you just add sth like this:
recyclerView.addItemDecoration(dividerItemDecoration);
the only drawback of this solution is i couldn't make this header clickable (in my case it contains another recyclerView), however I know some people achieved it as well.
For this moment I decided implement heterogeneous recyclerview, with 1 instance of header type and the rest of simple row types.
What is important, the header type is fully binded once in HeaderViewHolder constructor and onBindViewHolder looks like this:
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is HeaderViewHolder) {
//do nothing
Log.d("ProductAdapter", "Binding: Header")
} else if (holder is ItemViewHolder) {
Log.d("ProductAdapter", "Binding: " + position.toString())
val searchItem = items!![position - 1]
//here the proper binding is going on
}
}
You can try setting the recyclerview layout manager's method canScrollVertical to false and it won't respond to any touch inner scroll events.
override below method and return false.
boolean canScrollVertically()
here it is how to set.
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
// Lookup the recyclerview in activity layout
RecyclerView listTest = (RecyclerView) findViewById(R.id.list_test);
// Attach the adapter to the recyclerview to populate items
listTest.setAdapter(adapter);
// Set layout manager to position the items
listTest.setLayoutManager(new LinearLayoutManager(this){
#Override
public boolean canScrollVertically(){
return false;
}
});
// That's all!
}
I have Implemented a RecyclerView with GridLayoutManager which has both Vertical and Horizontal Scroll , I need to scroll the layout to particular position , referred many codes but it did'nt worked out for me .
Java Class code :
void RackGen()
{
STRarray = (String[]) mStringList.toArray(new String[mStringList.size()]);
ROWarray = (String[]) mRowList.toArray(new String[mRowList.size()]);
numcol = Integer.parseInt(col);
numdata = Integer.parseInt(num);
rv = (RecyclerView) findViewById(R.id.rview);
int numberOfColumns = numcol;
GridLayoutManager GM2 = new GridLayoutManager(this,numberOfColumns);
rv.setLayoutManager(GM2);
rv.setNestedScrollingEnabled(true);
rv.setHasFixedSize(true);
adapter = new MyRecyclerViewAdapter(this, STRarray, ROWarray);
rv.setAdapter(adapter);
XML :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:layout_centerHorizontal="true"
android:id="#+id/llzoom"
android:orientation="vertical"
>
<android.support.v4.widget.NestedScrollView
android:id="#+id/nested_scroll_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none"
android:overScrollMode="never">
<android.support.v7.widget.RecyclerView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/rview">
</android.support.v7.widget.RecyclerView>
</HorizontalScrollView>
</ScrollView>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
Only after adding nested scroll view along with vertical and horizontal scroll , scrolling worked smoothly on both directions . Now i need to scroll to particular position.
Thank You
You need to reference to your NestedScrollView inside your activity and then call the scrollTo(x,y) on that object, example:
NestedScrollView nestedScrollView = (NestedScrollView) findViewById(R.id.nested_scroll_view);
nestedScrollView.scrollTo(x,y);
x and y are horizontal and vertical scrolls.
I know this is an older post, and I haven't tested this code on nested scroll views, but as you only have one position value, you can try this technique.
This works great for scrolling to the currently selected view of a recyclerview GridLayoutManager with dynamic autofit (I use a custom recyclerview for autofit, not the grid itself).
After I set the adapter:
recyclerView.setAdapter(adapter);
I put (just under it in onCreate):
recyclerView.post(new Runnable()
{
#Override
public void run()
{
layoutManager.scrollToPositionWithOffset(currentSelected, 0);
}
});
It waits for the views to be drawn, then it scrolls to the position given. It works wonderfully!
I got the idea here: https://stackoverflow.com/a/52895262/2905354