code

ListView가 맨 아래로 스크롤되었는지 확인 하시겠습니까?

codestyles 2020. 8. 17. 08:57
반응형

ListView가 맨 아래로 스크롤되었는지 확인 하시겠습니까?


내 ListView가 맨 아래로 스크롤되는지 확인할 수 있습니까? 즉, 마지막 항목이 완전히 표시됩니다.


편집 :

내 응용 프로그램 중 하나 에서이 특정 주제에 대해 조사했기 때문에이 질문에 대한 향후 독자를 위해 확장 답변을 작성할 수 있습니다.

을 구현 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;
                }
            }
    }
}

늦은 답변이지만 이벤트 리스너를 만들지 않고 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

}

내가 한 방식 :

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");
                }
            }

스크롤링을 처리하고 완료되는 시점을 감지하고 실제로 목록의 맨 아래 (표시된 화면의 맨 아래가 아님)에 있는지 감지하고 웹에서 데이터를 가져 오기 위해 내 서비스를 한 번만 트리거하는 것은 매우 고통 스러웠습니다. 그러나 지금은 잘 작동합니다. 같은 상황에 처한 모든 사람을 위해 코드는 다음과 같습니다.

참고 : 어댑터 관련 코드를 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;
    }

이 두 조건이 모두 충족되는 경우에만 목록 맨 아래에 있으며 서비스가 한 번만 실행된다는 의미입니다. REACHED_THE_END를 잡지 못하면 마지막 항목이 보이는 한 뒤로 스크롤해도 서비스가 다시 트리거됩니다.


canScrollVertically(int direction)모든 뷰에서 작동하며 대부분의 다른 답변보다 적은 코드로 요청한 것을 수행하는 것 같습니다. 양수를 입력하고 결과가 거짓이면 맨 아래에있는 것입니다.

즉 :

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


위의 답변 중 하나를 조금 확장하려면 이것이 완전히 작동하도록해야했습니다. ListViews 내부에 약 6dp의 내장 패딩이있는 것으로 보이며 목록이 비어있을 때 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"를 사용하면 다시 한 번 아래로 스크롤 할 때마다 코드 (이 경우 로그)를 실행할 수 있습니다.


목록이 마지막에 도달했을 때 목록을 호출하고 오류가 발생하면 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
    }
}

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
            }
        }
    }

listview 스크롤 끝을 아래쪽으로 감지하는 더 좋은 방법을 찾고, 먼저이 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! ***/
    }
}

마지막으로 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");
                }


            }
        }

    };

stackoverflow의 포스터 덕분입니다! 몇 가지 아이디어를 결합하고 활동 및 조각에 대한 클래스 리스너를 만들었습니다 (따라서이 코드는 재사용 가능하므로 코드를 더 빠르게 작성하고 훨씬 더 깔끔하게 만들 수 있습니다).

내 클래스를 받았을 때해야 할 일은 내 클래스에 선언 된 인터페이스를 구현하고 (물론이를위한 메서드를 생성) 인수를 전달하는이 클래스의 객체를 만드는 것입니다.

/**
* 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에 빈 xml 바닥 글 리소스를 추가하고이 바닥 글이 표시되는지 감지해야합니다.

    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 메소드는 목록이 마지막 항목으로 스크롤 된시기를 감지합니다. 또한 어댑터가 이미 마지막 뷰를 렌더링 한 후에도 일부 이전 위치가 호출되는 드문 경우에 대한 수정을 추가합니다.


마지막 항목까지 목록을 아래로 스크롤합니다.

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);

참고 URL : https://stackoverflow.com/questions/5123675/find-out-if-listview-is-scrolled-to-the-bottom

반응형