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

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

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

эта проблема возникает только on 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 ответов


Я не уверен, что у вас все еще есть эта проблема, но Google I / O app 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);
}

см. ref:ссылке


Я основал это на предыдущем ответе, но обнаружил, что это работает немного лучше. Движение начинается с события 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дочерний макет, даже если ребенок специально запрашивает право собственности на событие. Единственное, что они проверяют, это то, что состояние вертикальной прокрутки его основного дочернего элемента равно нулю (в случае, если это дочерний элемент вертикально прокручивается). The requestDisallowInterceptTouchEvent() метод был переопределен пустым телом, а (не так) подсветка комментарий "Неа".

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


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

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

проблема здесь в том, что 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);
}

...