10

紛らわしいタイトルで申し訳ありませんが、問題を非常に簡潔に表現することはできません...

循環/「無限」アダプターを使用するListViewを備えたAndroidアプリがあります。これは、基本的に、上下にスクロールできることを意味し、アイテムが上または下に達すると折り返され、ユーザーには、無限に長いリスト (~100) の繰り返しアイテムを回転させているかのように表示されます。

このセットアップのポイントは、リストビューをスピン/フリングして、どこで停止するかを待つだけで、ユーザーがランダムなアイテムを選択できるようにすることです。リストビューのフリクションを減らして、少し速く長く飛ばすようにしました。これは非常にうまく機能しているようです。最後に、ListView の上に部分的に透明な画像を配置して、上部と下部のアイテムをブロックし (透明から黒への移行)、ユーザーが真ん中のアイテムを「選択」しているように見せます。彼らは飛び回ることによって制御する回転する「ホイール」に乗っていました。

明らかな問題が 1 つあります。ListView をフリングした後、特定の項目で停止せず、2 つの項目の間でホバリングを停止する可能性があります (最初に表示された項目が部分的にしか表示されない場合)。その場合、どのアイテムが「ランダムに選択された」かが明らかではないため、これを避けたいと思います。

簡単に言えば、ListView がフリング後にスクロールを終了した後、部分的に表示されている行ではなく、「全体」の行で停止したいのです。

現在、スクロールがいつ停止したかを確認し、最初に表示されているアイテムを選択することで、この動作を実装しました。

    lv = this.getListView();
    
    lv.setFriction(0.005f);
    lv.setOnScrollListener(new OnScrollListener() {
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}

        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) 
            {
                if (isAutoScrolling) return;
                
                isAutoScrolling = true;
                int pos = lv.getFirstVisiblePosition();
                lv.setSelection(pos);
                isAutoScrolling = false;
            }
        }
    });

これは、1 つの明白な問題を除けば、かなりうまく機能します... 最初に表示されるアイテムは、1 ピクセルまたは 2 ピクセルしか表示されない場合があります。その場合、 2 番目の表示項目が選択されるように、これらの 2 つのピクセルに対して ListView を「上に」ジャンプさせます。代わりに、もちろん、最初に表示されている項目が選択されます。つまり、ListView はほぼ行全体 (これらの 2 ピクセルを差し引いたもの) を "下に" ジャンプします。

つまり、最初に表示されているアイテムにジャンプするのではなく、最も表示されているアイテムにジャンプしたいのです。最初に表示されているアイテムが半分未満しか表示されていない場合は、2 番目に表示されているアイテムにジャンプしたいと考えています。

これが私の要点をうまく伝えている図です。(各ペアの) 左端の ListView は、フリングが停止した後の状態 (停止する場所) を示し、右側の ListView は、最初の表示項目を選択して「ジャンプ」した後の状態を示します。左側に現在の (間違った) 状況を示します。アイテム B はほとんど見えませんが、まだ最初に表示されているアイテムであるため、listView がジャンプしてそのアイテムを選択します。これは、アイテムの高さのほぼ全体をスクロールする必要があるため、論理的ではありません。そこに着くために。アイテム C (右側に示されている) にスクロールする方がはるかに論理的です。

画像
(出典: nickthissen.nl )

どうすればこの動作を実現できますか? 私が考えることができる唯一の方法は、最初に表示されているアイテムがどれだけ表示されているかを何らかの方法で測定することです。それが 50% を超える場合は、その位置にジャンプします。50% 未満の場合は、その位置 + 1 にジャンプします。ただし、それを測定する方法がわかりません...

何か案は?

4

7 に答える 7

6

getChildVisibleRectメソッドを使用して、子の表示サイズを取得できます。それがあり、子の合計の高さを取得したら、適切な子までスクロールできます。

以下の例では、子の少なくとも半分が表示されているかどうかを確認します。

View child = lv.getChildAt (0);    // first visible child
Rect r = new Rect (0, 0, child.getWidth(), child.getHeight());     // set this initially, as required by the docs
double height = child.getHeight () * 1.0;

lv.getChildVisibleRect (child, r, null);

if (Math.abs (r.height ()) < height / 2.0) {
    // show next child
}

else {
    // show this child
}
于 2013-03-11T13:00:24.323 に答える
3

これらの 3 つの手順に従ってください。

1.上下にスクロールするための 2 つの変数を初期化します。

int scrollingUp=0,scrollingDown=0;

2.次に、スクロールに基づいて変数の値を増やします。

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

        if(mLastFirstVisibleItem<firstVisibleItem)
                {
                    scrollingDown=1;
                }
                if(mLastFirstVisibleItem>firstVisibleItem)
                {
                    scrollingUp=1;
                }
                mLastFirstVisibleItem=firstVisibleItem;
    } 

3.次に、onScrollStateChanged() で変更を行います。

@Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            switch (scrollState) {
            case SCROLL_STATE_IDLE:

                if(scrollingUp==1)
                {
                    mainListView.post(new Runnable() {
                        public void run() {
                            View child = mainListView.getChildAt (0);    // first visible child
                            Rect r = new Rect (0, 0, child.getWidth(), child.getHeight());     // set this initially, as required by the docs
                            double height = child.getHeight () * 1.0;
                            mainListView.getChildVisibleRect (child, r, null);
                            int dpDistance=Math.abs (r.height());
                            double minusDistance=dpDistance-height;
                            if (Math.abs (r.height()) < height/2)
                            {
                                mainListView.smoothScrollBy(dpDistance, 1500);
                            }       
                            else
                            {
                                mainListView.smoothScrollBy((int)minusDistance, 1500);
                            }
                            scrollingUp=0;

                        }
                    });
                }
                if(scrollingDown==1)
                {
                    mainListView.post(new Runnable() {
                        public void run() {

                            View child = mainListView.getChildAt (0);    // first visible child
                            Rect r = new Rect (0, 0, child.getWidth(), child.getHeight());     // set this initially, as required by the docs
                            double height = child.getHeight () * 1.0;
                            mainListView.getChildVisibleRect (child, r, null);
                            int dpDistance=Math.abs (r.height());
                            double minusDistance=dpDistance-height;
                            if (Math.abs (r.height()) < height/2)
                            {
                                mainListView.smoothScrollBy(dpDistance, 1500);
                            }       
                            else
                            {
                                mainListView.smoothScrollBy((int)minusDistance, 1500);
                            }
                            scrollingDown=0;
                        } 
                    });
                }
            break;
            case SCROLL_STATE_TOUCH_SCROLL:

                break;
                }
            }
于 2014-11-12T07:24:22.023 に答える
1

あなたはおそらくこの問題を解決しましたが、私はこの解決策がうまくいくと思います

if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
    View firstChild = lv.getChildAt(0);
    int pos = lv.getFirstVisiblePosition();

    //if first visible item is higher than the half of its height
    if (-firstChild.getTop() > firstChild.getHeight()/2) {
        pos++;
    }

    lv.setSelection(pos);
}

getTop()Math.abs(firstChild.getTop())最初の項目ビューでは常に非正の値が返されるため、使用しません-firstChild.getTop()。この値が >0 になる場合でも、この状態は引き続き機能します。

これをよりスムーズにしたい場合はlv.smoothScrollToPosition(pos)、上記のコードをすべて使用して囲むことができます

if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
    post(new Runnable() {
        @Override
        public void run() {
            //put above code here
            //and change lv.setSelection(pos) to lv.smoothScrollToPosition(pos)
        }
    });
}
于 2013-05-24T12:21:01.540 に答える
0

私の解決策:

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

{

    if (swipeLayout.isRefreshing()) {
        swipeLayout.setRefreshing(false);
    } else {
        int pos = firstVisibleItem;
        if (pos == 0 && lv_post_list.getAdapter().getCount()>0) {

            int topOfNext = lv_post_list.getChildAt(pos + 1).getTop();
            int heightOfFirst = lv_post_list.getChildAt(pos).getHeight();
            if (topOfNext > heightOfFirst) {
                swipeLayout.setEnabled(true);
            } else {
                swipeLayout.setEnabled(false);
            }
        }
        else
            swipeLayout.setEnabled(false);
    }
}
于 2015-10-04T13:23:21.350 に答える
0

最初に表示される位置がわかったら、次の位置のビューでView.getLocationinWindow()またはを使用して、最初の の表示される高さを取得できるはずです。それを の高さと比較し、必要に応じて次の位置までスクロールします。View.getLocationOnScreen()View

行がどのように見えるかに応じて、パディングを考慮して微調整する必要がある場合があります。


私は上記を試していませんが、うまくいくようです。そうでない場合は、おそらくそれほど堅牢ではない別のアイデアを次に示します。

getLastVisiblePosition(). と の差を取るlastfirst、画面上にいくつの位置が表示されているかがわかります。リストが最初に入力されたとき (スクロール位置 0) に表示されていた位置の数と比較してください。

同じ数の位置が表示されている場合は、最初に表示されている位置までスクロールします。もう 1 つ表示されている場合は、「最初の + 1」の位置までスクロールします。

于 2013-03-11T13:00:32.980 に答える
0

スクロールする必要がある行の位置を取得できる場合は、次のメソッドを使用できます。

SmoothScrollToPosition

次のようなものです:

int pos = lv.getFirstVisiblePosition();
lv.smoothScrollToPosition(pos);

編集
これを試してください。申し訳ありませんが、テストする時間がありません。外出中です。

ImageView iv = //Code to find the image view
Rect rect = new Rect(iv.getLeft(), iv.getTop(), iv.getRight(), iv.getBottom());
lv.requestChildRectangleOnScreen(lv, rect, false);
于 2013-03-11T12:40:04.100 に答える