Yanıtlar:
Düzenlendi :
Uygulamalarımdan birinde bu konuyu araştırdığım için, bu sorunun gelecekteki okuyucuları için genişletilmiş bir cevap yazabilirim.
Bir Uygulamak OnScrollListener, kümenizle ListView's onScrollListenerve daha sonra doğru şeyler işlemek için mümkün olmalıdır.
Örneğin:
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;
}
}
}
}
listviewDİR stackFromBottom? Denedim if (0 == firstVisibleItem){//listviewtop}ama bu defalarca aranıyor.
Geç cevap, ancak sadece ListView'ınızın tamamen aşağı kaydırılıp kaydırılmadığını kontrol etmek istiyorsanız, bir olay dinleyicisi oluşturmadan, bu if-ifadesini kullanabilirsiniz:
if (yourListView.getLastVisiblePosition() == yourListView.getAdapter().getCount() -1 &&
yourListView.getChildAt(yourListView.getChildCount() - 1).getBottom() <= yourListView.getHeight())
{
//It is scrolled all the way down here
}
İlk olarak, olası son konumun görünüp görünmediğini kontrol eder. Ardından, son düğmenin altının ListView'ün alt kısmıyla hizalı olup olmadığını kontrol eder. En tepede olup olmadığını bilmek için benzer bir şey yapabilirsiniz:
if (yourListView.getFirstVisiblePosition() == 0 &&
yourListView.getChildAt(0).getTop() >= 0)
{
//It is scrolled all the way up here
}
getChildCount()görünüm grubundaki görünümleri döndürdüğünü düşünüyorum , bu görünüm geri dönüşümüyle bağdaştırıcıdaki öğe sayısıyla aynı değil. Ancak ListView, AdapterView'dan indiğinden, getCount()doğrudan ListView üzerinde kullanabilirsiniz .
Yaptığım yol:
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) {
}
});
Etkisi için bir şey:
if (getListView().getLastVisiblePosition() == (adapter.items.size() - 1))
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)
}
}
Bu benim için çalıştı.
Bu olabilir
@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");
}
}
Kaydırma ile uğraşmak, ne zaman bittiğini tespit etmek oldukça acı vericiydi ve aslında listenin en altında (görünen ekranın altında değil) ve web'den veri almak için hizmetimi yalnızca bir kez tetikliyor. Ancak şimdi iyi çalışıyor. Aynı durumla karşılaşan herkesin yararına olan kod aşağıdaki gibidir.
NOT: Adaptörle ilgili kodumu onCreate yerine onViewCreated'e taşımak zorunda kaldım ve öncelikle şu şekilde kaydırmayı algıladım:
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);
}
}
Burada RideListSimpleCursorAdapter.REACHED_THE_END, SimpleCustomAdapter cihazımda şu şekilde ayarlanan ek bir değişkendir:
if (position == getCount() - 1) {
REACHED_THE_END = true;
} else {
REACHED_THE_END = false;
}
Yalnızca bu koşulların her ikisi de karşılandığında, bu gerçekten listenin en altında olduğum ve hizmetimin yalnızca bir kez çalışacağı anlamına gelir. REACHED_THE_END'yi yakalayamazsam, geriye doğru kaydırmak bile, son öğe görünümde olduğu sürece hizmeti tekrar tetikler.
Yukarıdaki cevaplardan birini biraz genişletmek için, tam olarak çalışmasını sağlamak için yapmam gereken şey buydu. ListViews içinde yaklaşık 6dp yerleşik dolgu var gibi görünüyor ve liste boşken onScroll () çağrılıyordu. Bu, her ikisini de halleder. Muhtemelen biraz optimize edilebilir, ancak daha net olması için yazılmıştır.
Yan not: Birkaç farklı dp - piksel dönüştürme tekniği denedim ve bu dp2px () en iyisiydi.
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());
}
Henüz yorum yapamam çünkü yeterince itibarım yok ama @Ali Imran ve @ Wroclai'nin cevabında bir şeylerin eksik olduğunu düşünüyorum. Bu kod parçasıyla, preLast'ı bir kez güncellediğinizde, Günlüğü bir daha asla çalıştırmaz. Benim özel sorunumda, aşağıya her kaydırdığımda bir işlem yapmak istiyorum, ancak preLast LastItem'e güncellendiğinde, bu işlem bir daha asla yürütülmüyor.
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;
}
}
Bu "else" ile, bir kez daha aşağıya her kaydırdığınızda artık kodunuzu (bu durumda Log) çalıştırabilirsiniz.
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
}
}
}
Listeniz, listenin sonuna ulaştığında araması için ve bir hata meydana gelirse, bu durum endoflistview'u tekrar çağırmayacaktır . Bu kod, bu senaryoya da yardımcı olacaktır.
@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;
}
LIST_UP_THRESHOLD_VALUE, listenizin yukarı kaydırıldığı herhangi bir tamsayı değeri olabilir (5 kullandım) ve sona dönerken, bu liste görünümünü yeniden sonlandıracaktır.
Kendi sayfanızı gerektirmeyen bir şekilde sonraki sayfa grubunu otomatik olarak yüklemenin çok güzel bir yolunu buldum ScrollView(kabul edilen cevabın gerektirdiği gibi).
ParseQueryAdapter üzerinde, yüklenecekgetNextPageView daha fazla veri olduğunda listenin sonunda beliren kendi özel görünümünüzü sağlamanıza izin veren bir yöntem vardır, bu nedenle yalnızca mevcut sayfa grubunuzun sonuna ulaştığınızda tetiklenir. (varsayılan olarak "daha fazla yükle .." görünümüdür). Bu yöntem yalnızca yüklenecek daha fazla veri olduğunda çağrılır, bu nedenle aramak için harika bir yerdir loadNextPage(); Bu şekilde bağdaştırıcı, yeni verilerin ne zaman yüklenmesi gerektiğini belirlemek için tüm zor işi sizin için yapar ve eğer varsa hiç çağrılmaz. veri setinin sonuna ulaştı.
public class YourAdapter extends ParseQueryAdapter<ParseObject> {
..
@Override
public View getNextPageView(View v, ViewGroup parent) {
loadNextPage();
return super.getNextPageView(v, parent);
}
}
Daha sonra aktivitenizin / parçanızın içinde sadece adaptörü ayarlamanız gerekir ve yeni veriler sizin için sihir gibi otomatik olarak güncellenir.
adapter = new YourAdapter(getActivity().getApplicationContext());
adapter.setObjectsPerPage(15);
adapter.setPaginationEnabled(true);
yourList.setAdapter(adapter);
Son öğenin tamamen görünür olup olmadığını tespit etmek için , görünümün son görünür öğesinin altına basit bir hesaplama ekleyebilirsiniz 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;
}
}
İle gittim:
@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
}
}
getView()( BaseAdapter-Derived sınıfının) yönteminde , geçerli görünümün konumunun Adapter. Durum buysa, listenin sonuna / sonuna ulaştığımız anlamına gelir:
@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
}
}
ListView kaydırmasını en altta tespit etmenin daha iyi bir yolunu buluyorum, ilk olarak bir ListView'de kaydırmanın sonunu algılamak için bu onScrollListener Uygulamasıyla scoll sonunu tespit ediyorum
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! ***/
}
}
sonunda Martijn'in cevabını birleştir
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");
}
}
}
};
Stackoverflow'daki posterler için çok teşekkürler! Bazı fikirleri birleştirdim ve aktiviteler ve parçalar için sınıf dinleyicisi yarattım (bu nedenle bu kod daha yeniden kullanılabilir, kod yazmayı daha hızlı ve daha temiz hale getiriyor).
Benim sınıfımı aldığınızda yapmanız gereken tek şey, benim sınıfımda bildirilen arayüzü uygulamak (ve tabii ki onun için bir yöntem oluşturmak) ve bu sınıf geçen argümanların nesnesini yaratmaktır.
/**
* 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;
}
}
}
ListView'a boş bir xml altbilgi kaynağı eklemeniz ve bu altbilginin görünür olup olmadığını belirlemeniz gerekir.
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;
}
Sonra listenizde kaydırma dinleyicisini görüntüleyin, bunu yaparsınız
@
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);
}
Liste görünümünün son öğesinin görünümüne bir etiket ayarlarsanız, daha sonra görünümü etiketle alabilirsiniz, görünüm boşsa, bunun nedeni görünüm artık yüklenmemesidir. Bunun gibi:
private class YourAdapter extends CursorAdapter {
public void bindView(View view, Context context, Cursor cursor) {
if (cursor.isLast()) {
viewInYourList.setTag("last");
}
else{
viewInYourList.setTag("notLast");
}
}
}
son öğenin yüklenip yüklenmediğini bilmeniz gerekiyorsa
View last = yourListView.findViewWithTag("last");
if (last != null) {
// do what you want to do
}
Janwilx72 haklı, ancak minimum sdk 21, bu yüzden bu yöntemi oluşturuyorum :
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;
}
}
ScrollOrientation için:
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{}
Belki geç, sadece geç gelenler için。
Liste görünümünüzle özel bir adaptör kullanıyorsanız (çoğu insan bunu yapar!) Burada güzel bir çözüm var!
https://stackoverflow.com/a/55350409/1845404
Bağdaştırıcının getView yöntemi, listenin son öğeye ne zaman kaydırıldığını algılar. Ayrıca, bağdaştırıcı son görünümü zaten oluşturduktan sonra bile daha önceki bir konumun çağrıldığı nadir durumlar için düzeltme ekler.
Bunu yaptım ve benim için çalışıyor:
private void YourListView_Scrolled(object sender, ScrolledEventArgs e)
{
double itemheight = YourListView.RowHeight;
double fullHeight = YourListView.Count * itemheight;
double ViewHeight = YourListView.Height;
if ((fullHeight - e.ScrollY) < ViewHeight )
{
DisplayAlert("Reached", "We got to the end", "OK");
}
}
Bu, listenizi son girişe kaydırır.
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);