检测适配器中的滚动方向(上/下)
Posted
技术标签:
【中文标题】检测适配器中的滚动方向(上/下)【英文标题】:Detecting the scrolling direction in the adapter (up/down) 【发布时间】:2012-08-20 08:18:39 【问题描述】:我正在尝试在我的项目中模仿 Google Plus 应用程序,因为它现在似乎是参考。
滚动时的列表视图效果真的很好,我想做类似的事情。
我从 LayoutAnimationController 开始 http://android-er.blogspot.be/2009/10/listview-and-listactivity-layout.html
LayoutAnimationController controller
= AnimationUtils.loadLayoutAnimation(
this, R.anim.list_layout_controller);
getListView().setLayoutAnimation(controller);
这似乎很糟糕,因为并非所有元素都是动画的:
所以我最终使用了适配器的 getView 并使用了这个:
AnimationSet set = new AnimationSet(true);
Animation animation = new AlphaAnimation(0.0f, 1.0f);
animation.setDuration(800);
set.addAnimation(animation);
animation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 1.0f,Animation.RELATIVE_TO_SELF, 0.0f
);
animation.setDuration(600);
set.addAnimation(animation);
row.startAnimation(set);
结果太棒了,我真的很高兴!
不幸的是,它仅在我从列表的顶部滚动到底部时才有效!
我想让它在另一边滚动时工作,我需要稍微改变一下 TranslateAnimation。
所以我的问题是,有没有办法检测我在适配器中是向上还是向下滚动?
【问题讨论】:
您的示例中的“行”变量是什么? 您可以在这篇文章中看到完整的解决方案:***.com/questions/16791100/… 另一个可行的解决方案***.com/a/34788591/1939564 【参考方案1】:将OnScrollListener
分配给您的ListView
。创建一个标志,指示用户是向上还是向下滚动。通过检查当前 first 可见项位置是否大于或小于前一个 first 可见项位置,为标志设置适当的值。将该支票放入onScrollStateChanged()
。
示例代码:
private int mLastFirstVisibleItem;
private boolean mIsScrollingUp;
public void onScrollStateChanged(AbsListView view, int scrollState)
final ListView lw = getListView();
if (view.getId() == lw.getId())
final int currentFirstVisibleItem = lw.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem)
mIsScrollingUp = false;
else if (currentFirstVisibleItem < mLastFirstVisibleItem)
mIsScrollingUp = true;
mLastFirstVisibleItem = currentFirstVisibleItem;
检查mIsScrollingUp
在getView()
中是真还是假,并相应地分配动画。
【讨论】:
我写了我的解决方案,但你的似乎也很正确。我想知道哪个“更好”,我想我需要更少的代码,但也许使用这个位置更“脏” @Waza_Be:是的,除了我的回答添加了更多代码之外,没有太大区别。但是无论如何,例如,如果您扩展并希望在用户将ListView
扔掉时跳过动画,那么在OnScrollListener
内进行控制可能最合适。 :-)
这并不能真正告诉您是向上还是向下滚动,只有在您向上或向下滚动时才会告诉您,因为第一个可见项目在它不可见之前不会改变。在我的情况下,我实际上需要一个常量回调,因为列表正在滚动。
添加克里斯托弗的评论。这是获得有关您是否滚动的更直接反馈的简单方法。您可以根据列表视图的第一个子项的位置来执行此操作。它也适用于网格视图。 listView.getChildAt(0).getTop()
不错。虽然如果列表项的高度很大,这可能不是最优雅的解决方案。【参考方案2】:
我最终这样做了:
@Override
public View getView(int position, View convertView, ViewGroup parent)
Log.i("",position+" - "+lastposition);
if (position >= lastposition)
animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, 1.0f,
Animation.RELATIVE_TO_SELF, 0.0f);
else
animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_SELF, -1.0f,
Animation.RELATIVE_TO_SELF, 0.0f);
animation.setDuration(600);
set.addAnimation(animation);
row.startAnimation(set);
lastposition = position;
【讨论】:
甜蜜。这是一个更好的解决方案。lastposition
来自哪里?【参考方案3】:
更复杂的解决方案(在列表视图中处理长项目高度)
创建自定义列表视图
public class ScrollDetectingListView extends ListView
public ScrollDetectingListView(Context context)
super(context);
public ScrollDetectingListView(Context context, AttributeSet attrs)
super(context,attrs);
public ScrollDetectingListView(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
//we need this protected method for scroll detection
public int getVerticalScrollOffset()
return computeVerticalScrollOffset();
覆盖 onScroll
listView.setOnScrollListener(new AbsListView.OnScrollListener()
private int mInitialScroll = 0;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
int scrolledOffset = listView.getVerticalScrollOffset();
if (scrolledOffset!=mInitialScroll)
//if scroll position changed
boolean scrollUp = (scrolledOffset - mInitialScroll) < 0;
mInitialScroll = scrolledOffset;
);
【讨论】:
迄今为止最好的答案。这解决了 Listview 回收其视图使“listview.getChildAt(0).getTop()”不可靠时的问题 listView.getVerticalScrollOffset() 未找到【参考方案4】:接受的答案并没有真正“检测到”向上或向下滚动。如果当前可见项目真的很大,它将不起作用。使用onTouchListener
是可行的方法。
这是我使用的代码sn-p:
listView.setOnTouchListener(new View.OnTouchListener()
float initialY, finalY;
boolean isScrollingUp;
@Override
public boolean onTouch(View v, MotionEvent event)
int action = MotionEventCompat.getActionMasked(event);
switch(action)
case (MotionEvent.ACTION_DOWN):
initialY = event.getY();
case (MotionEvent.ACTION_UP):
finalY = event.getY();
if (initialY < finalY)
Log.d(TAG, "Scrolling up");
isScrollingUp = true;
else if (initialY > finalY)
Log.d(TAG, "Scrolling down");
isScrollingUp = false;
default:
if (isScrollingUp)
// do animation for scrolling up
else
// do animation for scrolling down
return false; // has to be false, or it will freeze the listView
);
【讨论】:
想象用户在没有从屏幕上分离他们的“工具”的情况下上下滚动,在这种情况下,初始值不会被更新,你会得到isScrollingUp
标志的错误结果。您应该始终检查current
和previous
点。不是最终的final
和initial
分【参考方案5】:
这是我遇到的最简单、最简单的方法。它就像一个魅力。
view.addOnScrollListener(new View.OnScrollListener()
@Override
public void onScrolled(@NonNull View view, int dx, int dy)
if (dy > 0)
//Scrolling down
else if (dy < 0)
//Scrolling up
);
【讨论】:
【参考方案6】:试试这个。我希望它对你有帮助。来自@Gal Rom 答案的逻辑。
lv.setOnScrollListener(new OnScrollListener()
private int mLastFirstVisibleItem;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
if(mLastFirstVisibleItem<firstVisibleItem)
Log.i("SCROLLING DOWN","TRUE");
if(mLastFirstVisibleItem>firstVisibleItem)
Log.i("SCROLLING UP","TRUE");
mLastFirstVisibleItem=firstVisibleItem;
);
【讨论】:
【参考方案7】:这是我的方法:它可以让您更直接地反馈您滚动了多少:
OnScroll
,您只需获取列表中第一项的 Top 位置即可。立即获得实际的滚动位置信息非常可靠。
listView.getChildAt(0).getTop()
【讨论】:
在适配器的getView
中,您可以找到 lisView 作为 parent
参数。【参考方案8】:
我使用了这个更简单的解决方案:
public class ScrollDetectingListView extends ListView
...
setOnScrollListener( new OnScrollListener()
private int mInitialScroll = 0;
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
int scrolledOffset = computeVerticalScrollOffset();
boolean scrollUp = scrolledOffset > mInitialScroll;
mInitialScroll = scrolledOffset;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
【讨论】:
什么是 computeVerticalScroll()? developer.android.com/reference/android/widget/AbsListView.html中的受保护方法【参考方案9】:list.setOnScrollListener(new OnScrollListener()
int last_item;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
if(last_item<firstVisibleItem+visibleItemCount-1)
System.out.println("List is scrolling upwards");
else if(last_item>firstVisibleItem+visibleItemCount-1)
System.out.println("List is scrolling downwards");
last_item = firstVisibleItem+visibleItemCount-1;
);
根据最后一个可见项目的位置,我决定 Listview 是向上还是向下。
【讨论】:
【参考方案10】:不依赖于视图/等位置的通用解决方案。只需检查垂直滚动偏移量并将其与之前的滚动偏移量进行比较。如果新值大于旧值,则用户向下滚动,反之亦然。
// [START check vertical scroll direction]
int oldScrollOffset = 0;
listView.setOnScrollChangeListener(new View.OnScrollChangeListener()
@Override
public void onScrollChange(View view, int i, int i1, int i2, int i3)
Boolean scrollDirectionDown;
int newScrollOffset = listView.computeVerticalScrollOffset();
if (newScrollOffset > oldScrollOffset)
scrollDirectionDown = true;
else
scrollDirectionDown = false;
oldScrollOffset = newScrollOffset;
if (scrollDirectionDown)
// Update accordingly for scrolling down
Log.d(TAG, "scrolling down");
else
// Update accordingly for scrolling up
Log.d(TAG, "scrolling up");
);
// [END check vertical scroll direction]
【讨论】:
【参考方案11】:view.setOnTouchListener(new View.OnTouchListener()
private long startClickTime;
float y0 = 0;
float y1 = 0;
@Override
public boolean onTouch(View view, MotionEvent motionEvent)
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN)
y0 = motionEvent.getY();
startClickTime = System.currentTimeMillis();
else if (motionEvent.getAction() == MotionEvent.ACTION_UP)
if (System.currentTimeMillis() - startClickTime < ViewConfiguration.getTapTimeout())
// Touch was a simple tap. Do whatever.
else
y1 = motionEvent.getY();
// Touch was a not a simple tap.
if (y1 - y0 > 50)
// this is down
else if (y1 - y0 < 50)
Log.d("daniY", "-");
// this is up
return true;
);
这对我有用,这将有助于检测我认为的所有视图的滚动方向。
【讨论】:
以上是关于检测适配器中的滚动方向(上/下)的主要内容,如果未能解决你的问题,请参考以下文章