SwipeRefreshLayout + ViewPager, ограничьте только горизонтальную прокрутку?


я реализовал SwipeRefreshLayout и ViewPager в моем приложении, но есть большая проблема: всякий раз, когда я собираюсь провести влево / вправо, чтобы переключаться между страницами прокрутка слишком чувствительна. Немного проведите пальцем вниз вызовет SwipeRefreshLayout обновить тоже.

Я хочу установить ограничение на то, когда начинается горизонтальный свайп, а затем принудительно горизонтально только до тех пор, пока свайп не закончится. Другими словами, Я хочу отменить вертикальное переключение, когда палец движется горизонтально.

эта проблема возникает только на ViewPager, Если я проведите пальцем вниз и SwipeRefreshLayout функция обновления запускается (отображается панель), а затем я перемещаю палец по горизонтали, он по-прежнему позволяет только вертикальные пойло.

Я пытался расширить ViewPager класс, но он не работает вообще:

public class CustomViewPager extends ViewPager {

    public CustomViewPager(Context ctx, AttributeSet attrs) {
        super(ctx, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean in = super.onInterceptTouchEvent(ev);
        if (in) {
            getParent().requestDisallowInterceptTouchEvent(true);
            this.requestDisallowInterceptTouchEvent(true);
        }
        return false;
    }

}

макет xml:

<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/viewTopic"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.myapp.listloader.foundation.CustomViewPager
        android:id="@+id/topicViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>

любая помощь будет оценили, спасибо

6 77

6 ответов:

Я не уверен, если у вас все еще есть эта проблема, но Google I / O приложение iosched решает эту проблему таким образом:

    viewPager.addOnPageChangeListener( new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled( int position, float v, int i1 ) {
        }

        @Override
        public void onPageSelected( int position ) {
        }

        @Override
        public void onPageScrollStateChanged( int state ) {
            enableDisableSwipeRefresh( state == ViewPager.SCROLL_STATE_IDLE );
        }
    } );


private void enableDisableSwipeRefresh(boolean enable) {
    if (swipeContainer != null) {
            swipeContainer.setEnabled(enable);
    }
}

я использовал то же самое и работает довольно хорошо.

EDIT: используйте addOnPageChangeListener () вместо setOnPageChangeListener ().

решается очень просто, ничего не расширяя

mPager.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        mLayout.setEnabled(false);
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                mLayout.setEnabled(true);
                break;
        }
        return false;
    }
});

работает

Я встретил вашу проблему. Настройка SwipeRefreshLayout решит проблему.

public class CustomSwipeToRefresh extends SwipeRefreshLayout {

private int mTouchSlop;
private float mPrevX;

public CustomSwipeToRefresh(Context context, AttributeSet attrs) {
    super(context, attrs);

    mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mPrevX = MotionEvent.obtain(event).getX();
            break;

        case MotionEvent.ACTION_MOVE:
            final float eventX = event.getX();
            float xDiff = Math.abs(eventX - mPrevX);

            if (xDiff > mTouchSlop) {
                return false;
            }
    }

    return super.onInterceptTouchEvent(event);
}

посмотреть код: ссылке

Я основал это на предыдущем ответе, но нашел, что это работает немного лучше. Движение начинается с события ACTION_MOVE и заканчивается либо ACTION_UP, либо ACTION_CANCEL в моем опыте.

mViewPager.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                mSwipeRefreshLayout.setEnabled(false);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mSwipeRefreshLayout.setEnabled(true);
                break;
        }
        return false;
    }
});

по какой-то причине, известной только им, команда разработчиков библиотеки поддержки сочла нужным силой перехватывать все события вертикального перетаскивания из SwipeRefreshLayoutдочерний макет, даже если ребенок специально запрашивает право собственности на событие. Единственное, что они проверяют, это то, что состояние вертикальной прокрутки его основного дочернего элемента равно нулю (в случае, если его дочерний элемент вертикально прокручивается). Элемент requestDisallowInterceptTouchEvent() метод был переопределен с пустым телом, и (не так) освещения комментарий "Нет".

самый простой способ решить эту проблему было бы просто скопировать класс из библиотеки в свой проект и удалить переопределение метода. ViewGroupреализация использует внутреннее состояние для обработки onInterceptTouchEvent(), поэтому вы не можете просто переопределить метод и дублируем его. Если вы действительно хотите переопределить реализация библиотеки поддержки, то вам придется настроить пользовательский флаг при вызовах requestDisallowInterceptTouchEvent() и переопределить onInterceptTouchEvent() и onTouchEvent() (или, возможно, взломать canChildScrollUp()) поведение, основанное на этом.

есть одна проблема с решением nhasan:

если горизонтальный свайп, который вызывает setEnabled(false) вызов на SwipeRefreshLayout на OnPageChangeListener происходит, когда SwipeRefreshLayout уже распознал Pull-to-Reload, но еще не вызвал обратный вызов уведомления, анимация исчезает, но внутреннее состояние SwipeRefreshLayout остается на "обновление" навсегда, так как не вызываются обратные вызовы уведомлений, которые могут сбросить состояние. С точки зрения пользователя это означает, что Pull-to-Reload больше не работает, так как все жесты вытягивания не распознаются.

проблема здесь в том, что disable(false) вызов удаляет анимацию блесны и уведомление обратного вызова вызывается из onAnimationEnd метод внутреннего AnimationListener для того прядильщика, который установлен не в порядке таким образом.

по общему признанию, наш тестер с самыми быстрыми пальцами, чтобы спровоцировать эту ситуацию, но это может произойти время от времени в реалистичных сценариях, как что ж.

решение, чтобы исправить это, чтобы переопределить onInterceptTouchEvent метод SwipeRefreshLayout следующим образом:

public class MySwipeRefreshLayout extends SwipeRefreshLayout {

    private boolean paused;

    public MySwipeRefreshLayout(Context context) {
        super(context);
        setColorScheme();
    }

    public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setColorScheme();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (paused) {
            return false;
        } else {
            return super.onInterceptTouchEvent(ev);
        }
    }

    public void setPaused(boolean paused) {
        this.paused = paused;
    }
}

использовать MySwipeRefreshLayout в вашем макете-файле и измените код в решении mhasan на

...

@Override
public void onPageScrollStateChanged(int state) {
    swipeRefreshLayout.setPaused(state != ViewPager.SCROLL_STATE_IDLE);
}

...