Usage scenarios and problems
When a list needs a list with the same sliding direction of the outer list, when you want to slide the inner list, the gesture will be intercepted by the outer RecyclerView by default, resulting in the inner list can not slide. ! [image](D:/Program Files/LICEcap/1.gif)
The solution
Thinking a
Since the outer layer will intercept the Touch event first and process it directly, to make the inner layer can get the Touch event, then make the outer RecyclerView to obtain the Touch, judge whether the location is in the inner RecyclerView, if so, the outer layer is not processed and handed over to the inner layer.
Implementation steps:
- Custom external RecyclerView, rewrite onInterceptTouchEvent method, handle touch events in the inside
- When the touch event location is in the inner RecyclerView, the touch event is not intercepted and distributed directly
The key code is as follows
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
val manager = layoutManager as LinearLayoutManager
// Get the first visible item position
val firstVisiblePosition = manager.findFirstVisibleItemPosition()
if (firstVisiblePosition == 0) {// Get the bottom edge of the view relative to the parent layout, and the visible height
val height = getChildAt(firstVisiblePosition).bottom
// If the location of the touch event is in the need to process item (i.e. in the inner RecyclerView), the need to process the touch event, do not intercept, directly distribute
if (e.y <= height){
return false}}return super.onInterceptTouchEvent(e)
}
Copy the code
The scheme needs to obtain the location information of the inner RecyclerView in the outer RecyclerView. Because the inner RecyclerView is in the first place, the processing is relatively simple. If it is in any position, When onInterceptTouchEvent is processed, it will be troublesome to obtain the RecyclerView location of the inner layer, so it is not recommended to adopt this scheme
Idea 2
Direct processing of RecyclerView, if touch in its own location, let the parent container is not intercepted, so that there is no need to judge the position, if it can accept the touch event, it must touch the area in the RecyclerView location
Implementation steps:
Custom layer RecyclerView, rewrite onInterceptTouchEvent method, notification container does not intercept the key code as follows
override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
// Let the parent layout not block
parent.requestDisallowInterceptTouchEvent(true)
return super.onInterceptTouchEvent(e)
}
Copy the code
It can be seen that the effect is basically the same as the idea, but the distribution of events is simpler, no need to calculate the relevant location information, and no matter where the inner RecyclerView is, it can slide normally.
The above two schemes can basically solve the same-direction sliding double RecyclerView nesting problem, but the event processing is separated, in the inner RecyclerView position can only slide the inner layer, the outer region can only slide the outer layer, and we expect that after the inner RecyclerView sliding to the bottom, RecyclerView can continue to slide, no need to loosen your finger to slide the outer layer again (the same is true for the inner layer to slide to the top, continue to slide the outer layer to slide)
Thought three
The parent container is not intercepted by touch. We can handle that as if we were already at the top when we were sliding down, there is no need for the parent container to distribute (the same goes for sliding up). So let’s continue to optimize on the basis of idea two
Implementation steps:
Implementation steps:
- Custom layer RecyclerView, rewrite onInterceptTouchEvent method, handle touch events in the inside
- Let the parent container not intercept in DOWN event handling (so that subsequent events such as MOVE can be received)
- Determine the direction of the slide and whether it has reached the boundary (bottom or top). If it has reached the boundary, it does not need to process the gesture itself and let the parent continue to intercept
class CustomRecyclerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {
// Record the last finger position
private var mLastY = 0f
// Whether it has slipped to the bottom
private var isToBottom = false
// Whether it has slid to the top
private var isToTop = true
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
// Record the press position
mLastY = event.y
// If the finger presses the touch area within itself, the parent View is not allowed to intercept the event
parent.requestDisallowInterceptTouchEvent(true)
}
MotionEvent.ACTION_MOVE -> {
checkPosition(event.y)
if (isToBottom || isToTop) {
// When you have slid to the top or bottom, you do not need to handle the gesture
parent.requestDisallowInterceptTouchEvent(false)
return false
} else {
parent.requestDisallowInterceptTouchEvent(true)
}
mLastY = event.y
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> parent.requestDisallowInterceptTouchEvent(
false)}return super.dispatchTouchEvent(event)
}
/** * Check the position of the item to determine whether to slide */
private fun checkPosition(nowY: Float) {
// Just handle the LinearLayoutManager case for now
val manager = layoutManager as LinearLayoutManager
isToTop = false
isToBottom = false
// Get the visible item position
val firstVisiblePosition = manager.findFirstCompletelyVisibleItemPosition()
val lastVisiblePosition = manager.findLastCompletelyVisibleItemPosition()
// If the current item is displayed
if(layoutManager!! .childCount >0) {
if (lastVisiblePosition == manager.itemCount - 1) {
// Check if you can slide up and the sliding direction is up
if (canScrollVertically(-1) && nowY < mLastY) {
// The tag has slid to the bottom and cannot slide up anymore
isToBottom = true}}else if (firstVisiblePosition == 0) {
// Check if you can slide down, and the sliding direction is down
if (canScrollVertically(1) && nowY > mLastY) {
// The tag has slid to the top and cannot slide down anymore
isToTop = true
}
}
}
}
}
Copy the code
It can be seen that when the inner RecyclerView slides to the top or bottom, the outer list will trigger cosliding when the finger does not loosen and continues to slide, which is in line with our expected effect