Android的ListView控件 - 停留在“整体”行位置滚动(Android ListView

2019-08-17 11:39发布

很抱歉的混乱冠军,我无法用语言表达的问题很简洁...

我有与使用圆形/“无限”的适配器,这基本上意味着我可以滚动向上或向下,就像我想,当它到达顶部或底部的项目将环绕一个ListView的Android应用程序,使它看起来给用户,如果他是纺纱的项重复无限长的名单(〜100)。

这种设置的关键是让用户选择一个随机的项目,只需通过旋转/丢列表视图和等待,看看它停止。 所以它甩了一下速度更快,时间更长,这似乎工作真的很好我减少列表视图的摩擦。 最后,我放置在部分透明的图像上的ListView来阻挡的顶部和底部的物品(具有从透明到黑色的转变)的顶部,使它看起来好像用户在中间的“选择”的项目,就好像它们是在一个旋转的“轮”,它们通过晃动控制。

有一个明显的问题:后扔ListView控件没有在特定的项目停下来,却阻挡不了两个项目(第一个可见的产品则仅部分示出)之间徘徊。 我想避免这种情况,因为在这种情况下,它并不明显,其项目已被“随机选择”。

长话短说: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;
            }
        }
    });

这工作还算不错,除了一个昭然若揭的问题。第一个可见的项目可能仅仅是为一个或两个像素可见。 在这种情况下,我想,这样选择第二可见项目ListView控件跳“起来”的那两个像素。 取而代之的是,当然,第一个可见项目被选择,这意味着ListView的跳跃“下”几乎一整行(减去这两个像素)。

总之,而不是跳跃到第一个可见项目,我希望它跳转到可见的大部分项目。 如果第一个可见的项目为不到一半可见,我希望它转移到第二可见项目。

下面是希望传达我的观点的说明。 最左边的ListView(每对)显示投掷后的状态已停止(它在哪里停止),而右边的ListView显示了它是如何照顾它提出的“跳跃”选择第一个可见项目。 在左边我显示当前的(错误的)情况:B项只是隐约可见,却还是那么ListView控件跳到选择该项目是第一个可见项目 - 因为它有滚动几乎整个项目高度是不符合逻辑到那里。 这将是更合理的滚动到C项(这是正确的描述),因为这是“终结者”。

图片http://nickthissen.nl/Images/lv.jpg

我怎样才能实现这种行为? 我能想到的唯一的办法就是以某种方式衡量如何第一个可见项目的多少是可见的。 如果是超过50%,那么我跳转到该位置。 如果低于50%,我跳到那个位置+ 1,但是我不知道如何来衡量?

有任何想法吗?

Answer 1:

你可以使用一个孩子的可视尺寸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
}


Answer 2:

请按照以下3个步骤,那么你可以得到你想要什么!!!!

1.Initialize滚动上下两个变量:

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.Then做在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;
                }
            }


Answer 3:

下面是我通过阴影的回答启发最终代码。

我忘了补充"if(Math.abs(r.height())!=height)"在第一。 然后,它只是滚动两次后,滚动到正确的位置,因为它总是比身高/ 2 childView更大。 希望能帮助到你。

listView.setOnScrollListener(new AbsListView.OnScrollListener(){

            @Override
            public void onScrollStateChanged(AbsListView view,int scrollState) {
                if (scrollState == SCROLL_STATE_IDLE){
                    View child = listView.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;
                    listView.getChildVisibleRect (child, r, null);
                    if(Math.abs(r.height())!=height){//only smooth scroll when not scroll to correct position
                        if (Math.abs (r.height ()) < height / 2.0) {
                            listView.smoothScrollToPosition(listView.getLastVisiblePosition());
                        }
                        else if(Math.abs (r.height ()) > height / 2.0){
                            listView.smoothScrollToPosition(listView.getFirstVisiblePosition());
                        }
                        else{
                            listView.smoothScrollToPosition(listView.getFirstVisiblePosition());
                        }

                    }
                }
            }

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

            }});


Answer 4:

一旦你知道第一个可见的位置,你应该能够使用View.getLocationinWindow()View.getLocationOnScreen()上的下一个位置的观点得到了第一的可见高度。 与此相比,该View的高度,并在适当滚动到下一个位置。

您可能需要调整它占填充,这取决于你行的样子。


我没试过以上,但似乎它应该工作。 如果没有,这里的另一个,可能是不太可靠的想法:

getLastVisiblePosition() 如果你拿的区别lastfirst ,你可以看到有多少位置显示在屏幕上。 与此相比,许多职位是如何看到该列表的第一填充(滚动位置0)。

如果相同数量的位置是可见的,只需滚动到第一个可见的位置,你在做什么。 如果有一个更加明显,滚动到“第一+ 1”的位置。



Answer 5:

如果你能得到一个需要滚动,你可以使用的方法行的位置:

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);


Answer 6:

你可能解决了这个问题,但我认为这个解决方案应该工作

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)
        }
    });
}


Answer 7:

我的解决方案:

@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);
    }
}


文章来源: Android ListView - stop scrolling at 'whole' row position