List view snap to item

后端 未结 6 582
不思量自难忘°
不思量自难忘° 2020-12-02 17:45

I\'m creating a list of pictures using a ListView and the photos are of a size that would fit 2 to 3 photos on the screen.

The problem that I\'m having is that I wou

相关标签:
6条回答
  • 2020-12-02 18:08

    Apart from trying the code above one thing you should make sure of is that your listView have a height that can fit exact number of items you want to be displayed. e.g If you want 4 items to be displayed after snap effect and your row height (defined in its layout) should be 1/4 of the total height of the list.

    0 讨论(0)
  • 2020-12-02 18:09

    I've found a way to do this just listening to scroll and change the position when the scroll ended by implementing ListView.OnScrollListener

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
        case OnScrollListener.SCROLL_STATE_IDLE:
            if (scrolling){
                // get first visible item
                View itemView = view.getChildAt(0);
                int top = Math.abs(itemView.getTop()); // top is a negative value
                int bottom = Math.abs(itemView.getBottom());
                if (top >= bottom){
                    ((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition()+1, 0);
                } else {
                    ((ListView)view).setSelectionFromTop(view.getFirstVisiblePosition(), 0);
                }
            }
            scrolling = false;
            break;
        case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
        case OnScrollListener.SCROLL_STATE_FLING:
            Log.i("TEST", "SCROLLING");
            scrolling = true;
            break;
        }
    }
    

    The change is not so smooth but it works.

    0 讨论(0)
  • 2020-12-02 18:17

    Utilizing a couple ideas from @nininho's solution, I got my listview to snap to the item with a smooth scroll instead of abruptly going to it. One caveat is that I've only tested this solution on a Moto X in a basic ListView with text, but it works very well on the device. Nevertheless, I'm confident about this solution, and encourage you to provide feedback.

    listview.setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // TODO Auto-generated method stub
                if (scrollState == SCROLL_STATE_IDLE) {
                    View itemView = view.getChildAt(0);
                    int top = Math.abs(itemView.getTop());
                    int bottom = Math.abs(itemView.getBottom());
                    int scrollBy = top >= bottom ? bottom : -top;
                    if (scrollBy == 0) {
                        return;
                    }
                    smoothScrollDeferred(scrollBy, (ListView)view);
                }
            }
    
            private void smoothScrollDeferred(final int scrollByF,
                    final ListView viewF) {
                final Handler h = new Handler();
                h.post(new Runnable() {
    
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        viewF.smoothScrollBy(scrollByF, 200);
                    }
                });
            }
    
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {
                // TODO Auto-generated method stub
    
            }
        });
    

    The reason I defer the smooth scrolling is because in my testing, directly calling the smoothScrollBy method in the state changed callback had problems actually scrolling. Also, I don't foresee a fully-tested, robust solution holding very much state, and in my solution below, I hold no state at all. This solution is not yet in the Google Play Store, but should serve as a good starting point.

    0 讨论(0)
  • 2020-12-02 18:18

    Note that after the smoothScrollBy() call, getFirstVisiblePosition() may point to the list item ABOVE the topmost one in the listview. This is especially true when view.getChildAt(0).getBottom() == 0. I had to call view.setSelection(view.getFirstVisiblePosition() + 1) to remedy this odd behavior.

    0 讨论(0)
  • 2020-12-02 18:26

    Using @nininho 's solution,

    In the onScrollStateChanged when the state changes to SCROLL_STATE_IDLE, remember the position to snap and raise a flag:

    snapTo = view.getFirstVisiblePosition();
    shouldSnap = true;
    

    Then, override the computeScroll() method:

    @Override
    public void computeScroll() {
        super.computeScroll();
        if(shouldSnap){
            this.smoothScrollToPositionFromTop(snapTo, 0);
            shouldSnap = false;
        }
    }
    
    0 讨论(0)
  • 2020-12-02 18:33

    You can do a much more smooth scrolling if you use RecyclerView. The OnScrollListener is way better.

    I have made an example here: https://github.com/plattysoft/SnappingList

    0 讨论(0)
提交回复
热议问题