2013-07-05 2 views
9

Я пытаюсь использовать SlidingPaneLayout с ViewPager, как такИспользование SlidingPaneLayout Андроида с 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 скользит, когда я тяну от левого края; однако, я не могу заставить ViewPager скользить, когда я вытягиваю с правого края. Когда я тяну с правого края, он скользит очень мало, а затем откидывается назад.

Выполняет это даже возможно? Есть лучший способ сделать это?

Я обнаружил, что, перемещая мой палец вверх и влево, я могу пронести по экрану пейджер.

+0

Я пытался сделать то же самое. Кто-нибудь знает, как открыть скользящую панель, только когда вытащить ее из края? так что, если не потянуть за край, будет отображаться просмотрщик. – Zul

ответ

17

Основной причиной является реализация #onInterceptTouchEvent. Более старая реализация SlidingPaneLayout вызывала вызов #canScroll, который бы проверял, может ли таргетинг касаться цели, и если это так, будет прокручивать цель касания вместо того, чтобы сдвигать панель. Самая последняя реализация выглядит так, как будто она всегда перехватывает событие движения, как только порог перетаскивания превышает slop, за исключением случая, когда X-перегрузка превышает slop, а Y-перетаскивание превышает сопротивление X (как отмечено OP).

Одно из решений этого - скопировать SlidingPaneLayout и внести несколько изменений, чтобы заставить это работать. Эти изменения являются:

  1. Изменить случай ACTION_MOVE в #onInterceptTouchEvent также проверить #canScroll,

    if (adx > slop && ady > adx || 
        canScroll(this, false, Math.round(x - mInitialMotionX), Math.round(x), Math.round(y))) 
    { ... } 
    
  2. Изменить окончательную проверку в #canScroll на особый случай ViewPager. Эта модификация также может быть выполнена в подклассе путем переопределения #canScroll, поскольку она не имеет доступа к каким-либо частным состояниям.

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

Существует вероятность более элегантный способ сделать это путем фиксации ViewDragHelper, но это что-то Google должен обратиться в будущем обновлении пакета поддержки. Взломы выше должны получить макет, работающий с ViewPagers (и другими горизонтальными прокручивающими контейнерами?) Сейчас.

+0

Работает из коробки! Спасибо! –

+0

Очень хороший ответ! Спасибо! – hooloovoo

7

Построение решения @Brien Colwell, я написал собственный подкласс SlidingPaneLayout, который обрабатывает это для вас, а также добавляет прокрутку края, так что, когда пользователь прокручивается далеко вправо, им не нужно прокручивать все пути назад, чтобы открыть панель.

Поскольку это подкласс SlidingPaneLayout, вам не нужно менять какие-либо ссылки на Java, просто убедитесь, что вы создаете экземпляр этого класса (обычно в вашем 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

Этот код потрясающий! – Gilian