Android RecyclerView inside NestedScrollView Horizontal Scrolling 很难做到
Android recyclerView inside NestedScrollView Horizontal Scrolling hard to do
我有一个 nestedScrollView
有很多 recyclerViews
将使用 linearLayoutManager(HORIZONTAL)
。
用nestedScrollingEnabled(false)
也可以,但是水平滚动非常难做。
如果我想向右滚动 recyclerView,我必须完全停止向下滚动 nestedScrollView,然后向右滚动。
将 nestedScrollingEnabled
设置为 true 只会禁用 nestedScrollView 的 fling。
我怎样做才能使 horizontal recyclerViews
滚动起来更轻松?
能否请您尝试以下示例或方法,这可能会对您有所帮助。
myRecyclerViewHorizental.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false) {
@Override
public boolean canScrollHorizontally() {
//return false if you don't want to scroll horizontal
return true;
}
@Override
public boolean canScrollVertically() {
//return false if you don't want to scroll vertical
return false;
}
});
非常感谢你的提问,不懂方法的管理是很典型的。
这是一个解决方案。
魔术方法是requestDisallowInterceptTouchEvent,这个方法可以允许或拒绝触摸事件。
public static void smartScroll(final ScrollView scrollView, final RecyclerView recyclerView) {
recyclerView.setOnTouchListener(new View.OnTouchListener() {
private boolean isListOnTop = false, isListOnBottom = false;
private float delta = 0, oldY = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
scrollView.requestDisallowInterceptTouchEvent(true);
recyclerView.requestDisallowInterceptTouchEvent(false);
oldY = event.getY();
break;
case MotionEvent.ACTION_UP:
delta = 0;
break;
case MotionEvent.ACTION_MOVE:
delta = event.getY() - oldY;
oldY = event.getY();
isListOnTop = false;
isListOnBottom = false;
View first = recyclerView.getChildAt(0);
View last = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (first != null && layoutManager.findFirstVisibleItemPosition() == 0 && first.getTop() == 0 && delta > 0.0f) {
isListOnTop = true;
}
if (last != null && layoutManager.findLastVisibleItemPosition() == recyclerView.getChildCount() - 1 && last.getBottom() <= recyclerView.getHeight() && delta < 0.0f) {
isListOnBottom = true;
}
if ((isListOnTop && delta > 0.0f) || (isListOnBottom && delta < 0.0f)) {
scrollView.requestDisallowInterceptTouchEvent(false);
recyclerView.requestDisallowInterceptTouchEvent(true);
}
break;
default:
break;
}
return false;
}
});
}
此解决方案适用于 ScrollView 和 RecyclerView,但您可以通过使用 requestDisallowInterceptTouchEvent
.
将其用于任何类型的视图,例如 ViewPager、ScrollView、ListView
几个月前我也遇到了同样的问题,唯一对我有用的解决方案是创建您自己的自定义 RecyclerView 和 NestedScrollView,如下所示:
MeroHorizontalRecyclerView class
public class MeroHorizontalRecyclerView extends RecyclerView {
private GestureDetector mGestureDetector;
public MeroHorizontalRecyclerView(Context context) {
super(context);
init();
}
public MeroHorizontalRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MeroHorizontalRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
setLayoutManager(layoutManager);
mGestureDetector = new GestureDetector(getContext(), new VerticalScrollDetector());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if (mGestureDetector.onTouchEvent(e)) {
return false;
}
return super.onInterceptTouchEvent(e);
}
public class VerticalScrollDetector extends
GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceY) > Math.abs(distanceX);
}
}
}
MeroNestedScrollView class
public class MeroNestedScrollView extends NestedScrollView {
public MeroNestedScrollView(Context context) {
super(context);
}
public MeroNestedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MeroNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private float xDistance, yDistance, lastX, lastY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
xDistance = yDistance = 0f;
lastX = ev.getX();
lastY = ev.getY();
// This is very important line that fixes
computeScroll();
break;
case MotionEvent.ACTION_MOVE:
final float curX = ev.getX();
final float curY = ev.getY();
xDistance += Math.abs(curX - lastX);
yDistance += Math.abs(curY - lastY);
lastX = curX;
lastY = curY;
if (xDistance > yDistance) {
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
}
现在将它们导入 xml 为:
<com.hancha.utils.MeroNestedScrollView
android:id="@+id/nestedScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.hancha.utils.MeroHorizontalRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/app_bg_color"/>
</com.hancha.utils.MeroNestedScrollView>
希望对您有所帮助!
我有一个 nestedScrollView
有很多 recyclerViews
将使用 linearLayoutManager(HORIZONTAL)
。
用nestedScrollingEnabled(false)
也可以,但是水平滚动非常难做。
如果我想向右滚动 recyclerView,我必须完全停止向下滚动 nestedScrollView,然后向右滚动。
将 nestedScrollingEnabled
设置为 true 只会禁用 nestedScrollView 的 fling。
我怎样做才能使 horizontal recyclerViews
滚动起来更轻松?
能否请您尝试以下示例或方法,这可能会对您有所帮助。
myRecyclerViewHorizental.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false) {
@Override
public boolean canScrollHorizontally() {
//return false if you don't want to scroll horizontal
return true;
}
@Override
public boolean canScrollVertically() {
//return false if you don't want to scroll vertical
return false;
}
});
非常感谢你的提问,不懂方法的管理是很典型的。 这是一个解决方案。
魔术方法是requestDisallowInterceptTouchEvent,这个方法可以允许或拒绝触摸事件。
public static void smartScroll(final ScrollView scrollView, final RecyclerView recyclerView) {
recyclerView.setOnTouchListener(new View.OnTouchListener() {
private boolean isListOnTop = false, isListOnBottom = false;
private float delta = 0, oldY = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
scrollView.requestDisallowInterceptTouchEvent(true);
recyclerView.requestDisallowInterceptTouchEvent(false);
oldY = event.getY();
break;
case MotionEvent.ACTION_UP:
delta = 0;
break;
case MotionEvent.ACTION_MOVE:
delta = event.getY() - oldY;
oldY = event.getY();
isListOnTop = false;
isListOnBottom = false;
View first = recyclerView.getChildAt(0);
View last = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (first != null && layoutManager.findFirstVisibleItemPosition() == 0 && first.getTop() == 0 && delta > 0.0f) {
isListOnTop = true;
}
if (last != null && layoutManager.findLastVisibleItemPosition() == recyclerView.getChildCount() - 1 && last.getBottom() <= recyclerView.getHeight() && delta < 0.0f) {
isListOnBottom = true;
}
if ((isListOnTop && delta > 0.0f) || (isListOnBottom && delta < 0.0f)) {
scrollView.requestDisallowInterceptTouchEvent(false);
recyclerView.requestDisallowInterceptTouchEvent(true);
}
break;
default:
break;
}
return false;
}
});
}
此解决方案适用于 ScrollView 和 RecyclerView,但您可以通过使用 requestDisallowInterceptTouchEvent
.
几个月前我也遇到了同样的问题,唯一对我有用的解决方案是创建您自己的自定义 RecyclerView 和 NestedScrollView,如下所示:
MeroHorizontalRecyclerView class
public class MeroHorizontalRecyclerView extends RecyclerView {
private GestureDetector mGestureDetector;
public MeroHorizontalRecyclerView(Context context) {
super(context);
init();
}
public MeroHorizontalRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MeroHorizontalRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
setLayoutManager(layoutManager);
mGestureDetector = new GestureDetector(getContext(), new VerticalScrollDetector());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if (mGestureDetector.onTouchEvent(e)) {
return false;
}
return super.onInterceptTouchEvent(e);
}
public class VerticalScrollDetector extends
GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceY) > Math.abs(distanceX);
}
}
}
MeroNestedScrollView class
public class MeroNestedScrollView extends NestedScrollView {
public MeroNestedScrollView(Context context) {
super(context);
}
public MeroNestedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MeroNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private float xDistance, yDistance, lastX, lastY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
xDistance = yDistance = 0f;
lastX = ev.getX();
lastY = ev.getY();
// This is very important line that fixes
computeScroll();
break;
case MotionEvent.ACTION_MOVE:
final float curX = ev.getX();
final float curY = ev.getY();
xDistance += Math.abs(curX - lastX);
yDistance += Math.abs(curY - lastY);
lastX = curX;
lastY = curY;
if (xDistance > yDistance) {
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
}
现在将它们导入 xml 为:
<com.hancha.utils.MeroNestedScrollView
android:id="@+id/nestedScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.hancha.utils.MeroHorizontalRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/app_bg_color"/>
</com.hancha.utils.MeroNestedScrollView>
希望对您有所帮助!