Макет Android: горизонтальный Recyclerview внутри вертикального Recyclerview внутри Viewpager с поведением прокрутки
Это приложение, которое я пытаюсь построить со всеми элементами, отображенными ниже:
все работает, однако я хочу, чтобы внутренний горизонтальный recyclerview не захватывал никаких вертикальных свитков. Все вертикальные прокрутки должны идти к внешнему вертикальному recyclerview, а не горизонтальному, так что вертикальная прокрутка позволит панели инструментов выйти из поля зрения в соответствии с его scrollFlag.
когда я положил палец на части" StrawBerry Plant " в recyclerview и прокрутите вверх, прокрутите панель инструментов:
если я положил палец на горизонтальный scrollview и прокрутите вверх, он не прокручивает панель инструментов вообще.
ниже приведен мой код макета xml до сих пор.
на активность XML-макета:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment_container"
android:clipChildren="false">
<android.support.design.widget.CoordinatorLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
>
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways">
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
style="@style/CustomTabLayout"
/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout>
на "фрукты" фрагмент XML layout (который является кодом для фрагмента - фрагмент помечен на приведенном выше рисунке):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:visibility="gone"
android:layout_centerInParent="true"
android:indeterminate="true"/>
<!-- <android.support.v7.widget.RecyclerView-->
<com.example.simon.customshapes.VerticallyScrollRecyclerView
android:id="@+id/main_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
я использовал пользовательский класс VerticallyScrollRecyclerView, который следует примеру google обработки событий касания в viewgroup. Его цель-перехватить и использовать все события вертикальной прокрутки, чтобы прокручивать панель инструментов:http://developer.android.com/training/gestures/viewgroup.html
код VerticallyScrollRecyclerView это ниже:
public class VerticallyScrollRecyclerView extends RecyclerView {
public VerticallyScrollRecyclerView(Context context) {
super(context);
}
public VerticallyScrollRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticallyScrollRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
ViewConfiguration vc = ViewConfiguration.get(this.getContext());
private int mTouchSlop = vc.getScaledTouchSlop();
private boolean mIsScrolling;
private float startY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
// Always handle the case of the touch gesture being complete.
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Release the scroll.
mIsScrolling = false;
startY = ev.getY();
return super.onInterceptTouchEvent(ev); // Do not intercept touch event, let the child handle it
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
Log.e("VRecView", "its moving");
if (mIsScrolling) {
// We're currently scrolling, so yes, intercept the
// touch event!
return true;
}
// If the user has dragged her finger horizontally more than
// the touch slop, start the scroll
// left as an exercise for the reader
final float yDiff = calculateDistanceY(ev.getY());
Log.e("yDiff ", ""+yDiff);
// Touch slop should be calculated using ViewConfiguration
// constants.
if (Math.abs(yDiff) > 5) {
// Start scrolling!
Log.e("Scroll", "we are scrolling vertically");
mIsScrolling = true;
return true;
}
break;
}
}
return super.onInterceptTouchEvent(ev);
}
private float calculateDistanceY(float endY) {
return startY - endY;
}
}
на "подходящий" макет который является recyclerview в вертикальном recyclerview:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/white"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Favourite"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:id="@+id/header_fav"/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_below="@+id/header_fav"
android:id="@+id/recyclerview_fav">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
это беспокоило меня некоторое время, и мне не удалось придумать решение. Кто-нибудь знает как решить эту проблему?
5 очков Гриффиндору за правильный ответ и, конечно же, репутация указывает на SO.
4 ответов
протестированное решение:
все, что вам нужно, это позвонить mInnerRecycler.setNestedScrollingEnabled(false);
на RecyclerView
s
объяснение:
RecyclerView
имеет поддержку вложенной прокрутки, введенной в API 21
в рамках реализации NestedScrollingChild
интерфейс. Это ценная функция, когда у вас есть вид прокрутки внутри другого, который прокручивается в том же направлении, и вы хотите прокрутить внутренний View
только при фокусировке.
в любом случае RecyclerView
по умолчанию звонки RecyclerView.setNestedScrollingEnabled(true);
на себя при инициализации. Теперь вернемся к проблеме, так как ваш RecyclerView
s находятся в пределах одного и того же ViewPager
что есть AppBarBehavior
, the CoordinateLayout
должен решить, какой свиток реагировать, когда вы прокручиваете из своего внутреннего RecyclerView
; когда ваш внутренний RecyclerView
вложенная прокрутка включена, она получает фокус прокрутки и CoordinateLayout
будет отвечать на его прокрутку по внешней RecyclerView
с прокруткой. Дело это, так как ваш внутренний RecyclerView
s не прокручивать по вертикали, нет изменения вертикальной прокрутки (от CoordinateLayout
точка зрения), и если нет никаких изменений, то AppBarLayout
не менялись.
в вашем случае, потому что ваш внутренний RecyclerView
с прокруткой в другом направлении, вы можете отключить его, тем самым причинив CoordinateLayout
игнорировать его прокрутку и реагировать на внешний RecyclerView
' s прокрутка.
обратите внимание:
атрибут xml android:nestedScrollingEnabled="boolean"
не предназначен для использования с RecyclerView
, и попытка использовать android:nestedScrollingEnabled="false"
в результате java.lang.NullPointerException
так, по крайней мере сейчас, вам придется сделать это в коде.
протестированное решение, использовать специальный NestedScrollView()
.
код:
public class CustomNestedScrollView extends NestedScrollView {
public CustomNestedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// Explicitly call computeScroll() to make the Scroller compute itself
computeScroll();
}
return super.onInterceptTouchEvent(ev);
}
}
Я немного опоздал, но это определенно будет работать для других, стоящих перед той же проблемой
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
int action = e.getAction();
// Toast.makeText(getActivity(),"HERE",Toast.LENGTH_SHORT).show();
switch (action) {
case MotionEvent.ACTION_POINTER_UP:
rv.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return false;
}
Я бы предложил вам добавить горизонтальный recyclerview внутри фрагментов, таких как приложение google