对于令人困惑的标题,我不能非常简洁地表达问题...
我有一个带有ListView的Android应用程序,它使用循环/“无限”适配器,这基本上意味着我可以根据需要向上或向下滚动它,当它到达顶部或底部时,项目将会环绕,让用户觉得好像他正在旋转无限长的(~100)重复项目列表。
此设置的目的是让用户选择一个随机项,只需旋转/推送列表视图并等待它停止的位置即可。我减少了Listview的摩擦力,因此它的速度更快,更长,这看起来非常好。最后,我在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(右图所示)会更合乎逻辑,因为它更“接近”。
Image http://nickthissen.nl/Images/lv.jpg
我该如何实现这种行为?我能想到的唯一方法是以某种方式测量第一个可见项目的可见数量。如果超过50%,那么我跳到那个位置。如果它低于50%,我跳到那个位置+ 1.然而我不知道如何衡量......
有什么想法吗?
答案 0 :(得分: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
}
答案 1 :(得分:3)
按照以下3个步骤,您可以得到您想要的!!!!
1.初始化两个变量以进行上下滚动:
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;
}
}
答案 2 :(得分:3)
这是我的最终代码,受Shade的回答启发。
我忘了先添加"if(Math.abs(r.height())!=height)"
。然后它滚动到正确位置后滚动两次,因为它总是大于childView的height / 2。
希望它有所帮助。
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) {
}});
答案 3 :(得分:0)
如果您可以获取需要滚动到的行的位置,则可以使用以下方法:
类似于:
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);
答案 4 :(得分:0)
知道第一个可见位置后,您应该能够在下一个位置的视图中使用View.getLocationinWindow()
或View.getLocationOnScreen()
来获得的可见高度第一即可。将其与View
的高度进行比较,如果合适,滚动到下一个位置。
您可能需要对其进行调整以考虑填充,具体取决于行的外观。
我没有尝试过上述内容,但似乎应该可行。如果没有,这是另一个可能不那么强大的想法:
getLastVisiblePosition()
。如果您采用last
和first
之间的差异,则可以查看屏幕上可见的位置数。将其与首次填充列表时可见的位置数相比较(滚动位置0)。
如果可以看到相同数量的位置,只需滚动到您正在进行的第一个可见位置。如果还有一个可见,请滚动到“第一个+ 1”位置。
答案 5 :(得分:0)
你可能已经解决了这个问题,但我认为这个解决方案应该可行
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)
}
});
}
答案 6 :(得分: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);
}
}