回答:
編集:
私は自分のアプリケーションの1つでこの特定の主題について調査しているので、この質問の将来の読者のために拡張回答を書くことができます。
実装OnScrollListener、あなたの設定ListViewのをonScrollListenerして、あなたは正しく物事を処理できる必要があります。
例えば:
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;
                }
            }
    }
}
listviewIS stackFromBottom?私は試しましたif (0 == firstVisibleItem){//listviewtop}が、それは繰り返し呼び出されています。
                    遅い答えですが、イベントリスナーを作成せずにListViewが下にスクロールされているかどうかを確認したいだけの場合は、次のifステートメントを使用できます。
if (yourListView.getLastVisiblePosition() == yourListView.getAdapter().getCount() -1 &&
    yourListView.getChildAt(yourListView.getChildCount() - 1).getBottom() <= yourListView.getHeight())
{
    //It is scrolled all the way down here
}
最初に、可能な最後の位置が表示されているかどうかを確認します。次に、最後のボタンの下部がListViewの下部に揃っているかどうかを確認します。あなたはそれが一番上にあるかどうかを知るために同様のことをすることができます:
if (yourListView.getFirstVisiblePosition() == 0 &&
    yourListView.getChildAt(0).getTop() >= 0)
{
    //It is scrolled all the way up here
}
getChildCount()が、ビューグループのビューを返します。ビューのリサイクルでは、アダプターのアイテム数と同じではありません。ただし、ListViewはAdapterViewの子孫getCount()であるため、ListViewで直接使用できます。
                    私がやった方法:
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) {
    }
});
以下の影響があるもの:
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)
    }
}
これでうまくいきました。
これは
            @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");
                }
            }
スクロールを処理し、それがいつ終了し、実際にリストの一番下(表示されている画面の一番下ではない)にあるかを検出し、Webからデータをフェッチするためにサービスを1回だけトリガーするのは非常に困難でした。ただし、現在は正常に動作しています。同じ状況に直面している人のために、コードを以下に示します。
注:アダプタ関連のコードをonCreateではなくonViewCreatedに移動し、主に次のようにスクロールを検出する必要がありました。
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);
        }
}
ここでRideListSimpleCursorAdapter.REACHED_THE_ENDは、SimpleCustomAdapterの追加変数で、次のように設定されています。
if (position == getCount() - 1) {
      REACHED_THE_END = true;
    } else {
      REACHED_THE_END = false;
    }
これらの条件の両方が満たされた場合にのみ、私は実際にリストの一番下にあり、私のサービスは1回だけ実行されます。REACHED_THE_ENDをキャッチできない場合、最後にスクロールした場合でも、最後のアイテムが表示されている限り、サービスが再度トリガーされます。
上記の答えの1つを少し拡張するために、これを完全に機能させるために私はしなければなりませんでした。ListViewsの内部には約6 dpの組み込みパディングがあり、リストが空のときにonScroll()が呼び出されていたようです。これはそれらの両方を扱います。それはおそらく少し最適化されるかもしれませんが、明確にするためにもっと書かれています。
補足:私はいくつかの異なるdpからピクセルへの変換技術を試しましたが、このdp2px()が最高です。
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());
}評判が足りないのでコメントはまだできませんが、@ Ali Imranと@Wroclaiの回答では何かが足りないと思います。このコードを使用すると、preLastを更新すると、ログが再度実行されることはありません。私の特定の問題では、一番下までスクロールするたびにいくつかの操作を実行したいのですが、preLastがLastItemに更新されると、その操作が再度実行されることはありません。
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;
}}
その「else」を使用すると、もう一度下にスクロールするたびにコード(この場合はLog)を実行できます。
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
            }
        }
    }リストが最後に到達したときにリストを呼び出すために、エラーが発生した場合、これはendoflistviewを再度呼び出すことはありません。このコードは、このシナリオにも役立ちます。
@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は任意の整数値(私は5を使用しました)にできます。リストは上にスクロールされ、最後に戻るときに、リストの最後のビューが再度呼び出されます。
私は、次のページセットを自動的にロードするための非常に良い方法を見つけましたScrollView(独自の方法を必要としない方法で(受け入れられた回答が必要とするように))。
上ParseQueryAdapterと呼ばれる方法があるgetNextPageViewあなたは、現在のページセットの終わりに達したときにのみトリガーされますので、あなたが負荷に、より多くのデータがリストの最後に表示されていることを独自のカスタムビューを供給できるようにすることがあります(デフォルトでは「もっと読み込む」ビューです)。このメソッドは、ロードするデータが多い場合にのみ呼び出されるため、呼び出すのに最適な場所です。loadNextPage();  このようにして、アダプタは、新しいデータをいつロードする必要があるかを判断するためのすべてのハードワークを実行します。データセットの終わりに達しました。
public class YourAdapter extends ParseQueryAdapter<ParseObject> {
..
@Override
public View getNextPageView(View v, ViewGroup parent) {
   loadNextPage();
   return super.getNextPageView(v, parent);
  }
}次に、アクティビティ/フラグメント内でアダプターを設定するだけで、魔法のように新しいデータが自動的に更新されます。
adapter = new YourAdapter(getActivity().getApplicationContext());
adapter.setObjectsPerPage(15);
adapter.setPaginationEnabled(true);
yourList.setAdapter(adapter);最後のアイテムが完全に表示されているかどうかを検出するには、によってビューの最後に表示されているアイテムの下部に計算を追加し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;
    }
}私は一緒に行きました:
@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派生クラスの)メソッドでは、現在のビューの位置が内のアイテムのリストと等しいかどうかを確認できAdapterます。その場合は、リストの最後または最後に到達したことを意味します。
@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
    }
}私はリストビューのスクロール終了を下部で検出するより良い方法を見つけます。最初に、onScrollListenerのこの実装によってscoll終了
を検出して、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! ***/
    }
}最後にマルティンの答えを組み合わせる
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のポスターに感謝します。いくつかのアイデアを組み合わせて、アクティビティとフラグメントのクラスリスナーを作成しました(このコードは再利用性が高く、コードの作成が速くなり、よりクリーンになりました)。
私のクラスを取得したときに行う必要があるのは、私のクラスで宣言されているインターフェイス(およびもちろんそのためのcreateメソッド)を実装し、引数を渡すこのクラスのオブジェクトを作成することだけです。
/**
* 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;
    }
}
}空のxmlフッターリソースをlistViewに追加し、このフッターが表示されるかどうかを検出する必要があります。
    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;
    }次に、listViewスクロールリスナーでこれを行います
@
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);
}リストビューの最後のアイテムのビューにタグを設定すると、後でタグを使用してビューを取得できます。ビューがnullの場合は、ビューが読み込まれていないためです。このような:
private class YourAdapter extends CursorAdapter {
    public void bindView(View view, Context context, Cursor cursor) {
         if (cursor.isLast()) {
            viewInYourList.setTag("last");
         }
         else{
            viewInYourList.setTag("notLast");
         }
    }
}最後のアイテムがロードされているかどうかを知る必要がある場合
View last = yourListView.findViewWithTag("last");
if (last != null) {               
   // do what you want to do
}Janwilx72は正しいですが、最小SDKは21なので、このメソッドを作成します:
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の場合:
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{}たぶん遅く、遅刻した人のために。
リストビューでカスタムアダプターを使用している場合(ほとんどの人が使用します)、美しいソリューションがここに表示されます!
https://stackoverflow.com/a/55350409/1845404
アダプターのgetViewメソッドは、リストが最後のアイテムまでスクロールされたことを検出します。また、アダプターが既に最後のビューをレンダリングした後でも、以前の位置が呼び出されるというまれな場合の修正を追加します。
私はそれをして私のために働きました:
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");
                }
}