Reputation: 314
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
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
Reputation: 3235
Have a look at this site and his code on GitHub Nested Recyclerview
Upvotes: -1