我有以下要求:
考虑到prev page&下一页在场景中可用,添加了以下代码:
if(prevPageNo > 0){
mListViewActual.setOnScrollListener(this);
}
if(nextPageNo > 0){
mListViewActual.setOnScrollListener(this);
}
我应该在什么条件下检测向上滚动&向下滚动以下方法:
答案 0 :(得分:88)
尝试使用setOnScrollListener并使用scrollState实现onScrollStateChanged
setOnScrollListener(new OnScrollListener(){
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
final ListView lw = getListView();
if(scrollState == 0)
Log.i("a", "scrolling stopped...");
if (view.getId() == lw.getId()) {
final int currentFirstVisibleItem = lw.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
mIsScrollingUp = false;
Log.i("a", "scrolling down...");
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
mIsScrollingUp = true;
Log.i("a", "scrolling up...");
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
});
答案 1 :(得分:62)
以下是一些上述解决方案的工作修改版本。
添加另一个类ListView:
package com.example.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
public class ListView extends android.widget.ListView {
private OnScrollListener onScrollListener;
private OnDetectScrollListener onDetectScrollListener;
public ListView(Context context) {
super(context);
onCreate(context, null, null);
}
public ListView(Context context, AttributeSet attrs) {
super(context, attrs);
onCreate(context, attrs, null);
}
public ListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
onCreate(context, attrs, defStyle);
}
@SuppressWarnings("UnusedParameters")
private void onCreate(Context context, AttributeSet attrs, Integer defStyle) {
setListeners();
}
private void setListeners() {
super.setOnScrollListener(new OnScrollListener() {
private int oldTop;
private int oldFirstVisibleItem;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (onScrollListener != null) {
onScrollListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (onScrollListener != null) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if (onDetectScrollListener != null) {
onDetectedListScroll(view, firstVisibleItem);
}
}
private void onDetectedListScroll(AbsListView absListView, int firstVisibleItem) {
View view = absListView.getChildAt(0);
int top = (view == null) ? 0 : view.getTop();
if (firstVisibleItem == oldFirstVisibleItem) {
if (top > oldTop) {
onDetectScrollListener.onUpScrolling();
} else if (top < oldTop) {
onDetectScrollListener.onDownScrolling();
}
} else {
if (firstVisibleItem < oldFirstVisibleItem) {
onDetectScrollListener.onUpScrolling();
} else {
onDetectScrollListener.onDownScrolling();
}
}
oldTop = top;
oldFirstVisibleItem = firstVisibleItem;
}
});
}
@Override
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
public void setOnDetectScrollListener(OnDetectScrollListener onDetectScrollListener) {
this.onDetectScrollListener = onDetectScrollListener;
}
}
接口:
public interface OnDetectScrollListener {
void onUpScrolling();
void onDownScrolling();
}
最后如何使用:
com.example.view.ListView listView = (com.example.view.ListView) findViewById(R.id.list);
listView.setOnDetectScrollListener(new OnDetectScrollListener() {
@Override
public void onUpScrolling() {
/* do something */
}
@Override
public void onDownScrolling() {
/* do something */
}
});
在XML布局中:
<com.example.view.ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
这是我的第一个话题,不要严厉地评判我。 =)
答案 2 :(得分:44)
这是一个简单的实现:
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;
}
});
如果您需要更高的精度,可以使用此自定义ListView类:
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
/**
* Created by root on 26/05/15.
*/
public class ScrollInterfacedListView extends ListView {
private OnScrollListener onScrollListener;
private OnDetectScrollListener onDetectScrollListener;
public ScrollInterfacedListView(Context context) {
super(context);
onCreate(context, null, null);
}
public ScrollInterfacedListView(Context context, AttributeSet attrs) {
super(context, attrs);
onCreate(context, attrs, null);
}
public ScrollInterfacedListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
onCreate(context, attrs, defStyle);
}
@SuppressWarnings("UnusedParameters")
private void onCreate(Context context, AttributeSet attrs, Integer defStyle) {
setListeners();
}
private void setListeners() {
super.setOnScrollListener(new OnScrollListener() {
private int oldTop;
private int oldFirstVisibleItem;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (onScrollListener != null) {
onScrollListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (onScrollListener != null) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if (onDetectScrollListener != null) {
onDetectedListScroll(view, firstVisibleItem);
}
}
private void onDetectedListScroll(AbsListView absListView, int firstVisibleItem) {
View view = absListView.getChildAt(0);
int top = (view == null) ? 0 : view.getTop();
if (firstVisibleItem == oldFirstVisibleItem) {
if (top > oldTop) {
onDetectScrollListener.onUpScrolling();
} else if (top < oldTop) {
onDetectScrollListener.onDownScrolling();
}
} else {
if (firstVisibleItem < oldFirstVisibleItem) {
onDetectScrollListener.onUpScrolling();
} else {
onDetectScrollListener.onDownScrolling();
}
}
oldTop = top;
oldFirstVisibleItem = firstVisibleItem;
}
});
}
@Override
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
public void setOnDetectScrollListener(OnDetectScrollListener onDetectScrollListener) {
this.onDetectScrollListener = onDetectScrollListener;
}
public interface OnDetectScrollListener {
void onUpScrolling();
void onDownScrolling();
}
}
使用示例: (别忘了在layout.xml中将其添加为Xml标记)
scrollInterfacedListView.setOnDetectScrollListener(new ScrollInterfacedListView.OnDetectScrollListener() {
@Override
public void onUpScrolling() {
//Do your thing
}
@Override
public void onDownScrolling() {
//Do your thing
}
});
答案 3 :(得分:10)
发布所有方法后,在识别用户从第一个元素向上滚动或从最后一个元素向下滚动时会出现问题。 这是检测向上/向下滚动的另一种方法:
listView.setOnTouchListener(new View.OnTouchListener() {
float height;
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
float height = event.getY();
if(action == MotionEvent.ACTION_DOWN){
this.height = height;
}else if(action == MotionEvent.ACTION_UP){
if(this.height < height){
Log.v(TAG, "Scrolled up");
}else if(this.height > height){
Log.v(TAG, "Scrolled down");
}
}
return false;
}
});
答案 4 :(得分:10)
ListView listView = getListView();
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
view.setOnTouchListener(new OnTouchListener() {
private float mInitialX;
private float mInitialY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialX = event.getX();
mInitialY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
final float x = event.getX();
final float y = event.getY();
final float yDiff = y - mInitialY;
if (yDiff > 0.0) {
Log.d(tag, "SCROLL DOWN");
scrollDown = true;
break;
} else if (yDiff < 0.0) {
Log.d(tag, "SCROLL up");
scrollDown = true;
break;
}
break;
}
return false;
}
});
答案 5 :(得分:8)
我的解决方案完美地为每个滚动方向提供精确值。
distanceFromFirstCellToTop
包含从第一个单元格到父视图顶部的确切距离。我将此值保存在previousDistanceFromFirstCellToTop
中,当我滚动时,我将其与新值进行比较。如果它低于我向上滚动,否则,我向下滚动。
private int previousDistanceFromFirstCellToTop;
listview.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
View firstCell = listview.getChildAt(0);
int distanceFromFirstCellToTop = listview.getFirstVisiblePosition() * firstCell.getHeight() - firstCell.getTop();
if(distanceFromFirstCellToTop < previousDistanceFromFirstCellToTop)
{
//Scroll Up
}
else if(distanceFromFirstCellToTop > previousDistanceFromFirstCellToTop)
{
//Scroll Down
}
previousDistanceFromFirstCellToTop = distanceFromFirstCellToTop;
}
});
对于Xamarin开发人员,解决方案如下:
注意:不要忘记在UI线程上运行
listView.Scroll += (o, e) =>
{
View firstCell = listView.GetChildAt(0);
int distanceFromFirstCellToTop = listView.FirstVisiblePosition * firstCell.Height - firstCell.Top;
if (distanceFromFirstCellToTop < previousDistanceFromFirstCellToTop)
{
//Scroll Up
}
else if (distanceFromFirstCellToTop > previousDistanceFromFirstCellToTop)
{
//Scroll Down
}
previousDistanceFromFirstCellToTop = distanceFromFirstCellToTop;
};
答案 6 :(得分:4)
为了检测更大元素的滚动,我更喜欢onTouch Listener:
listview.setOnTouchListener(new View.OnTouchListener() {
int scrollEventListSize = 5;
float lastY;
// Used to correct for occasions when user scrolls down(/up) but the onTouchListener detects it incorrectly. We will store detected up-/down-scrolls with -1/1 in this list and evaluate later which occured more often
List<Integer> downScrolledEventsHappened;
@Override
public boolean onTouch(View v, MotionEvent event) {
float diff = 0;
if(event.getAction() == event.ACTION_DOWN){
lastY = event.getY();
downScrolledEventsHappened = new LinkedList<Integer>();
}
else if(event.getAction() == event.ACTION_MOVE){
diff = event.getY() - lastY;
lastY = event.getY();
if(diff>0)
downScrolledEventsHappened.add(1);
else
downScrolledEventsHappened.add(-1);
//List needs to be filled with some events, will happen very quickly
if(downScrolledEventsHappened.size() == scrollEventListSize+1){
downScrolledEventsHappened.remove(0);
int res=0;
for(int i=0; i<downScrolledEventsHappened.size(); i++){
res+=downScrolledEventsHappened.get(i);
}
if (res > 0)
Log.i("INFO", "Scrolled up");
else
Log.i("INFO", "Scrolled down");
}
}
return false; // don't interrupt the event-chain
}
});
答案 7 :(得分:4)
我使用了这个更简单的解决方案:
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) {
}
}
答案 8 :(得分:3)
存储firstVisibleItem并在下一个onScroll上检查新的firstVisibleItem是否小于或大于前一个。
示例伪代码(未测试):
int lastVisibleItem = 0;
boolean isScrollingDown = false;
void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem > lastVisibleItem) {
isScrollingDown = true;
}
else {
isScrollingDown = false;
}
lastVisibleItem = firstVisibleItem;
}
答案 9 :(得分:3)
只需将滚动侦听器设置为列表视图。
如果您有页眉或页脚,则应检查可见计数。如果它增加它意味着你向下滚动。 (如果有页脚而不是标题,则反转它)
如果列表视图中没有任何页眉或页脚,则可以删除显示可见项目数的行。
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mLastFirstVisibleItem > firstVisibleItem) {
Log.e(getClass().toString(), "scrolling up");
} else if (mLastFirstVisibleItem < firstVisibleItem) {
Log.e(getClass().toString(), "scrolling down");
} else if (mLastVisibleItemCount < visibleItemCount) {
Log.e(getClass().toString(), "scrolling down");
} else if (mLastVisibleItemCount > visibleItemCount) {
Log.e(getClass().toString(), "scrolling up");
}
mLastFirstVisibleItem = firstVisibleItem;
mLastVisibleItemCount = visibleItemCount;
}
public void onScrollStateChanged(AbsListView listView, int scrollState) {
}
});
并拥有此变量
int mLastFirstVisibleItem;
int mLastVisibleItemCount;
答案 10 :(得分:1)
由于某些原因,Android doc不会涵盖这个,并且使用的方法甚至不在文档中......花了我一段时间才找到它。
要检测您的滚动条是否位于顶部,您可以使用此功能。
public boolean checkAtTop()
{
if(listView.getChildCount() == 0) return true;
return listView.getChildAt(0).getTop() == 0;
}
这将检查您的滚动条是否位于顶部。现在,为了在底部执行此操作,您必须传递它拥有的子项数,并检查该数字。您可能需要一次弄清楚屏幕上有多少,并从您的孩子数中减去。我从来没有这样做过。希望这有帮助
答案 11 :(得分:1)
在android listview上检测向上/向下滚动的简单方法
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount){
if(prevVisibleItem != firstVisibleItem){
if(prevVisibleItem < firstVisibleItem)
//ScrollDown
else
//ScrollUp
prevVisibleItem = firstVisibleItem;
}
别忘了
yourListView.setOnScrollListener(yourScrollListener);
答案 12 :(得分:1)
在listview中关于检测向上或向下滚动的技巧,只需在ListView的OnScrollListener上的onScroll函数上调用此函数。
private int oldFirstVisibleItem = -1;
private protected int oldTop = -1;
// you can change this value (pixel)
private static final int MAX_SCROLL_DIFF = 5;
private void calculateListScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem == oldFirstVisibleItem) {
int top = view.getChildAt(0).getTop();
// range between new top and old top must greater than MAX_SCROLL_DIFF
if (top > oldTop && Math.abs(top - oldTop) > MAX_SCROLL_DIFF) {
// scroll up
} else if (top < oldTop && Math.abs(top - oldTop) > MAX_SCROLL_DIFF) {
// scroll down
}
oldTop = top;
} else {
View child = view.getChildAt(0);
if (child != null) {
oldFirstVisibleItem = firstVisibleItem;
oldTop = child.getTop();
}
}
}
答案 13 :(得分:1)
这些方法不能直接用于检测滚动方向。有很多方法可以获得方向。下面解释了一种用于这种方法的简单代码(未经测试):
public class ScrollTrackingListView extends ListView { private boolean readyForMeasurement = false; private Boolean isScrollable = null; private float prevDistanceToEnd = -1.0; private ScrollDirectionListener listener = null; public ScrollTrackingListView(Context context) { super(context); init(); } public ScrollTrackingListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScrollTrackingListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { ViewTreeObserver observer = getViewTreeObserver(); observer.addOnGlobalLayoutListener(globalLayoutListener); setOnScrollListener(scrollListener); } private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { readyForMeasurement = true; calculateDistanceToEnd(); } }; public void registerScrollDirectionListener(ScrollDirectionListener listener) { scrollDirectionListener = listener; } public void unregisterScrollDirectionListener() { scrollDirectionListener = null; } private OnScrollListener scrollListener = new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView absListView, int i) { calculateDistanceToEnd(); } @Override public void onScroll(AbsListView absListView, int i, int i1, int i2) { // Do nothing } }; private void calculateDistanceToEnd() { if (readyForMeasurement) { // I'm using the height of the layout, horizontal scrollbar and // content along with scroll down offset // computeVerticalScrollExtent is used to compute the length of the thumb within the scrollbar's track. // The length of the thumb is a function of the view height and the content length. int verticalScrollExtent = computeVerticalScrollExtent(); int verticalScrollOffset = computeVerticalScrollOffset(); int verticalScrollRange = computeVerticalScrollRange(); int horizontalScrollBarHeight = getHorizontalScrollbarHeight(); /** * 1. Let "R" represent the range of the vertical scrollbar. This corresponds to the length of the content * in the view. * 2. Let "E" represent the extent of the vertical scrollbar. The extent is a constant value and is * (probably) equal to a value proportional to the height of the view. * 3. Offset "o" represents the current position in the range that is visible to the user. It can take * values from "0 to E". * * Now the DistanceToEnd is calculated using these three values as follows : * * DistanceToEnd = (R - o) / E * * DistanceToEnd will hold the value in NumberOfScreenToEnd units. * */ float distanceToEnd = ((float)(verticalScrollRange - verticalScrollOffset))/((float)(verticalScrollExtent)); if(prevDistanceToEnd == -1) { prevDistanceToEnd = distanceToEnd; } else { if(listener != null) { if(distanceToEnd > prevDistanceToEnd) { // User is scrolling up listener.onScrollingUp(); } else { // User is scrolling up listener.onScrollingDown(); } } prevDistanceToEnd = distanceToEnd; } if(isScrollable == null) { // Check if the view height is less than a screen (i.e., no scrolling is enabled) if((horizontalScrollBarHeight + verticalScrollExtent) >= verticalScrollRange) { isScrollable = Boolean.FALSE; } else { isScrollable = Boolean.TRUE; } } } } public interface ScrollDirectionListener { public void onScrollingUp(); public void onScrollingDown(); } }
想法是计算distanceToEnd。如果distanceToEnd增加,则用户向上滚动,如果它减小,则用户向下滚动。这也将为您提供到列表末尾的确切距离。
如果您只是想知道用户是向上还是向下滚动,您可以覆盖onInterceptTouchEvent以检测滚动方向,如下所示:
@Override public boolean onInterceptTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mInitialX = event.getX(); mInitialY = event.getY(); return true; case MotionEvent.ACTION_MOVE: final float x = event.getX(); final float y = event.getY(); final float yDiff = y - mInitialY; // yDiff less than 0.0 implies scrolling down while yDiff greater than 0.0 implies scrolling up. If I try to add the less than or greater than symbols, the preview refuses to display it. if(yDiff less than 0.0) listener.onScrollingDown(); else if(yDiff greater than 0.0) listener.onScrollingUp(); break; } return super.onInterceptTouchEvent(event); }
答案 14 :(得分:0)
我在使用ListView的单元格大小很好的一些示例时遇到了问题。所以我找到了解决问题的方法,可以检测到手指的最轻微移动。我已经简化到最小可能,如下:
private int oldScrolly;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
View view = absListView.getChildAt(0);
int scrolly = (view == null) ? 0 : -view.getTop() + absListView.getFirstVisiblePosition() * view.getHeight();
int margin = 10;
Log.e(TAG, "Scroll y: " + scrolly + " - Item: " + firstVisibleItem);
if (scrolly > oldScrolly + margin) {
Log.d(TAG, "SCROLL_UP");
oldScrolly = scrolly;
} else if (scrolly < oldScrolly - margin) {
Log.d(TAG, "SCROLL_DOWN");
oldScrolly = scrolly;
}
}
});
PD:我使用MARGIN来检测滚动,直到你遇到这个边距。当我显示或隐藏视图并避免它们闪烁时,这可以避免出现问题。
答案 15 :(得分:0)
以下是我首先尝试的内容:
1)使用以下方法创建一个接口(让我们称之为OnScrollTopOrBottomListener):
void onScrollTop();
void onScrollBottom();
2)在列表的适配器中,添加一个成员实例,键入为您创建的界面,并提供一个setter和getter。
3)在适配器的getView()实现中,检查position参数是0还是getCount() - 1.同时检查你的OnScrollTopOrBottomListener实例是否为空。
4)如果位置为0,则调用onScrollTopOrBottomListener.onScrollTop()。如果position是getCount() - 1,则调用onScrollTopOrBottomListener.onScrollBottom()。
5)在OnScrollTopOrBottomListener实现中,调用适当的方法来获取所需的数据。
希望在某种程度上有所帮助。
布兰登
答案 16 :(得分:0)
在android GridView中的向上/向下滚动事件中加载更多项目的简单方法
grid.setOnScrollListener(new AbsListView.OnScrollListener() {
private int mLastFirstVisibleItem;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
Log.d("state",String.valueOf(scrollState));
if(scrollState == 0)
Log.i("a", "scrolling stopped...");
if (view.getId() == grid.getId()) {
final int currentFirstVisibleItem = grid.getLastVisiblePosition();
mLastFirstVisibleItem = grid.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
mIsScrollingUp = false;
if(!next.contains("null")){
//Call api to get products from server
}
Log.i("a", "scrolling down...");
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
mIsScrollingUp = true;
Log.i("a", "scrolling up...");
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
Log.d("on scroll","");
}
});