98

アプリに と を実装SwipeRefreshLayoutViewPagerましたが、大きな問題があります。左/右にスワイプしてページを切り替えるときはいつでも、スクロールが敏感すぎます。少し下にスワイプすると、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>

助けていただければ幸いです、ありがとう

4

9 に答える 9

164

この問題がまだあるかどうかはわかりませんが、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);
    }
}

私は同じものを使用しており、非常にうまく機能しています。

編集: setOnPageChangeListener() の代わりに addOnPageChangeListener() を使用します。

于 2015-04-29T14:30:52.133 に答える
12

これは以前の回答に基づいていましたが、これは少しうまく機能することがわかりました。私の経験では、モーションは 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;
    }
});
于 2015-01-14T02:02:10.407 に答える
9

何らかの理由で、サポート ライブラリの開発者チームは、子がイベントの所有権を明示的に要求した場合でも、 の子レイアウトからすべての垂直方向のドラッグ モーション イベントを強制的に傍受することが適切であると判断しました。SwipeRefreshLayout彼らがチェックする唯一のことは、メインの子の垂直スクロール状態がゼロであることです(子が垂直スクロール可能な場合)。メソッドは空のrequestDisallowInterceptTouchEvent()本体と (そうではない) 照明コメント「いいえ」でオーバーライドされています。

この問題を解決する最も簡単な方法は、クラスをサポート ライブラリからプロジェクトにコピーし、メソッドのオーバーライドを削除することです。ViewGroupの実装は の処理に内部状態を使用するonInterceptTouchEvent()ため、単純にメソッドを再度オーバーライドして複製することはできません。本当にサポート ライブラリの実装をオーバーライドしたい場合は、 の呼び出し時にカスタム フラグを設定し、それに基づいて動作をrequestDisallowInterceptTouchEvent()オーバーライドonInterceptTouchEvent()およびonTouchEvent()(場合によってはハック) する必要があります。canChildScrollUp()

于 2014-09-22T21:28:48.280 に答える
3

nhasan の解決には 1 つの問題があります。

がすでに Pull-to-Reload を認識しているが、まだ通知コールバックを呼び出していないときに、 で呼び出しをトリガーする水平方向のスワイプが発生した場合、setEnabled(false)アニメーションは消えますが、の内部状態は永久に「更新中」のままになります。状態をリセットできる通知コールバックが呼び出されます。ユーザーの観点からは、これは、すべてのプル ジェスチャが認識されないため、Pull-to-Reload が機能しなくなったことを意味します。SwipeRefreshLayoutOnPageChangeListenerSwipeRefreshLayoutSwipeRefreshLayout

ここでの問題は、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);
}

...
于 2017-03-30T14:27:35.117 に答える