IT박스

ScrollView의 끝 감지

itboxs 2020. 11. 19. 07:58
반응형

ScrollView의 끝 감지


.NET Framework를 사용하는 활동이있는 앱이 ScrollView있습니다. 사용자가 ScrollView. 나는 약간의 googleing을하고 설명 된 이 페이지를 발견 했다. 그러나 예제에서 그 사람은 ScrollView. 말했듯이 활동을 확장해야합니다.

그래서 나는 "좋아, 확장하는 커스텀 클래스를 만들고 ScrollView, onScrollChanged()메소드를 재정의하고 , 스크롤의 끝을 감지하고, 그에 따라 행동 해보자"라고 말했다 .

하지만이 줄에서 :

scroll = (ScrollViewExt) findViewById(R.id.scrollView1);

그것은 java.lang.ClassCastException. <ScrollView>내 XML 태그를 변경 했지만 분명히 작동하지 않습니다. 내 질문은 다음과 같습니다. 왜 ScrollViewExt확장 ScrollView하면 내 얼굴에 던지 ClassCastException나요? 너무 많이 엉망으로 만들지 않고 스크롤의 끝을 감지하는 방법이 있습니까?

감사합니다.

편집 : 약속했듯이 중요한 XML 부분은 다음과 같습니다.

<ScrollView
        android:id="@+id/scrollView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >


        <WebView
            android:id="@+id/textterms"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_horizontal"
            android:textColor="@android:color/black" />

    </ScrollView>

내부 텍스트를 정당화 할 수 있도록 에서 TextView변경했습니다 WebView. 내가 달성하고 싶은 것은 "계약 조건을 완전히 읽을 때까지 수락 버튼이 활성화되지 않는다"는 것입니다. 내 확장 클래스는 ScrollViewExt 라고 합니다. 태그 ScrollView변경 ScrollViewExt하면

android.view.InflateException: Binary XML file line #44: Error inflating class ScrollViewExt

태그를 이해하지 못하기 때문 ScrollViewEx입니다. 해결책이 없다고 생각합니다 ...

답변 해 주셔서 감사합니다!


그것을했다!

Alexandre가 친절하게 제공하는 수정 사항 외에도 인터페이스를 만들어야했습니다.

public interface ScrollViewListener {
    void onScrollChanged(ScrollViewExt scrollView, 
                         int x, int y, int oldx, int oldy);
}

그런 다음 ScrollViewExt의 ScrollView에서 OnScrollChanged 메서드를 재정의해야했습니다.

public class ScrollViewExt extends ScrollView {
    private ScrollViewListener scrollViewListener = null;
    public ScrollViewExt(Context context) {
        super(context);
    }

    public ScrollViewExt(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public ScrollViewExt(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setScrollViewListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (scrollViewListener != null) {
            scrollViewListener.onScrollChanged(this, l, t, oldl, oldt);
        }
    }
}

이제 Alexandre가 말했듯이 패키지 이름을 XML 태그 (내 잘못)에 넣고 내 Activity 클래스가 이전에 만든 인터페이스를 구현하도록 한 다음 모두 합칩니다.

scroll = (ScrollViewExt) findViewById(R.id.scrollView1);
scroll.setScrollViewListener(this);

그리고 인터페이스에서 OnScrollChanged 메서드에서 ...

@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
    // We take the last son in the scrollview
    View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
    int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));

    // if diff is zero, then the bottom has been reached
    if (diff == 0) {
        // do stuff
    }
}

그리고 작동했습니다!

도와 주셔서 대단히 감사합니다, Alexandre!


이것을 감지하는 간단한 방법을 찾았습니다.

   scrollView.getViewTreeObserver()
       .addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                if (scrollView.getChildAt(0).getBottom()
                     <= (scrollView.getHeight() + scrollView.getScrollY())) {
                    //scroll view is at bottom
                } else {
                    //scroll view is not at bottom
                }
            }
        });
  • 사용자 정의 ScrollView가 필요하지 않습니다.
  • Scrollview는 하나의 직계 자식 만 호스트 할 수 있으므로 scrollView.getChildAt (0) 은 괜찮습니다.
  • 이 솔루션은 스크롤 뷰의 직계 자식 높이가 match_parent 또는 wrap_content 일지라도 맞습니다 .

이 모든 답변은 매우 복잡하지만이를 수행하는 간단한 기본 제공 방법이 있습니다. canScrollVertically(int)

예를 들면 :

@Override
public void onScrollChanged() {
    if (!scrollView.canScrollVertically(1)) {
        // bottom of scroll view
    }
    if (!scrollView.canScrollVertically(-1)) {
        // top of scroll view
    }
}

이것은 RecyclerView, ListView 및 실제로 다른 모든 뷰에서도 작동합니다 View.

수평 ScrollView가있는 경우 다음을 사용하여 동일한 결과를 얻을 수 있습니다. canScrollHorizontally(int)


Fustigador 답변은 훌륭했지만 일부 장치 (예 : Samsung Galaxy Note V)는 계산 후 0에 도달 할 수없고 2 포인트가 남았습니다. 아래와 같이 약간의 버퍼를 추가하는 것이 좋습니다.

@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
    // We take the last son in the scrollview
    View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
    int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));

    // if diff is zero, then the bottom has been reached
    if (diff <= 10) {
        // do stuff
    }
}

편집하다

XML 내용으로 ScrollView를 사용하고 있음을 알 수 있습니다. 사용자 지정보기를 사용하려면 com.your.packagename.ScrollViewExt를 작성해야하며 코드에서 사용할 수 있습니다.

<com.your.packagename.ScrollViewExt
    android:id="@+id/scrollView1"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <WebView
        android:id="@+id/textterms"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:textColor="@android:color/black" />

</com.your.packagename.ScrollViewExt>

끝 수정

XML 콘텐츠를 게시 할 수 있습니까?

스크롤 리스너를 추가하고 마지막으로 표시된 항목이 다음과 같이 목록보기에서 가장 최근 항목인지 확인할 수 있다고 생각합니다.

mListView.setOnScrollListener(new OnScrollListener() {      
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub      
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        // TODO Auto-generated method stub
        if(view.getLastVisiblePosition()==(totalItemCount-1)){
            //dosomething
        }
    }
});

지원 라이브러리 NestedScrollViewNestedScrollView.OnScrollChangeListener인터페이스를 사용할 수 있습니다 .

https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

또는 앱이 API 23 이상을 대상으로하는 경우에서 다음 방법을 사용할 수 있습니다 ScrollView.

View.setOnScrollChangeListener(OnScrollChangeListener listener) 

그런 다음 @Fustigador가 그의 답변에서 설명한 예를 따르십시오. 그러나 @Will이 설명했듯이 사용자 나 시스템이 어떤 이유로 든 목록의 전체 맨 아래에 도달 할 수없는 경우 작은 버퍼를 추가하는 것을 고려해야합니다.

또한 스크롤 변경 리스너가 때때로 음수 값 또는 뷰 높이보다 큰 값으로 호출된다는 점도 주목할 가치가 있습니다. 아마도 이러한 값은 스크롤 동작의 '모멘텀'을 나타냅니다. 그러나 적절하게 처리하지 않으면 (바닥 / 복근) 뷰가 범위의 맨 위 또는 맨 아래로 스크롤 될 때 스크롤 방향을 감지하는 데 문제가 발생할 수 있습니다.

https://developer.android.com/reference/android/view/View.html#setOnScrollChangeListener(android.view.View.OnScrollChangeListener)


We should always add scrollView.getPaddingBottom() to match full scrollview height because some time scroll view has padding in xml file so that case its not going to work.

scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            if (scrollView != null) {
               View view = scrollView.getChildAt(scrollView.getChildCount()-1);
               int diff = (view.getBottom()+scrollView.getPaddingBottom()-(scrollView.getHeight()+scrollView.getScrollY()));

          // if diff is zero, then the bottom has been reached
               if (diff == 0) {
               // do stuff
                }
            }
        }
    });

scrollView = (ScrollView) findViewById(R.id.scrollView);

    scrollView.getViewTreeObserver()
            .addOnScrollChangedListener(new 
            ViewTreeObserver.OnScrollChangedListener() {
                @Override
                public void onScrollChanged() {

                    if (!scrollView.canScrollVertically(1)) {
                        // bottom of scroll view
                    }
                    if (!scrollView.canScrollVertically(-1)) {
                        // top of scroll view


                    }
                }
            });

Most of answers works beside a fact, that when u scroll to the bottom, listener is triggered several times, which in my case is undesirable. To avoid this behavior I've added flag scrollPositionChanged that checks if scroll position even changed before calling method once again.

public class EndDetectingScrollView extends ScrollView {
    private boolean scrollPositionChanged = true;

    private ScrollEndingListener scrollEndingListener;

    public interface ScrollEndingListener {
        void onScrolledToEnd();
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        View view = this.getChildAt(this.getChildCount() - 1);
        int diff = (view.getBottom() - (this.getHeight() + this.getScrollY()));
        if (diff <= 0) {
            if (scrollPositionChanged) {
                scrollPositionChanged = false;
                if (scrollEndingListener != null) {
                    scrollEndingListener.onScrolledToEnd();
                }
            }
        } else {
            scrollPositionChanged = true;
        }
    }

    public void setScrollEndingListener(ScrollEndingListener scrollEndingListener) {
        this.scrollEndingListener = scrollEndingListener;
    }
}

Then just set listener

scrollView.setScrollEndingListener(new EndDetectingScrollView.ScrollEndingListener() {
    @Override
    public void onScrolledToEnd() {
        //do your stuff here    
    }
});

You may do the same thing if u do in like

scrollView.getViewTreeObserver().addOnScrollChangedListener(...)

but you have to provide flag from class your adding this listener.


To determine if you are at the end of your custom ScrollView you could also use a member variable and store the last y-position. Then you can compare the last y-position with the current scroll position.

private int scrollViewPos;

...

@Override
public void onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy) {

   //reached end of scrollview
   if (y > 0 && scrollViewPos == y){
      //do something
   }

   scrollViewPos = y;
}

I wanted to show/hide a FAB with an offset before the very bottom of the scrollview. This is the solution I came up with (Kotlin):

scrollview.viewTreeObserver.addOnScrollChangedListener {
    if (scrollview.scrollY < scrollview.getChildAt(0).bottom - scrollview.height - offset) {
        // fab.hide()
    } else {
        // fab.show()
    }
}

참고URL : https://stackoverflow.com/questions/10316743/detect-end-of-scrollview

반응형