2013-07-05 16 views
9

Próbuję użyć SlidingPaneLayout z ViewPager, tak jakKorzystanie Androida SlidingPaneLayout z 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> 

SlidingPaneLayout ślizga kiedy ciągnąć od lewej krawędzi; jednak nie mogę sprawić, że ViewPager będzie się ślizgał, gdy wyciągnę z prawej krawędzi. Kiedy wyciągam z prawej krawędzi, przesuwa się bardzo niewiele, a następnie odskakuje.

Czy to możliwe? Czy jest lepszy sposób to zrobić?

Znalazłem, że przesuwając palec w górę iw lewo, mogę przesunąć widok pager.

+0

Próbowałem zrobić to samo. Czy ktokolwiek wie, jak otworzyć przesuwalną szynę tylko po oderwaniu od krawędzi? więc jeśli nie będzie ciągnąć od krawędzi, widok będzie się przesuwał. – Zul

Odpowiedz

17

Podstawową przyczyną jest implementacja #onInterceptTouchEvent. Starsza implementacja SlidingPaneLayout nawiązywała połączenie z #canScroll, które sprawdza, czy cel może być przewijany, a jeśli tak, przewijałby cel dotykowy zamiast przesuwać panel. Najnowsza implementacja wygląda tak, jakby zawsze przechwytywała zdarzenie ruchu, gdy próg przeciągania przekroczy nachylenie, z wyjątkiem przypadku, gdy przeciągnięcie X przekroczy błąd, a przeciągnięcie Y przekroczy wartość X (jak odnotowano w OP).

Jednym z rozwiązań jest skopiowanie SlidingPaneLayout i wprowadzenie kilku zmian, aby to zadziałało. Zmiany te są następujące:

  1. Modyfikacja sprawę ACTION_MOVE w #onInterceptTouchEvent również sprawdzić #canScroll,

    if (adx > slop && ady > adx || 
        canScroll(this, false, Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) 
    { ... } 
    
  2. zmodyfikować ostateczną kontrolę w #canScroll na szczególny przypadek ViewPager. Tę modyfikację można również wykonać w podklasie przez przesłonięcie #canScroll, ponieważ nie ma ona dostępu do żadnego prywatnego stanu.

    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());  
    } 
    

Jest prawdopodobne, bardziej elegancki sposób to zrobić poprzez ustalenie ViewDragHelper, ale to jest coś, Google powinno zająć w przyszłej aktualizacji pakietu wsparcia. Powyższe hacki powinny teraz uzyskać układ pracy z ViewPager (i innymi kontenerami przewijającymi się poziomo?).

+0

Działa po wyjęciu z pudełka! Dziękuję Ci! –

+0

Bardzo dobra odpowiedź! Dziękuję Ci! – hooloovoo

7

Zbudowanie rozwiązania @Brien Colwella, napisałem niestandardową podklasę SlidingPaneLayout, która obsługuje to za ciebie, a także dodaje przesuwanie krawędzi, tak, że gdy użytkownik przewinie się daleko w prawo, nie muszą przewijać całą drogę z powrotem w lewo, aby otworzyć szybę.

Ponieważ jest to podklasa SlidingPaneLayout, nie trzeba zmieniać żadnych odniesień w Javie, wystarczy upewnić się, że tworzysz instancję tej klasy (zazwyczaj w swoim 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

Ten kod jest niesamowity! – Gilian

Powiązane problemy