Dies ist die App, die ich mit allen folgenden Elementen erstellen möchte:
Alles funktioniert, jedoch möchte ich, dass der innere horizontale Recycler keine der vertikalen Schriftrollen erfasst. Alle vertikalen Bildlaufleisten müssen in Richtung der äußeren vertikalen Recyclingansicht gehen, nicht in die horizontale, so dass die vertikale Bildlaufleiste die Symbolleiste entsprechend ihrem scrollFlag aus dem Sichtbereich verlassen kann.
Wenn ich den Finger auf den Bereich "StrawBerry Plant" der Recyclingübersicht lege und nach oben scrolle, scrolle er aus der Symbolleiste heraus:
Wenn ich den Finger auf die horizontale Bildlaufansicht lege und nach oben scrolle, wird die Symbolleiste überhaupt nicht angezeigt.
Das Folgende ist mein XML-Layout-Code bisher.
Das Activity XML-Layout:
<?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>
Das "Fruits" -Fragment XML-Layout (das ist der Code für das Fragment - das Fragment ist im obigen Bild gekennzeichnet):
<?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>
Ich habe eine benutzerdefinierte Klasse namens VerticallyScrollRecyclerView verwendet, die dem Beispiel von Google für die Handhabung von Berührungsereignissen in einer Ansichtsgruppe folgt. Ihr Ziel ist es, alle vertikalen Bildlaufereignisse abzufangen und zu konsumieren, damit sie in der Symbolleiste ein- und ausgeblendet werden: http://developer.Android.com/training/gestures/viewgroup.html
Der Code für VerticallyScrollRecyclerView befindet sich unten:
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;
}
}
Das "Favorite" -Layout, das die Recyclingübersicht innerhalb der vertikalen Recyclingübersicht ist:
<?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>
Das nervt mich schon seit einiger Zeit und ich habe keine Lösung gefunden. Weiß jemand, wie man dieses Problem löst?
5 Punkte an Griffindor für die richtige Antwort und natürlich Reputationspunkte für SO.
Getestete Lösung:
Alles, was Sie brauchen, ist, mInnerRecycler.setNestedScrollingEnabled(false);
auf Ihrem inneren RecyclerView
s aufzurufen
Erklärung :
RecyclerView
bietet Unterstützung für verschachteltes Scrollen in API 21
durch Implementierung der NestedScrollingChild
-Schnittstelle. Dies ist eine wertvolle Funktion, wenn Sie eine Bildlaufansicht in einer anderen haben, die in dieselbe Richtung blättert und Sie das innere View
nur bei Fokussierung scrollen möchten.
In jedem Fall ruft RecyclerView
standardmäßig RecyclerView.setNestedScrollingEnabled(true);
bei der Initialisierung auf. Nun zurück zum Problem: Da sich beide RecyclerView
s innerhalb desselben ViewPager
befinden, das über das AppBarBehavior
verfügt, muss das CoordinateLayout
entscheiden, auf welche Bildlaufleiste Sie reagieren, wenn Sie von Ihrem inneren RecyclerView
aus scrollen. Wenn der verschachtelte Bildlauf Ihres inneren RecyclerView
aktiviert ist, wird der Bildlauffokus angezeigt, und der CoordinateLayout
wählt, ob er auf den Bildlauf des äußeren RecyclerView
-Bildlaufs reagiert. Die Sache ist, dass, da Ihre inneren RecyclerView
s nicht vertikal scrollen, keine vertikale Bildlaufänderung erfolgt (aus Sicht von CoordinateLayout
), und wenn keine Änderung vorliegt, ändert sich auch AppBarLayout
nicht.
In Ihrem Fall können Sie, da Ihre inneren RecyclerView
s in eine andere Richtung blättern, die Funktion deaktivieren, so dass CoordinateLayout
das Scrollen ignoriert und auf das Scrollen des äußeren RecyclerView
reagiert.
Beachten :
Das XML-Attribut Android:nestedScrollingEnabled="boolean"
ist nicht für die Verwendung mit RecyclerView
vorgesehen. Ein Versuch, Android:nestedScrollingEnabled="false"
zu verwenden, führt zu einem Java.lang.NullPointerException
. Daher müssen Sie es zumindest im Code tun.
Ich bin etwas spät dran, aber das funktioniert definitiv für andere, die das gleiche Problem haben
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;
}
Getestete Lösung, verwenden Sie eine benutzerdefinierte NestedScrollView()
.
Code:
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);
}
}
versuchen
public OuterRecyclerViewAdapter(List<Item> items) {
//Constructor stuff
viewPool = new RecyclerView.RecycledViewPool();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Create viewHolder etc
holder.innerRecyclerView.setRecycledViewPool(viewPool);
}
inneres Recylerview verwendet den gleichen Viewpool und wird glatter
Ich würde vorschlagen, dass Sie die horizontale Recyclingübersicht in Fragmenten wie der Google-App hinzufügen
wenn jemand noch auf der Suche ist, versuchen Sie Folgendes:
private val Y_BUFFER = 10
private var preX = 0f
private var preY = 0f
mView.rv.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
override fun onTouchEvent(p0: RecyclerView, p1: MotionEvent) {
}
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_DOWN -> rv.parent.requestDisallowInterceptTouchEvent(true)
MotionEvent.ACTION_MOVE -> {
if (Math.abs(e.x - preX) > Math.abs(e.y - preY)) {
rv.parent.requestDisallowInterceptTouchEvent(true)
} else if (Math.abs(e.y - preY) > Y_BUFFER) {
rv.parent.requestDisallowInterceptTouchEvent(false)
}
}
}
preX = e.x
preY = e.y
return false
}
override fun onRequestDisallowInterceptTouchEvent(p0: Boolean) {
}
})
es prüft, ob gerade ein horizontaler Bildlauf ausgeführt wird, und lässt dann nicht zu, dass das übergeordnete Element das Ereignis behandelt