[英]How to make a GridView snap scroll
有沒有一種方法可以使GridView
在滾動時對齊到一行?
似乎為ListView
應用已知的修復程序不足以為GridView
足夠的支持。
注意:此代碼已針對API 23進行了測試
注: listPaddingTop == 11
,和listPaddingBottom == 10
為我GridView
。 找不到設置的位置,可能是默認設置(在下面的構造函數鏈中未以Android.Resource.Attribute.GridViewStyle
引用的樣式看到它(在res / values中指向Widget.GridView
/ Widget.AbsListView
/styles.xml )。需要檢查源代碼。
注意:在構造函數中調用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;
}
}
上面提到的例外情況的示例
如果用戶從最上端向下精確地滾動到填充量,然后調用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);
用法
private void GridViewAdapterLoad()
{
int pos = ((SnapScrollGridView)_gridView).FirstVisiblePosition;
_gridView.Adapter = new GridViewAdapter(
Activity,
Resource.Layout.GridItem,
Model.GridItems);
_gridView.SetSelection(pos);
}
在添加新項目后將網格定位到底部(到最后)
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.