Alexandru Sandu
Alexandru Sandu

Reputation: 314

Horizontal RecyclerView inside a Horizontal RecyclerView

I am trying to use a RecyclerView with a Horizontal LinearLayoutManager inside another RecyclerView with a Horizontal LinearLayoutManager. The whole hierarchy is like this: RecyclerView, the child of the Recycler is a ScrollView which contains a TextView and another RecyclerView. For better understanding, i want the first recycler to work like, but not exactly the same, a ViewPager (i don't want to use a ViewPager). The problem is that the when i try to scroll horizontally on the child Recycler the motion event is caught by the parent Recycler resulting in scrolling to the next page without being able to scroll through the child Recycler.

MainActivityLayout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    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=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/RVpage"
        android:descendantFocusability="blocksDescendants"
        android:focusableInTouchMode="false"
        android:focusable="false"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setRecycler();
    }

    private void setRecycler() {
        recyclerView=(RecyclerView) findViewById(R.id.RVpage);
        SnapHelper snapHelper = new LinearSnapHelper();
        snapHelper.attachToRecyclerView(recyclerView);
        BigRecyclerAdapter bigRecyclerAdapter=new BigRecyclerAdapter(this);
        recyclerView.setAdapter(bigRecyclerAdapter);

        //recyclerView.setNestedScrollingEnabled(true);


        recyclerView.setLayoutManager(new LinearLayoutManager(
            this, LinearLayoutManager.HORIZONTAL, false)
        );
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                //Toast.makeText(MainActivity.this, "CHANGED BIG", Toast.LENGTH_SHORT).show();
                super.onScrollStateChanged(recyclerView, newState);
            }
        });
    }
}

PageRecycler layout:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView             xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:text="nskjdnaskdn \n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak\n sudbadbak
v
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
v
v
\n sudbadbak
\n sudbadbak
v
v
\n sudbadbak
vv
\n sudbadbak
v
v
\n sudbadbak"
        android:textColor="@android:color/black"
        android:textSize="50sp"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:focusableInTouchMode="true"
        android:focusedByDefault="true"
        android:descendantFocusability="afterDescendents"
        android:focusable="true"
        android:padding="20dp" />

    </LinearLayout>

</ScrollView>

In the relativeLayout i left all the things i have tried, from clickable=true to descendentFocusability.

PageRecycler Adapter:

class BigRecyclerAdapter(private val context: MainActivity) :     RecyclerView.Adapter<BigRecyclerAdapter.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
        val v = LayoutInflater.from(parent?.context)
            .inflate(R.layout.raw, parent, false)

        return ViewHolder(v)
    }

/* override fun onViewAttachedToWindow(holder: ViewHolder?) {
    var parent = holder?.recycler?.parent


    val DEBUG_TAG_SCROLL = "DEBUG_TAG_SCROLL"
    Log.d(DEBUG_TAG_SCROLL, "NESTED_SCROLL_VALUE: ${holder?.recycler?.hasNestedScrollingParent()}")



    if (holder?.recycler?.parent is RecyclerView) {
        Log.d(DEBUG_TAG_SCROLL, "Good parent")
    }
    super.onViewAttachedToWindow(holder)
}*/

    override fun getItemCount(): Int {
        return 5
    }

    override fun onBindViewHolder(holder: ViewHolder?, position: Int) {

        holder?.bind()
//        val DEBUG_TAG_SCROLL = "DEBUG_TAG_SCROLL"
////        Log.d(DEBUG_TAG_SCROLL, "NESTED_SCROLL_VALUE: ${holder?.recycler?.hasNestedScrollingParent()}")
//
//        if(holder?.recycler?.parent is RecyclerView){
//            Log.d(DEBUG_TAG_SCROLL, "Good parent")
//        }
    }


    inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
        var recycler: RecyclerView

        init {
            recycler = v.findViewById(R.id.photo) as RecyclerView
        }

        fun bind() {
            recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
                override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                    Toast.makeText(context, "CHANGED SMALL", Toast.LENGTH_SHORT).show()
                    super.onScrollStateChanged(recyclerView, newState)
                }

//            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
//                super.onScrolled(recyclerView, dx, dy)
//            }
            })

            val smallRecyclerAdapter = SmallRecyclerAdapter(context)
            recycler.adapter = smallRecyclerAdapter
            recycler.layoutManager = LinearLayoutManager(
                    context, LinearLayoutManager.HORIZONTAL, false
            )
            recycler.scrollToPosition(5)
        }
    }
}

PhotoRecycler raw (child recyclerView):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/photoView"/>

</LinearLayout>

PhotoRecycler adapter:

class SmallRecyclerAdapter(private val context: MainActivity): RecyclerView.Adapter<SmallRecyclerAdapter.ViewHolder>(){
    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
        val v = LayoutInflater.from(parent?.context)
            .inflate(R.layout.raw_small, parent, false)
        // set the view's size, margins, paddings and layout parameters

        return ViewHolder(v)
    }

    override fun getItemCount(): Int {
        return 10
    }

    override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
        //TODO here

        var drawableName="p"+((position%4)+1)
        val drawable =  context.getResources().getDrawable(context.getResources()
            .getIdentifier(drawableName, "drawable", context.getPackageName()))
        holder?.imageView?.setImageDrawable(drawable)
    }


    inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
        var imageView: ImageView

        init {
            imageView=v.findViewById(R.id.photoView) as ImageView
        }
    }
}

If you need anymore details let me know and i will update.

Upvotes: 3

Views: 2122

Answers (2)

Omar HossamEldin
Omar HossamEldin

Reputation: 3111

I had implemented before a nested ScrollView, What I thought that the child scroll view is always consuming touches, and the parent scroll view will never listen to them

Here comes the role of the method onInterceptTouchEvent which control whether the view consumes the touch or just pass it to its parent.

So the plan was to calculate the direction of the scroll and do I reached the end of scrolling of the child ScrollView or not.

This code was written 2 years ago, so please forgive if something deprecated.

public class CustomScrollView extends ScrollView {

private boolean bottomReached = false;
private boolean topReached = true;
private float startTouch = -1;
private float distance = -1;

public CustomScrollView(Context context) {
    super(context);
}

public CustomScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startTouch = ev.getY();
            break;
        case MotionEvent.ACTION_UP:
            startTouch = -1;
            break;
        case MotionEvent.ACTION_MOVE:
            distance = ev.getY() - startTouch;
                if (Math.abs(distance) < 10) {
                    boolean onIntercept = super.onInterceptTouchEvent(ev);
                    return onIntercept;
                } else {
                    if (!bottomReached && !topReached) {
                        return true;
                    } else {
                        if (distance > 0) {
                            // Scrolling Up
                            return bottomReached;
                        } else {
                            // Scrolling Down
                            return topReached;
                        }
                    }
                }
    }
    return super.onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startTouch = ev.getY();
            break;
        case MotionEvent.ACTION_UP:


            startTouch = -1;
            break;
        case MotionEvent.ACTION_MOVE:
            distance = ev.getY() - startTouch;
    }
    return super.onTouchEvent(ev);
}
}

Upvotes: 0

Vector
Vector

Reputation: 3235

Have a look at this site and his code on GitHub Nested Recyclerview

Upvotes: -1

Related Questions