studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones android listview

android - programacion - Averigüe si ListView se desplaza hacia abajo?



manual de programacion android pdf (22)

Última respuesta, pero si simplemente desea comprobar si su ListView se desplaza hacia abajo o no, sin crear un detector de eventos, puede utilizar esta instrucción if:

if (yourListView.getLastVisiblePosition() == yourListView.getAdapter().getCount() -1 && yourListView.getChildAt(yourListView.getChildCount() - 1).getBottom() <= yourListView.getHeight()) { //It is scrolled all the way down here }

Primero verifica si la última posición posible está a la vista. Luego verifica si la parte inferior del último botón se alinea con la parte inferior de ListView. Puede hacer algo similar para saber si está en la parte superior:

if (yourListView.getFirstVisiblePosition() == 0 && yourListView.getChildAt(0).getTop() >= 0) { //It is scrolled all the way up here }

¿Puedo averiguar si mi ListView se desplaza hacia abajo? Con eso quiero decir que el último elemento es completamente visible.


¡Muchas gracias a los posters en ! Combiné algunas ideas y creé un oyente de clase para actividades y fragmentos (por lo que este código es más reutilizable y hace que el código sea más rápido de escribir y mucho más limpio).

Todo lo que tiene que hacer cuando obtiene mi clase es implementar la interfaz (y, por supuesto, crear un método para ella) que está declarada en mi clase y crear el objeto de esta clase que pasa argumentos.

/** * Listener for getting call when ListView gets scrolled to bottom */ public class ListViewScrolledToBottomListener implements AbsListView.OnScrollListener { ListViewScrolledToBottomCallback scrolledToBottomCallback; private int currentFirstVisibleItem; private int currentVisibleItemCount; private int totalItemCount; private int currentScrollState; public interface ListViewScrolledToBottomCallback { public void onScrolledToBottom(); } public ListViewScrolledToBottomListener(Fragment fragment, ListView listView) { try { scrolledToBottomCallback = (ListViewScrolledToBottomCallback) fragment; listView.setOnScrollListener(this); } catch (ClassCastException e) { throw new ClassCastException(fragment.toString() + " must implement ListViewScrolledToBottomCallback"); } } public ListViewScrolledToBottomListener(Activity activity, ListView listView) { try { scrolledToBottomCallback = (ListViewScrolledToBottomCallback) activity; listView.setOnScrollListener(this); } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement ListViewScrolledToBottomCallback"); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.currentFirstVisibleItem = firstVisibleItem; this.currentVisibleItemCount = visibleItemCount; this.totalItemCount = totalItemCount; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { this.currentScrollState = scrollState; if (isScrollCompleted()) { if (isScrolledToBottom()) { scrolledToBottomCallback.onScrolledToBottom(); } } } private boolean isScrollCompleted() { if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) { return true; } else { return false; } } private boolean isScrolledToBottom() { System.out.println("First:" + currentFirstVisibleItem); System.out.println("Current count:" + currentVisibleItemCount); System.out.println("Total count:" + totalItemCount); int lastItem = currentFirstVisibleItem + currentVisibleItemCount; if (lastItem == totalItemCount) { return true; } else { return false; } } }


Algo en el sentido de:

if (getListView().getLastVisiblePosition() == (adapter.items.size() - 1))


Debe agregar un recurso de pie de página xml vacío a su listaView y detectar si este pie de página está visible.

private View listViewFooter; public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_newsfeed, container, false); listView = (CardListView) rootView.findViewById(R.id.newsfeed_list); footer = inflater.inflate(R.layout.newsfeed_listview_footer, null); listView.addFooterView(footer); return rootView; }

Luego, en su lista de oyentes de desplazamiento por desplazamiento, usted hace esto

@ Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (firstVisibleItem == 0) { mSwipyRefreshLayout.setDirection(SwipyRefreshLayoutDirection.TOP); mSwipyRefreshLayout.setEnabled(true); } else if (firstVisibleItem + visibleItemCount == totalItemCount) //If last row is visible. In this case, the last row is the footer. { if (footer != null) //footer is a variable referencing the footer view of the ListView. You need to initialize this onCreate { if (listView.getHeight() == footer.getBottom()) { //Check if the whole footer is visible. mSwipyRefreshLayout.setDirection(SwipyRefreshLayoutDirection.BOTTOM); mSwipyRefreshLayout.setEnabled(true); } } } else mSwipyRefreshLayout.setEnabled(false); }


En el método getView() (de una clase derivada de BaseAdapter ) se puede verificar si la posición de la vista actual es igual a la lista de elementos en el Adapter . Si ese es el caso, significa que hemos llegado al final o al final de la lista:

@Override public View getView(int position, View convertView, ViewGroup parent) { // ... // detect if the adapter (of the ListView/GridView) has reached the end if (position == getCount() - 1) { // ... end of list reached } }


Encontré una manera muy agradable de cargar automáticamente el siguiente conjunto de páginas de una manera que no requiere su propio ScrollView (como requiere la respuesta aceptada).

En ParseQueryAdapter hay un método llamado getNextPageView que le permite proporcionar su propia vista personalizada que aparece al final de la lista cuando hay más datos para cargar, de modo que solo se activará cuando haya llegado al final de su página actual. set (es la vista "cargar más ..." de forma predeterminada). Este método solo se loadNextPage(); cuando hay más datos para cargar, por lo que es un gran lugar para llamar a loadNextPage(); De esta forma, el adaptador hace todo el trabajo por usted para determinar cuándo se deben cargar nuevos datos y no se llamará en absoluto si ha llegado al final del conjunto de datos.

public class YourAdapter extends ParseQueryAdapter<ParseObject> { .. @Override public View getNextPageView(View v, ViewGroup parent) { loadNextPage(); return super.getNextPageView(v, parent); } }

Luego, dentro de tu actividad / fragmento, solo tienes que configurar el adaptador y los nuevos datos se actualizarán automáticamente para ti, como la magia.

adapter = new YourAdapter(getActivity().getApplicationContext()); adapter.setObjectsPerPage(15); adapter.setPaginationEnabled(true); yourList.setAdapter(adapter);


Encuentro una forma mejor de detectar listview scroll end the bottom, primero detectar scoll end por este
Implementación de onScrollListener para detectar el final del desplazamiento en un ListView

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.currentFirstVisibleItem = firstVisibleItem; this.currentVisibleItemCount = visibleItemCount; } public void onScrollStateChanged(AbsListView view, int scrollState) { this.currentScrollState = scrollState; this.isScrollCompleted(); } private void isScrollCompleted() { if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) { /*** In this way I detect if there''s been a scroll which has completed ***/ /*** do the work! ***/ } }

finalmente combine la respuesta de Martijn

OnScrollListener onScrollListener_listview = new OnScrollListener() { private int currentScrollState; private int currentVisibleItemCount; @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO Auto-generated method stub this.currentScrollState = scrollState; this.isScrollCompleted(); } @Override public void onScroll(AbsListView lw, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub this.currentVisibleItemCount = visibleItemCount; } private void isScrollCompleted() { if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) { /*** In this way I detect if there''s been a scroll which has completed ***/ /*** do the work! ***/ if (listview.getLastVisiblePosition() == listview.getAdapter().getCount() - 1 && listview.getChildAt(listview.getChildCount() - 1).getBottom() <= listview.getHeight()) { // It is scrolled all the way down here Log.d("henrytest", "hit bottom"); } } } };


Esto se desplazará hacia abajo en su lista hasta la última entrada.

ListView listView = new ListView(this); listView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); listView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL); listView.setStackFromBottom(true);


Fue muy doloroso lidiar con el desplazamiento, detectar cuándo terminó y, de hecho, está en la parte inferior de la lista (no en la parte inferior de la pantalla visible) y activa mi servicio solo una vez para obtener datos de la web. Sin embargo, está funcionando bien ahora. El código es el siguiente para el beneficio de cualquier persona que enfrenta la misma situación.

NOTA: tuve que mover mi código relacionado con el adaptador a onViewCreated en lugar de onCreate y detectar el desplazamiento principalmente así:

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {} public void onScrollStateChanged(AbsListView view, int scrollState) { if (getListView().getLastVisiblePosition() == (adapter.getCount() - 1)) if (RideListSimpleCursorAdapter.REACHED_THE_END) { Log.v(TAG, "Loading more data"); RideListSimpleCursorAdapter.REACHED_THE_END = false; Intent intent = new Intent(getActivity().getApplicationContext(), FindRideService.class); getActivity().getApplicationContext().startService(intent); } }

Aquí, RideListSimpleCursorAdapter.REACHED_THE_END es una variable adicional en mi SimpleCustomAdapter, que se establece así:

if (position == getCount() - 1) { REACHED_THE_END = true; } else { REACHED_THE_END = false; }

Solo cuando ambas condiciones se cumplan, significa que de hecho estoy en la parte inferior de la lista, y que mi servicio se ejecutará solo una vez. Si no capturo REACHED_THE_END, incluso desplazar hacia atrás activa nuevamente el servicio, siempre que el último elemento esté a la vista.


Janwilx72 tiene razón, pero es min sdk tiene 21, así que creo este método:

private boolean canScrollList(@ScrollOrientation int direction, AbsListView listView) { final int childCount = listView.getChildCount(); if (childCount == 0) { return false; } final int firstPos = listView.getFirstVisiblePosition(); final int paddingBottom = listView.getListPaddingBottom(); final int paddingTop = listView.getListPaddingTop(); if (direction > 0) { final int lastBottom = listView.getChildAt(childCount - 1).getBottom(); final int lastPos = firstPos + childCount; return lastPos < listView.getChildCount() || lastBottom > listView.getHeight() - paddingBottom; } else { final int firstTop = listView.getChildAt(0).getTop(); return firstPos > 0 || firstTop < paddingTop; } }

para ScrollOrientation:

protected static final int SCROLL_UP = -1; protected static final int SCROLL_DOWN = 1; @Retention(RetentionPolicy.SOURCE) @IntDef({SCROLL_UP, SCROLL_DOWN}) protected @interface Scroll_Orientation{}

Quizás tarde, solo para los que se despiertan tarde.


La forma en que lo hice:

listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && (listView.getLastVisiblePosition() - listView.getHeaderViewsCount() - listView.getFooterViewsCount()) >= (adapter.getCount() - 1)) { // Now your listview has hit the bottom } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } });


No puedo comentar aún porque no tengo suficiente reputación, pero en la respuesta de @Ali Imran y @Wroclai, creo que falta algo. Con ese trozo de código, una vez que actualice preLast, nunca ejecutará el Log nuevamente. En mi problema específico, quiero ejecutar alguna operación cada vez que me desplazo hacia abajo, pero una vez que preLast se actualiza a LastItem, esa operación nunca se ejecuta nuevamente.

private int preLast; // Initialization stuff. yourListView.setOnScrollListener(this); // ... ... ... @Override public void onScroll(AbsListView lw, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) { switch(lw.getId()) { case android.R.id.list: // Make your calculation stuff here. You have all your // needed info from the parameters of this function. // Sample calculation to determine if the last // item is fully visible. final int lastItem = firstVisibleItem + visibleItemCount; if(lastItem == totalItemCount) { if(preLast!=lastItem){ //to avoid multiple calls for last item Log.d("Last", "Last"); preLast = lastItem; } } else { preLast = lastItem; }

}

Con ese "else" ahora puede ejecutar su código (Log, en este caso) cada vez que se desplaza hacia abajo una vez más.


Para ampliar un poco en una de las respuestas anteriores, esto es lo que tenía que hacer para que funcione completamente. Parece haber aproximadamente 6dp de relleno interno dentro de ListViews, y onScroll () se estaba llamando cuando la lista estaba vacía. Esto maneja ambas cosas. Probablemente podría optimizarse un poco, pero está escrito más por claridad.

Nota al margen: he probado varias técnicas diferentes de conversión de dp a pixel, y este dp2px () ha sido el mejor.

myListView.setOnScrollListener(new OnScrollListener() { public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (visibleItemCount > 0) { boolean atStart = true; boolean atEnd = true; View firstView = view.getChildAt(0); if ((firstVisibleItem > 0) || ((firstVisibleItem == 0) && (firstView.getTop() < (dp2px(6) - 1)))) { // not at start atStart = false; } int lastVisibleItem = firstVisibleItem + visibleItemCount; View lastView = view.getChildAt(visibleItemCount - 1); if ((lastVisibleItem < totalItemCount) || ((lastVisibleItem == totalItemCount) && ((view.getHeight() - (dp2px(6) - 1)) < lastView.getBottom())) ) { // not at end atEnd = false; } // now use atStart and atEnd to do whatever you need to do // ... } } public void onScrollStateChanged(AbsListView view, int scrollState) { } }); private int dp2px(int dp) { return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); }


Para detectar si el último elemento es completamente visible , puede agregar el cálculo en el último elemento visible de la última vista por lastItem.getBottom() .

yourListView.setOnScrollListener(this); @Override public void onScroll(AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) { int vH = view.getHeight(); int topPos = view.getChildAt(0).getTop(); int bottomPos = view.getChildAt(visibleItemCount - 1).getBottom(); switch(view.getId()) { case R.id.your_list_view_id: if(firstVisibleItem == 0 && topPos == 0) { //TODO things to do when the list view scroll to the top } if(firstVisibleItem + visibleItemCount == totalItemCount && vH >= bottomPos) { //TODO things to do when the list view scroll to the bottom } break; } }


Si configura una etiqueta en una vista del último elemento de la vista de lista, más adelante puede recuperar la vista con la etiqueta, si la vista es nula es porque la vista ya no está cargada. Me gusta esto:

private class YourAdapter extends CursorAdapter { public void bindView(View view, Context context, Cursor cursor) { if (cursor.isLast()) { viewInYourList.setTag("last"); } else{ viewInYourList.setTag("notLast"); } } }

luego, si necesita saber si el último elemento está cargado

View last = yourListView.findViewWithTag("last"); if (last != null) { // do what you want to do }


Yo fui con:

@Override public void onScroll(AbsListView listView, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if(totalItemCount - 1 == favoriteContactsListView.getLastVisiblePosition()) { int pos = totalItemCount - favoriteContactsListView.getFirstVisiblePosition() - 1; View last_item = favoriteContactsListView.getChildAt(pos); //do stuff } }


canScrollVertically(int direction) funciona para todas las vistas y parece hacer lo que usted pidió, con menos código que la mayoría de las otras respuestas. Ingrese un número positivo, y si el resultado es falso, estará en la parte inferior.

es decir:

if (!yourView.canScrollVertically(1)) { //you''ve reached bottom }


Editado :

Como he estado investigando sobre este tema en particular en una de mis aplicaciones, puedo escribir una respuesta extendida para futuros lectores de esta pregunta.

Implemente un OnScrollListener , establezca ListView en onScrollListener y luego debería poder manejar las cosas correctamente.

Por ejemplo:

private int preLast; // Initialization stuff. yourListView.setOnScrollListener(this); // ... ... ... @Override public void onScroll(AbsListView lw, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) { switch(lw.getId()) { case R.id.your_list_id: // Make your calculation stuff here. You have all your // needed info from the parameters of this function. // Sample calculation to determine if the last // item is fully visible. final int lastItem = firstVisibleItem + visibleItemCount; if(lastItem == totalItemCount) { if(preLast!=lastItem) { //to avoid multiple calls for last item Log.d("Last", "Last"); preLast = lastItem; } } } }


Esto puede ser

@Override public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO Auto-generated method stub if (scrollState == 2) flag = true; Log.i("Scroll State", "" + scrollState); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub if ((visibleItemCount == (totalItemCount - firstVisibleItem)) && flag) { flag = false; Log.i("Scroll", "Ended"); } }


Para que su lista llame cuando la lista llegue al último y si ocurre un error, esto no volverá a llamar a la vista de lista final . Este código también ayudará a este escenario.

@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { final int lastPosition = firstVisibleItem + visibleItemCount; if (lastPosition == totalItemCount) { if (previousLastPosition != lastPosition) { //APPLY YOUR LOGIC HERE } previousLastPosition = lastPosition; } else if(lastPosition < previousLastPosition - LIST_UP_THRESHOLD_VALUE){ resetLastIndex(); } } public void resetLastIndex(){ previousLastPosition = 0; }

donde LIST_UP_THRESHOLD_VALUE puede ser cualquier valor entero (he usado 5) donde su lista se desplaza hacia arriba y mientras regresa al final, esto volverá a llamar al final de la vista de lista.


public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { int lastindex = view.getLastVisiblePosition() + 1; if (lastindex == totalItemCount) { //showing last row if ((view.getChildAt(visibleItemCount - 1)).getTop() == view.getHeight()) { //Last row fully visible } } }


public void onScrollStateChanged(AbsListView view, int scrollState) { if (!view.canScrollList(View.SCROLL_AXIS_VERTICAL) && scrollState == SCROLL_STATE_IDLE) { //When List reaches bottom and the list isn''t moving (is idle) } }

Esto funcionó para mí.