ScrollView에 onScrollListener를 사용할 수 있습니까?


140

내가 사용하고 HorizontalScrollView레이아웃과 나는 스크롤의 시작과 끝 지점에 도달 한 사용자를 식별해야합니다.

들어 ListView나는 a를 시도했다 onScrollListener그리고 스크롤의 시작과 끝 지점을 찾을 수 있습니다.

나는 똑같이하려고했지만 Scrollview불가능한 것 같습니다. 필요한 것을 달성 할 수있는 다른 방법이 있습니까?


3
것이 가능하다. user2695685의 답변을 참조하십시오. 간단히 말해 다음 onStart은 트릭을 수행합니다 hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});.onStart () 어디 hsv에서 HorizontalScrollView작동합니다.
JohnnyLambada 2018 년

유용한 답변을 수락하십시오 .. 그렇지 않으면 자신의 답변을 게시하십시오.
Ranjith Kumar

12
Android에서 ScrollView로 스크롤 이벤트를 감지하는 것이 왜 그렇게 어려운가요? 이것은 견과류 imo입니다.
일함

답변:


389

View 호출의 모든 인스턴스 getViewTreeObserver(). 이제의 인스턴스를 보유 할 때 메소드를 사용하여 인스턴스를 ViewTreeObserver추가 할 수 있습니다 .OnScrollChangedListener()addOnScrollChangedListener()

이 클래스에 대한 자세한 내용은 여기를 참조 하십시오 .

모든 스크롤 이벤트를 알 수 있지만 좌표는 없습니다. 그래도 리스너 를 사용 getScrollY()하거나 getScrollX()리스너 내에서 가져올 수 있습니다 .

scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
    @Override
    public void onScrollChanged() {
        int scrollY = rootScrollView.getScrollY(); // For ScrollView
        int scrollX = rootScrollView.getScrollX(); // For HorizontalScrollView
        // DO SOMETHING WITH THE SCROLL COORDINATES
    }
});

6
이 답변은 올바른 것으로 표시되어야합니다. hsv.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {@Override public void onScrollChanged() {Log.i(TAG,"scroll:"+hsv.getScrollX());}});ONSTART에 어디 () hsvA는 HorizontalScrollView작동합니다. ScrollView에서도 동일하게 작동한다고 생각합니다.
JohnnyLambada 2014 년

3
바로 그거죠. 이것이 가장 좋은 대답입니다. HorizontalScrollView를 확장 할 필요가 없습니다. ScrollView로 방금 테스트했으며 훌륭하게 작동합니다. final ScrollView sv = (ScrollView) findViewById (R.id.scrollViewArticle); sv.getViewTreeObserver (). addOnScrollChangedListener (new ViewTreeObserver.OnScrollChangedListener () {@ public void onScrollChanged () {Log.d ( "onScrollChanged Y", String.valueOf (sv.getScrollY ()));}});
Raphael C


7
답변의 샘플 코드는 메모리 누수를 유발합니다. 이 메서드는 addand가 아니기 set때문에 모든 리스너는 명시 적으로 제거 될 때까지 유지됩니다. 따라서 샘플에서 리스너 구현으로 사용되는 익명 클래스는 참조하는 모든 것, 즉 외부 클래스와 함께 누출됩니다.
Brett Duncavage

7
아마도 멍청한 질문이지만 rootScrollView는 무엇입니까?
Iqbal

49

이것은 매우 유용 할 수 있습니다. NestedScrollView대신에 사용하십시오 ScrollView. 지원 라이브러리 23.1는 도입 OnScrollChangeListenerNestedScrollView. 이런 식으로 할 수 있습니다.

 myScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
        @Override
        public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
            Log.d("ScrollView","scrollX_"+scrollX+"_scrollY_"+scrollY+"_oldScrollX_"+oldScrollX+"_oldScrollY_"+oldScrollY);
            //Do something
        }
    });

2
getViewTreeObserver ()를 사용하여 리스너를 추가했는지 여부를 추적하는 것보다 훨씬 쉽습니다. 감사!
Anthony Mills

그러나 NestedScrollView를 가로 스크롤보기로 사용하는 방법은 무엇입니까? 어떤 자원도 찾을 수 없음
GensaGames

가로 스크롤을 사용하려면 어떻게합니까?
Nikita Axyonov

7
@ dazed'n'confused NestedScrollView는 지원 라이브러리의 일부이며, 그 setOnScrollChangeListener방법에는 최소 버전 요구 사항이 없습니다.
Tom

4
와 혼동하지 View의 ' setOnScrollChangeListenerAPI 레벨 23를 필요로하는
앤더스 Ullnæss에게

18

다음은 스크롤 및 스크롤 종료에 대한 알림을 처리하기 위해 작성한 파생 HorizontalScrollView입니다. 사용자가 스크롤 적극적으로 중지 할 때 제대로 처리 하고 완전히 감속 할 때 사용자가 갈 수 후 :

public class ObservableHorizontalScrollView extends HorizontalScrollView {
    public interface OnScrollListener {
        public void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY);
        public void onEndScroll(ObservableHorizontalScrollView scrollView);
    }

    private boolean mIsScrolling;
    private boolean mIsTouching;
    private Runnable mScrollingRunnable;
    private OnScrollListener mOnScrollListener;

    public ObservableHorizontalScrollView(Context context) {
        this(context, null, 0);
    }

    public ObservableHorizontalScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();

        if (action == MotionEvent.ACTION_MOVE) {
            mIsTouching = true;
            mIsScrolling = true;
        } else if (action == MotionEvent.ACTION_UP) {
            if (mIsTouching && !mIsScrolling) {
                if (mOnScrollListener != null) {
                    mOnScrollListener.onEndScroll(this);
                }
            }

            mIsTouching = false;
        }

        return super.onTouchEvent(ev);
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
        super.onScrollChanged(x, y, oldX, oldY);

        if (Math.abs(oldX - x) > 0) {
            if (mScrollingRunnable != null) {
                removeCallbacks(mScrollingRunnable);
            }

            mScrollingRunnable = new Runnable() {
                public void run() {
                    if (mIsScrolling && !mIsTouching) {
                        if (mOnScrollListener != null) {
                            mOnScrollListener.onEndScroll(ObservableHorizontalScrollView.this);
                        }
                    }

                    mIsScrolling = false;
                    mScrollingRunnable = null;
                }
            };

            postDelayed(mScrollingRunnable, 200);
        }

        if (mOnScrollListener != null) {
            mOnScrollListener.onScrollChanged(this, x, y, oldX, oldY);
        }
    }

    public OnScrollListener getOnScrollListener() {
        return mOnScrollListener;
    }

    public void setOnScrollListener(OnScrollListener mOnEndScrollListener) {
        this.mOnScrollListener = mOnEndScrollListener;
    }

}

1
ViewTreeObserver 메소드를 사용하는 것보다 낫습니다. ListViews 및 ScrollViews와 함께 여러 조각 (예 : 슬라이딩 탭이있는 3 개의 조각) 이로 드 된 활동이 있고 특정보기에 대한 이벤트를 등록해야하기 때문입니다. ViewTreeObserver가 등록 된 경우 활성보기가 아니더라도 이벤트를 발생시킵니다.
Torsten Ojaperv

3
@ZaBlanc-ScrollView를 보유한 내 활동 에서이 클래스와 리스너를 초기화하는 방법을 알려주시겠습니까? 나는 그것을 잘 구현했지만 리스너는 아직 값을 반환하지 않습니다.
Edmond Tamas

이것은 실제로 작동합니다! 그리고 그것을 위해 SDK> 23 필요 없음 :)
Matan Dahan

5

뷰의 스크롤 위치를 알고 싶다면 View 클래스에서 다음 확장 기능을 사용할 수 있습니다.

fun View?.onScroll(callback: (x: Int, y: Int) -> Unit) {
    var oldX = 0
    var oldY = 0
    this?.viewTreeObserver?.addOnScrollChangedListener {
        if (oldX != scrollX || oldY != scrollY) {
            callback(scrollX, scrollY)
            oldX = scrollX
            oldY = scrollY
        }
    }
}

5

NestedScrollView대신 사용할 수 있습니다 ScrollView. 그러나 Kotlin Lambda를 사용할 때 setOnScrollChangeListenerView (API 레벨 23) 대신 NestedScrollView를 원한다는 것을 알 수 없습니다 . 첫 번째 매개 변수를 NestedScrollView로 지정하여이 문제를 해결할 수 있습니다.

nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
    Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}

viewTreeObserver를 사용하는 메소드는 메모리 누수를 유발하고 수동으로 제거해야합니다. 그렇지 않으면 여러 스크롤 관찰자가 추가되므로 이는 허용되는 답변이어야합니다.
Ray Li

1

사용자 정의 ScrollView 클래스를 정의하고 다음과 같이 스크롤 할 때 호출되는 인터페이스를 추가 할 수 있습니다.

public class ScrollChangeListenerScrollView extends HorizontalScrollView {


private MyScrollListener mMyScrollListener;

public ScrollChangeListenerScrollView(Context context) {
    super(context);
}

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

public ScrollChangeListenerScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}


public void setOnMyScrollListener(MyScrollListener myScrollListener){
    this.mMyScrollListener = myScrollListener;
}


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

}

public interface MyScrollListener {
    void onScrollChange(View view,int scrollX,int scrollY,int oldScrollX, int oldScrollY);
}

}

0
    // --------Start Scroll Bar Slide--------
    final HorizontalScrollView xHorizontalScrollViewHeader = (HorizontalScrollView) findViewById(R.id.HorizontalScrollViewHeader);
    final HorizontalScrollView xHorizontalScrollViewData = (HorizontalScrollView) findViewById(R.id.HorizontalScrollViewData);
    xHorizontalScrollViewData.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            int scrollX; int scrollY;
            scrollX=xHorizontalScrollViewData.getScrollX();
            scrollY=xHorizontalScrollViewData.getScrollY();
            xHorizontalScrollViewHeader.scrollTo(scrollX, scrollY);
        }
    });
    // ---------End Scroll Bar Slide---------

모든 사람이 타자를 이해하게하는 코드를 지원하는 설명을 추가해보십시오.
Aniruddha Das

이 코드는 다른 답변 과 매우 유사 합니다.
Sergii
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.