71

次の要件があります。

  • 最初に、ページ番号 2 のデータがサーバーから取得され、項目が L​​istView に取り込まれます。

前のページと次のページの両方がシナリオで使用できることを考慮して、次のコードが追加されました。

 if(prevPageNo > 0){
    mListViewActual.setOnScrollListener(this);
 }

 if(nextPageNo > 0){
    mListViewActual.setOnScrollListener(this);
 }

次の方法でスクロールアップとスクロールダウンを検出するには、どのような条件を設定する必要がありますか。

  1. void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
  2. void onScrollStateChanged(AbsListView view, int scrollState)

action: scroll up & scroll down が検出された後、それに応じてサービスが prev page no または next page no で呼び出され、Listview に入力される項目をフェッチします。

どんな入力も役に立ちます。

次のリンクをたどりましたが、正しいスクロールアップ/スクロールダウンアクションを返しません:

リンク 1 リンク 2

4

17 に答える 17

94

setOnScrollListener を使用してみて、onScrollStateChanged を scrollState で実装してください。

setOnScrollListener(new OnScrollListener(){
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
      // TODO Auto-generated method stub
    }
    public void onScrollStateChanged(AbsListView view, int scrollState) {
      // TODO Auto-generated method stub
      final ListView lw = getListView();

       if(scrollState == 0) 
      Log.i("a", "scrolling stopped...");


        if (view.getId() == lw.getId()) {
        final int currentFirstVisibleItem = lw.getFirstVisiblePosition();
         if (currentFirstVisibleItem > mLastFirstVisibleItem) {
            mIsScrollingUp = false;
            Log.i("a", "scrolling down...");
        } else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
            mIsScrollingUp = true;
            Log.i("a", "scrolling up...");
        }

        mLastFirstVisibleItem = currentFirstVisibleItem;
    } 
    }
  });
于 2013-06-10T05:43:10.033 に答える
47

これは簡単な実装です:

lv.setOnScrollListener(new OnScrollListener() {
        private int mLastFirstVisibleItem;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {

            if(mLastFirstVisibleItem<firstVisibleItem)
            {
                Log.i("SCROLLING DOWN","TRUE");
            }
            if(mLastFirstVisibleItem>firstVisibleItem)
            {
                Log.i("SCROLLING UP","TRUE");
            }
            mLastFirstVisibleItem=firstVisibleItem;

        }
    });

さらに精度が必要な場合は、次のカスタム ListView クラスを使用できます。

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;

/**
 * Created by root on 26/05/15.
 */
public class ScrollInterfacedListView extends ListView {


    private OnScrollListener onScrollListener;
    private OnDetectScrollListener onDetectScrollListener;

    public ScrollInterfacedListView(Context context) {
        super(context);
        onCreate(context, null, null);
    }

    public ScrollInterfacedListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        onCreate(context, attrs, null);
    }

    public ScrollInterfacedListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        onCreate(context, attrs, defStyle);
    }

    @SuppressWarnings("UnusedParameters")
    private void onCreate(Context context, AttributeSet attrs, Integer defStyle) {
        setListeners();
    }

    private void setListeners() {
        super.setOnScrollListener(new OnScrollListener() {

            private int oldTop;
            private int oldFirstVisibleItem;

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if (onScrollListener != null) {
                    onScrollListener.onScrollStateChanged(view, scrollState);
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (onScrollListener != null) {
                    onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
                }

                if (onDetectScrollListener != null) {
                    onDetectedListScroll(view, firstVisibleItem);
                }
            }

            private void onDetectedListScroll(AbsListView absListView, int firstVisibleItem) {
                View view = absListView.getChildAt(0);
                int top = (view == null) ? 0 : view.getTop();

                if (firstVisibleItem == oldFirstVisibleItem) {
                    if (top > oldTop) {
                        onDetectScrollListener.onUpScrolling();
                    } else if (top < oldTop) {
                        onDetectScrollListener.onDownScrolling();
                    }
                } else {
                    if (firstVisibleItem < oldFirstVisibleItem) {
                        onDetectScrollListener.onUpScrolling();
                    } else {
                        onDetectScrollListener.onDownScrolling();
                    }
                }

                oldTop = top;
                oldFirstVisibleItem = firstVisibleItem;
            }
        });
    }

    @Override
    public void setOnScrollListener(OnScrollListener onScrollListener) {
        this.onScrollListener = onScrollListener;
    }

    public void setOnDetectScrollListener(OnDetectScrollListener onDetectScrollListener) {
        this.onDetectScrollListener = onDetectScrollListener;
    }


    public interface OnDetectScrollListener {

        void onUpScrolling();

        void onDownScrolling();
    }

}

使用例: (layout.xml に Xml タグとして追加することを忘れないでください)

scrollInterfacedListView.setOnDetectScrollListener(new ScrollInterfacedListView.OnDetectScrollListener() {
            @Override
            public void onUpScrolling() {
               //Do your thing
            }

            @Override
            public void onDownScrolling() {

             //Do your thing
            }
        });
于 2014-06-03T08:34:21.503 に答える
8

私のソリューションは、スクロール方向ごとに正確な値を与えることで完全に機能します。 distanceFromFirstCellToTop最初のセルから親ビューの上部までの正確な距離が含まれています。この値を保存し、previousDistanceFromFirstCellToTopスクロールしながら新しい値と比較します。低い場合は上にスクロールし、そうでない場合は下にスクロールします。

private int previousDistanceFromFirstCellToTop;

listview.setOnScrollListener(new OnScrollListener() {

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        View firstCell = listview.getChildAt(0);
        int distanceFromFirstCellToTop = listview.getFirstVisiblePosition() * firstCell.getHeight() - firstCell.getTop();

        if(distanceFromFirstCellToTop < previousDistanceFromFirstCellToTop)
        {
           //Scroll Up
        }
        else if(distanceFromFirstCellToTop  > previousDistanceFromFirstCellToTop)
        {
           //Scroll Down
        }
        previousDistanceFromFirstCellToTop = distanceFromFirstCellToTop;
    }
});

Xamarin 開発者向けのソリューションは次のとおりです。

注: UI スレッドで実行することを忘れないでください

listView.Scroll += (o, e) =>
{
    View firstCell = listView.GetChildAt(0);
    int distanceFromFirstCellToTop = listView.FirstVisiblePosition * firstCell.Height - firstCell.Top;

    if (distanceFromFirstCellToTop < previousDistanceFromFirstCellToTop)
    {
        //Scroll Up
    }
    else if (distanceFromFirstCellToTop > previousDistanceFromFirstCellToTop)
    {
        //Scroll Down
    }
    previousDistanceFromFirstCellToTop = distanceFromFirstCellToTop;
};
于 2015-01-13T14:29:39.463 に答える
4

リストビューにスクロールリスナーを設定するだけです。

ヘッダーまたはフッターがある場合は、表示数も確認する必要があります。増加する場合は、下にスクロールしていることを意味します。(ヘッダーの代わりにフッターがある場合は逆にします)

リストビューにヘッダーまたはフッターがない場合は、表示されているアイテム数をチェックする行を削除できます。

listView.setOnScrollListener(new AbsListView.OnScrollListener() {
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (mLastFirstVisibleItem > firstVisibleItem) {
                Log.e(getClass().toString(), "scrolling up");
            } else if (mLastFirstVisibleItem < firstVisibleItem) {
                Log.e(getClass().toString(), "scrolling down");
            } else if (mLastVisibleItemCount < visibleItemCount) {
                Log.e(getClass().toString(), "scrolling down");
            } else if (mLastVisibleItemCount > visibleItemCount) {
                Log.e(getClass().toString(), "scrolling up");
            }
            mLastFirstVisibleItem = firstVisibleItem;
            mLastVisibleItemCount = visibleItemCount;
        }

        public void onScrollStateChanged(AbsListView listView, int scrollState) {
        }
    });

そして、この変数を持っています

int mLastFirstVisibleItem;
int mLastVisibleItemCount;
于 2014-02-19T08:59:36.757 に答える
4

より大きな要素でのスクロールも検出するには、onTouch リスナーを好みます。

listview.setOnTouchListener(new View.OnTouchListener() {

        int scrollEventListSize = 5; 
        float lastY;
        // Used to correct for occasions when user scrolls down(/up) but the onTouchListener detects it incorrectly. We will store detected up-/down-scrolls with -1/1 in this list and evaluate later which occured more often
        List<Integer> downScrolledEventsHappened;

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            float diff = 0;
            if(event.getAction() == event.ACTION_DOWN){
                lastY = event.getY();
                downScrolledEventsHappened = new LinkedList<Integer>();
            }
            else if(event.getAction() == event.ACTION_MOVE){
                diff = event.getY() - lastY;
                lastY = event.getY();

                if(diff>0)
                    downScrolledEventsHappened.add(1);
                else 
                    downScrolledEventsHappened.add(-1);

               //List needs to be filled with some events, will happen very quickly
                if(downScrolledEventsHappened.size() == scrollEventListSize+1){
                    downScrolledEventsHappened.remove(0);
                    int res=0;
                    for(int i=0; i<downScrolledEventsHappened.size(); i++){
                        res+=downScrolledEventsHappened.get(i);
                    }

                    if (res > 0) 
                        Log.i("INFO", "Scrolled up");
                    else 
                        Log.i("INFO", "Scrolled down");
                }
            }
            return false; // don't interrupt the event-chain
        }
    });
于 2015-12-29T06:50:53.440 に答える
3

firstVisibleItem を保存し、次の onScroll で、新しい firstVisibleItem が前のものよりも小さいか大きいかを確認します。

疑似コードの例 (テストされていません):

int lastVisibleItem = 0;
boolean isScrollingDown = false;

void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    if (firstVisibleItem > lastVisibleItem) {
        isScrollingDown = true;
    }
    else {
        isScrollingDown = false;
    }
    lastVisibleItem = firstVisibleItem;
}
于 2013-05-28T12:18:37.103 に答える
1

これらのメソッドは、スクロール方向を直接検出するために使用することはできません。方向を取得するには多くの方法があります。そのようなメソッドの 1 つの単純なコード (テストされていない) を以下に説明します。

public class ScrollTrackingListView extends ListView {

    private boolean readyForMeasurement = false;
    プライベート ブール isScrollable = null;
    private float prevDistanceToEnd = -1.0;
    プライベート ScrollDirectionListener リスナー = null;

    public Sc​​rollTrackingListView(Context context) {
        スーパー(コンテキスト);
        初期化();
    }

    public Sc​​rollTrackingListView(Context context, AttributeSet attrs) {
        スーパー (コンテキスト、属性);
        初期化();
    }

    public Sc​​rollTrackingListView(Context context, AttributeSet attrs, int defStyle) {
        スーパー (コンテキスト、attrs、defStyle);
        初期化();
    }

    プライベートボイドinit() {
        ViewTreeObserver オブザーバー = getViewTreeObserver();
        observer.addOnGlobalLayoutListener(globalLayoutListener);
        setOnScrollListener(scrollListener);
    }

    プライベート ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener
            = new ViewTreeObserver.OnGlobalLayoutListener() {

        @オーバーライド
        public void onGlobalLayout() {
            readyForMeasurement = true;
            calculateDistanceToEnd();
        }

    };

    public void registerScrollDirectionListener(ScrollDirectionListenerリスナー) {
        scrollDirectionListener = リスナー;
    }

    public void unregisterScrollDirectionListener() {
        scrollDirectionListener = null;
    }

    プライベート OnScrollListener scrollListener
            = 新しい OnScrollListener() {

        @オーバーライド
        public void onScrollStateChanged(AbsListView absListView, int i) {
            calculateDistanceToEnd();
        }

        @オーバーライド
        public void onScroll(AbsListView absListView, int i, int i1, int i2) {
            // 何もしない
        }

    };

    プライベートボイドcalculateDistanceToEnd(){

        if (readyForMeasurement) {

            // レイアウトの高さ、水平スクロールバー、および
            // コンテンツとスクロール ダウン オフセット

            // computeVerticalScrollExtent は、スクロールバーのトラック内のつまみの長さを計算するために使用されます。
            // つまみの長さは、ビューの高さとコンテンツの長さの関数です。
            int verticalScrollExtent = computeVerticalScrollExtent();
            int verticalScrollOffset = computeVerticalScrollOffset();
            int verticalScrollRange = computeVerticalScrollRange();
            int horizo​​ntalScrollBarHeight = getHorizo​​ntalScrollbarHeight();

            /**
             ※1.縦スクロールバーの範囲を「R」とする。これは、コンテンツの長さに対応します。
             *ビューで。
             * 2. 垂直スクロールバーの範囲を「E」とします。エクステントは定数値であり、
             * (おそらく) ビューの高さに比例する値に等しい。
             * 3. オフセット「o」は、ユーザーに表示される範囲内の現在の位置を表します。かかります
             * 「0 から E」までの値。
             *
             * ここで、DistanceToEnd は、次のようにこれら 3 つの値を使用して計算されます。
             *
             * 終点までの距離 = (R - o) / E
             *
             * DistanceToEnd は NumberOfScreenToEnd 単位で値を保持します。
             *
             */

            フロート距離ToEnd =
                    ((float)(verticalScrollRange - verticalScrollOffset))/((float)(verticalScrollExtent));

            if(prevDistanceToEnd == -1) {
                 前までの距離 = 終了までの距離;
            } そうしないと {
                 if (リスナー != null) {
                     if(終了までの距離 > 終了までの距離) {
                        // ユーザーは上にスクロールしています
                         listener.onScrollingUp();
                     } そうしないと {
                        // ユーザーは上にスクロールしています
                         listener.onScrollingDown();
                    }
                 }
                 前までの距離 = 終了までの距離;
            }

            if(isScrollable == null) {
                // ビューの高さが画面よりも小さいかどうかを確認します (つまり、スクロールが有効になっていません)
                if((horizo​​ntalScrollBarHeight + verticalScrollExtent) >= verticalScrollRange) {
                    isScrollable = Boolean.FALSE;
                } そうしないと {
                    isScrollable = Boolean.TRUE;
                }
            }

        }

    }

    パブリック インターフェイス ScrollDirectionListener {

        public void onScrollingUp();

        public void onScrollingDown();

    }

}

アイデアは、distanceToEnd を計算することです。distanceToEnd が増加するとユーザーは上にスクロールし、減少するとユーザーは下にスクロールします。これにより、リストの最後までの正確な距離もわかります。

ユーザーが上下にスクロールしているかどうかを知りたいだけの場合は、onInterceptTouchEventをオーバーライドして、以下のようにスクロール方向を検出できます。

    @オーバーライド
    public boolean onInterceptTouchEvent(MotionEvent イベント) {
        スイッチ (event.getAction()) {
            ケース MotionEvent.ACTION_DOWN:
                mInitialX = event.getX();
                mInitialY = event.getY();
                true を返します。
            ケース MotionEvent.ACTION_MOVE:
                最終的な float x = event.getX();
                最終フロート y = event.getY();
                最終フロート yDiff = y - mInitialY; // yDiff が 0.0 より小さい場合は下にスクロールすることを意味し、yDiff が 0.0 より大きい場合は上にスクロールすることを意味します。小なり記号または大なり記号を追加しようとすると、プレビューで表示されません。
                if(yDiff 0.0 未満) listener.onScrollingDown();
                else if(yDiff が 0.0 より大きい) listener.onScrollingUp();
                壊す;
        }
        super.onInterceptTouchEvent(イベント)を返します。
    }

于 2013-06-10T02:34:52.950 に答える
1

リストビューでスクロールアップまたはダウンを検出するためのトリックは、ListView の OnScrollListener の onScroll 関数でこの関数を呼び出すだけです。

private int oldFirstVisibleItem = -1;
    private protected int oldTop = -1;
    // you can change this value (pixel)
    private static final int MAX_SCROLL_DIFF = 5;

    private void calculateListScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (firstVisibleItem == oldFirstVisibleItem) {
            int top = view.getChildAt(0).getTop();
            // range between new top and old top must greater than MAX_SCROLL_DIFF
            if (top > oldTop && Math.abs(top - oldTop) > MAX_SCROLL_DIFF) {
                // scroll up
            } else if (top < oldTop && Math.abs(top - oldTop) > MAX_SCROLL_DIFF) {
                // scroll down
            }
            oldTop = top;
        } else {
            View child = view.getChildAt(0);
            if (child != null) {
                oldFirstVisibleItem = firstVisibleItem;
                oldTop = child.getTop();
            }
        }
    }
于 2013-11-20T08:43:04.480 に答える
1

何らかの理由で、Android のドキュメントはこれをカバーしておらず、使用されている方法はドキュメントにもありません...見つけるのに時間がかかりました。

スクロールが一番上にあるかどうかを検出するには、これを使用します。

public boolean checkAtTop() 
{
    if(listView.getChildCount() == 0) return true;
    return listView.getChildAt(0).getTop() == 0;
}

これにより、スクローラーが一番上にあるかどうかがチェックされます。ここで、一番下に対してそれを行うには、自分が持っている子の数を渡して、その数と照合する必要があります。一度に画面に表示される人数を計算し、それを子供の数から差し引く必要がある場合があります。私はそれをする必要はありませんでした。お役に立てれば

于 2013-06-10T01:08:16.440 に答える
0

ListView のセル サイズが大きいいくつかの例を使用して問題が発生しました。だから私はあなたの指のわずかな動きを検出する私の問題に対する解決策を見つけました。可能な限り単純化して、次のようにしました。

private int oldScrolly;


@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {

}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int    visibleItemCount, int totalItemCount) {

            View view = absListView.getChildAt(0);
            int scrolly = (view == null) ? 0 : -view.getTop() + absListView.getFirstVisiblePosition() * view.getHeight();
            int margin = 10;

            Log.e(TAG, "Scroll y: " + scrolly + " - Item: " + firstVisibleItem);


            if (scrolly > oldScrolly + margin) {
                Log.d(TAG, "SCROLL_UP");
                oldScrolly = scrolly;
            } else if (scrolly < oldScrolly - margin) {
                Log.d(TAG, "SCROLL_DOWN");
                oldScrolly = scrolly;
            }
        }
    });

PD: MARGIN を使用して、そのマージンを満たすまでスクロールを検出しません。これにより、ビューを表示または非表示にするときに問題が回避され、ビューの点滅が回避されます。

于 2015-05-12T16:06:24.397 に答える