2013-07-05 10 views
9

Ich versuche, die SlidingPaneLayout mit ViewPager zu bedienen, wie soMit Android SlidingPaneLayout mit ViewPager

<?xml version="1.0" encoding="utf-8"?> 

<android.support.v4.widget.SlidingPaneLayout 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     android:id="@+id/scientific_graph_slidingPaneLayout" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"> 

    <!-- 
     The first child view becomes the left pane. 
    --> 

    <ListView 
      android:id="@+id/left_pane" 
      android:layout_width="240dp" 
      android:layout_height="match_parent" 
      android:layout_gravity="left" /> 
    <!-- 
     The second child becomes the right (content) pane. 
    --> 

    <android.support.v4.view.ViewPager 
      android:id="@+id/scientific_graph_viewPager" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent"> 
    </android.support.v4.view.ViewPager> 

</android.support.v4.widget.SlidingPaneLayout> 

Die SlidingPaneLayout gleitet, wenn ich von der linken Kante ziehen; Allerdings kann ich den ViewPager nicht gleiten sehen, wenn ich von der rechten Kante ziehe. Wenn ich von der rechten Kante ziehe, gleitet es sehr wenig und schnappt dann zurück.

Ist das überhaupt möglich? Gibt es einen besseren Weg, dies zu tun?

Ich fand, dass ich durch das Bewegen meines Fingers nach oben und der nach links, den Sichtpager wischen kann.

+0

Ich habe versucht, das Gleiche zu tun. Weiß jemand, wie man die Gleitfläche nur öffnet, wenn man von der Kante zieht? damit der Viewpager rutscht, wenn er nicht von der Kante zieht. – Zul

Antwort

17

Die Ursache ist die Implementierung von #onInterceptTouchEvent. Eine ältere Implementierung von SlidingPaneLayout hat einen Aufruf an #canScroll gemacht, der prüfen würde, ob das Touch-Ziel scrollen könnte, und wenn dies der Fall wäre, würde er das Touch-Ziel scrollen, anstatt das Panel zu verschieben. Die neueste Implementierung sieht so aus, als ob sie immer das Bewegungsereignis abfängt, sobald der Schleppschwellenwert den Slop überschreitet, außer in dem Fall, in dem der X-Widerstand den Slop überschreitet und der Y-Widerstand den X-Widerstand überschreitet (wie vom OP angegeben).

Eine Lösung hierfür ist, SlidingPaneLayout zu kopieren und ein paar Änderungen vorzunehmen, damit dies funktioniert. Diese Änderungen sind:

  1. die ACTION_MOVE Fall in #onInterceptTouchEvent ändern auch #canScroll zu überprüfen,

    if (adx > slop && ady > adx || 
        canScroll(this, false, Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) 
    { ... } 
    
  2. Ändern Sie die Endkontrolle in #canScroll zu Sonderfall ViewPager. Diese Änderung kann auch in einer Unterklasse durch Überschreiben von #canScroll vorgenommen werden, da sie nicht auf einen privaten Zustand zugreift.

    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { 
        ... 
        /* special case ViewPagers, which don't properly implement the scrolling interface */ 
        return checkV && (ViewCompat.canScrollHorizontally(v, -dx) || 
         ((v instanceof ViewPager) && canViewPagerScrollHorizontally((ViewPager) v, -dx))) 
    } 
    
    boolean canViewPagerScrollHorizontally(ViewPager p, int dx) { 
        return !(dx < 0 && p.getCurrentItem() <= 0 || 
         0 < dx && p.getAdapter().getCount() - 1 <= p.getCurrentItem());  
    } 
    

Es ist wahrscheinlich eine elegantere Art und Weise, dies zu tun, indem Sie die ViewDragHelper Fixierung, aber das ist etwas, Google in einem zukünftigen Update des Support-Paket befassen sollte. Die oben genannten Hacks sollten jetzt das Layout mit ViewPagers (und anderen horizontal scrollenden Containern) funktionieren lassen.

+0

Funktioniert aus der Box! Vielen Dank! –

+0

Sehr gute Antwort! Vielen Dank! – hooloovoo

7

Aufbauend auf @Brien Colwells Lösung, habe ich eine benutzerdefinierte Unterklasse von SlidingPaneLayout geschrieben, die dies für Sie behandelt, und fügt auch Kanten wischen, so dass wenn der Benutzer weit scrollt, müssen sie nicht scrollen den ganzen Weg zurück nach links, um die Scheibe zu öffnen.

Da dies eine Unterklasse von SlidingPaneLayout ist, müssen Sie keine Ihrer Referenzen in Java ändern, stellen Sie nur sicher, dass Sie eine Instanz dieser Klasse instanziieren (normalerweise in Ihrem XML).

package com.ryanharter.android.view; 

import android.content.Context; 
import android.support.v4.view.MotionEventCompat; 
import android.support.v4.widget.SlidingPaneLayout; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.ViewConfiguration; 

/** 
* SlidingPaneLayout that, if closed, checks if children can scroll before it intercepts 
* touch events. This allows it to contain horizontally scrollable children without 
* intercepting all of their touches. 
* 
* To handle cases where the user is scrolled very far to the right, but should still be 
* able to open the pane without the need to scroll all the way back to the start, this 
* view also adds edge touch detection, so it will intercept edge swipes to open the pane. 
*/ 
public class PagerEnabledSlidingPaneLayout extends SlidingPaneLayout { 

    private float mInitialMotionX; 
    private float mInitialMotionY; 
    private float mEdgeSlop; 

    public PagerEnabledSlidingPaneLayout(Context context) { 
     this(context, null); 
    } 

    public PagerEnabledSlidingPaneLayout(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
    } 

    public PagerEnabledSlidingPaneLayout(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 

     ViewConfiguration config = ViewConfiguration.get(context); 
     mEdgeSlop = config.getScaledEdgeSlop(); 
    } 

    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 

     switch (MotionEventCompat.getActionMasked(ev)) { 
      case MotionEvent.ACTION_DOWN: { 
       mInitialMotionX = ev.getX(); 
       mInitialMotionY = ev.getY(); 
       break; 
      } 

      case MotionEvent.ACTION_MOVE: { 
       final float x = ev.getX(); 
       final float y = ev.getY(); 
       // The user should always be able to "close" the pane, so we only check 
       // for child scrollability if the pane is currently closed. 
       if (mInitialMotionX > mEdgeSlop && !isOpen() && canScroll(this, false, 
         Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) { 

        // How do we set super.mIsUnableToDrag = true? 

        // send the parent a cancel event 
        MotionEvent cancelEvent = MotionEvent.obtain(ev); 
        cancelEvent.setAction(MotionEvent.ACTION_CANCEL); 
        return super.onInterceptTouchEvent(cancelEvent); 
       } 
      } 
     } 

     return super.onInterceptTouchEvent(ev); 
    } 
} 
+0

Dieser Code ist erstaunlich! – Gilian