简体   繁体   English

如何制作GridView快照滚动

[英]How to make a GridView snap scroll

Is there a way to make a GridView snap to a row while scrolling? 有没有一种方法可以使GridView在滚动时对齐到一行?

It seems that applying the known fixes for a ListView does not suffice for a GridView . 似乎为ListView应用已知的修复程序不足以为GridView足够的支持。

Note: This code was tested for API 23 注意:此代码已针对API 23进行了测试

Note: listPaddingTop == 11 , and listPaddingBottom == 10 for my GridView . 注: listPaddingTop == 11 ,和listPaddingBottom == 10为我GridView Couldn't find where this is set, may be default (didn't see it in the style referenced by Android.Resource.Attribute.GridViewStyle in the below constructor chain (which points to Widget.GridView / Widget.AbsListView in res/values/styles.xml ). Need to check source. 找不到设置的位置,可能是默认设置(在下面的构造函数链中未以Android.Resource.Attribute.GridViewStyle引用的样式看到它(在res / values中指向Widget.GridView / Widget.AbsListView /styles.xml )。需要检查源代码。

Note: Calling SetPadding(0,0,0,0) in constructor has no effect on listPaddingTop (Bottom). 注意:在构造函数中调用SetPadding(0,0,0,0)对listPaddingTop(底部)无效。

public class SnapScrollGridView : GridView,
    //View.IOnScrollChangeListener
    AbsListView.IOnScrollListener
{        
    private bool _bSnapScroll;

    public new int FirstVisiblePosition { get; set; }

    // ctor chain
    public SnapScrollGridView(Context context) : this(context, null) { }
    public SnapScrollGridView(Context context, IAttributeSet attrs) : this(context, attrs, Android.Resource.Attribute.GridViewStyle) { }
    public SnapScrollGridView(Context context, IAttributeSet attrs, int defaultStyleAttr) : this(context, attrs, defaultStyleAttr,0) { }
    public SnapScrollGridView(Context context, IAttributeSet attrs, int defaultStyleAttr, int defStyleRes) : base(context, attrs, defaultStyleAttr, defStyleRes)
    {
        _bSnapScroll = false;
        FirstVisiblePosition = 0;
        //SetOnScrollChangeListener(this);
        SetOnScrollListener(this);
        // no effect on listPaddingTop (or bottom)
        // SetPadding(0, 0, 0, 0);
    }

    void AbsListView.IOnScrollListener.OnScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
    {
        // firstVisibleItem and visibleItemCount not accurate
    }

    protected override void OnOverScrolled(int scrollX, int scrollY, bool clampedX, bool clampedY)
    {            
        // - would be very useful, however is called after OnScrollStateChanged()
        // - may be able to refactor OnScrollStateChanged to work in conjuction with this
        base.OnOverScrolled(scrollX, scrollY, clampedX, clampedY);
    }

    void AbsListView.IOnScrollListener.OnScrollStateChanged(AbsListView view, [GeneratedEnum] ScrollState scrollState)
    {
        switch (scrollState)
        {
            case ScrollState.TouchScroll:
            case ScrollState.Fling:
                break;           

            case ScrollState.Idle:

                if (_bSnapScroll)
                {
                    _bSnapScroll = false;
                }
                else
                {
                    View item = view.GetChildAt(0);
                    int top = System.Math.Abs(item.Top);
                    int btm = System.Math.Abs(item.Bottom);

                    // OVERSCROLL TOP ("topping out")
                    // - user tried scrolling up (swiping down) past top

                    if (top == view.ListPaddingTop && view.FirstVisiblePosition == 0)
                    {
                        this.FirstVisiblePosition = 0;
                        return;
                    }

                    // OVERSCROLL BOTTOM ("bottoming out")                      
                    // - for now, user needs to hit bottom to force snap when last row wont like "normal"
                    // - this happens for certain GridView height and item/row height combinations
                    // - could smooth scroll in the same manner as below, but requires more code

                    // nRows, curRow round down (floor() not needed)
                    int nRows = view.Count / NumColumns;
                    int curRow = view.LastVisiblePosition / NumColumns;

                    if (curRow == nRows)
                    {                            
                        View item2 = view.GetChildAt(view.LastVisiblePosition-view.FirstVisiblePosition);
                        int top2 = System.Math.Abs(item2.Top);
                        int btm2 = System.Math.Abs(item2.Bottom);                            

                        if (btm2 == view.Height - view.ListPaddingBottom)
                        {
                            //view.SetSelection(view.LastVisiblePosition);
                            this.FirstVisiblePosition = view.LastVisiblePosition;
                            return;
                        }
                    }

                    // EXCEPTION CASE: smore info below
                    if (item.Top == 0)
                    {
                        view.SetSelection(0);
                        this.FirstVisiblePosition = 0;
                        return;
                    }

                    _bSnapScroll = true;

                    // Compensate for ListPaddingTop
                    // - initially gives a positive offset (11 for me),
                    //   observed when attempting to scroll upward from top
                    //   (swipe downward while at very top)
                    //   (break point will show item.Top = 11)
                    // - if scrolling down a tiny bit, item.Top lessens,
                    //   eventually becoming zero, then negative after that

                    int scrollBy = item.Top > 0
                        ? top >= btm ? btm - view.ListPaddingTop : -(view.ListPaddingTop - top)
                        : top >= btm ? btm - view.ListPaddingTop : -(view.ListPaddingTop + top);

                    // ALTERNATIVE SYNTAX - debug friendly (less cool)
                    //int scrollBy = 0;
                    //if (item.Top > 0)
                    //    scrollBy = top >= btm ? btm - view.ListPaddingTop : -(view.ListPaddingTop - top);
                    //else
                    //    scrollBy = top >= btm ? btm - view.ListPaddingTop : -(view.ListPaddingTop + top);

                    if (scrollBy <= 0)
                    {
                        this.FirstVisiblePosition = view.FirstVisiblePosition;
                    }
                    else
                    {
                        this.FirstVisiblePosition = view.FirstVisiblePosition + NumColumns;
                    }

                    view.Post(() => { view.SmoothScrollBy(scrollBy, 1000); });
                }
                break;

            default: break;
        }
    }

Example of exception case noted above 上面提到的例外情况的示例

If user scrolls down, from very top, by exactly the padding amount, and SmoothScrollBy() is called with the resulting value calculated below (which is -view.ListPaddingTop since item.Top == 0 ), no scrolling occurs for some reason (leaving _bSnapScroll inconsistent since OnScrollStateChanged() subsequently misfires). 如果用户从最上端向下精确地滚动到填充量,然后调用SmoothScrollBy()并使用下面计算的结果值(由于item.Top == 0 -view.ListPaddingTop item.Top == 0 ),由于某种原因,不会发生滚动(留下_bSnapScroll不一致,因为OnScrollStateChanged()随后会触发错误)。

                    //if (item.Top == 0)
                    //{
                    //    view.SetSelection(0);
                    //    return;
                    //}

                    _bSnapScroll = true;

                    //int scrollBy = item.Top > 0
                    int scrollBy = item.Top >= 0
                        ? top >= btm ? btm - view.ListPaddingTop : -(view.ListPaddingTop - top)
                        : top >= btm ? btm - view.ListPaddingTop : -(view.ListPaddingTop + top);

Usage 用法

private void GridViewAdapterLoad()
{
    int pos = ((SnapScrollGridView)_gridView).FirstVisiblePosition;

    _gridView.Adapter = new GridViewAdapter(
        Activity,
        Resource.Layout.GridItem,
        Model.GridItems);

    _gridView.SetSelection(pos);
}

Positing grid to bottom after adding a new item (to end) 在添加新项目后将网格定位到底部(到最后)

private void GridViewLoad(bool bNewItem)
{
    _controller.LoadGridItems();

    _gridView.Adapter = null;

    GridViewAdapterLoad();

    if (bNewItem)
    {
        int count = _gridView.Count;            
        int row = --count / _gridView.NumColumns; // floor not needed
        int pos = row * _gridView.NumColumns;

        ((SnapScrollGridView)_gridView).FirstVisiblePosition = pos;

        _gridView.SetSelection(pos);

        // Would be sweet to scroll from current position, but isn't.
        //_gridView.Post(() => { _gridView.SmoothScrollToPosition(pos); });
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM