프로그래밍 방식으로 스크롤보기를 특정 편집 텍스트로 스크롤하는 방법이 있습니까?


206

스크롤보기로 매우 긴 활동이 있습니다. 사용자가 작성 해야하는 다양한 필드가있는 양식입니다. 양식의 절반 아래에 확인란이 있으며 사용자가 확인하면보기의 특정 부분으로 스크롤하고 싶습니다. 프로그래밍 방식으로 EditText 객체 (또는 다른 뷰 객체)로 스크롤하는 방법이 있습니까?

또한 X 및 Y 좌표를 사용하여 이것이 가능하다는 것을 알고 있지만 양식이 사용자마다 변경 될 수 있으므로이 작업을 피하고 싶습니다.


3
the form may changed from user to user하지만 당신은 그냥 사용할 수 있습니다mEditView.getTop();
nebkat

1
누군가 CoordinatorLayout 내에서 NestedScrollView를 사용하는 경우 stackoverflow.com/questions/52083678/…을
user1506104

답변:


451
private final void focusOnView(){
        your_scrollview.post(new Runnable() {
            @Override
            public void run() {
                your_scrollview.scrollTo(0, your_EditBox.getBottom());
            }
        });
    }

129
smoothScrollTo (0, your_EditBox.getTop ())을 사용하면 더 나은 결과 (원하는보기로 스크롤하는 smother)가 있지만 그 외에는 큰 답변을 얻었습니다.
Muzikant

18
@ xmenW.K. 짧은 대답 : UI는 할 일을 기반으로 작업을 수행하고 기본적으로 UI 스레드에 알리기 때문에 이전에해야 할 모든 작업을 수행 한 후에는이 작업을 수행 할 수 있습니다 (스크롤) . 기본적으로 스크롤을 대기열에 넣고 요청하기 전에 수행 한 작업의 순서를 고려하여 스레드가 가능한 한 스레드가 수행하도록합니다.
Martin Marconcini

37
새로운 핸들러를 인스턴스화하는 대신 Runnable을 ScrollView 자체에 게시 할 수도 있습니다.your_scrollview.post(...
Sherif elKhatib

getBottom () 대신에 중심을 얻는 방법이 있습니까? 내 사건은 EditText 대신 googleMap입니다.
Zin Win Htet

5
getBottom () 및 getTop ()는 상위에 상대적인
cambunctious

57

보기를 스크롤보기의 중앙으로 스크롤 하려는 경우 Sherif elKhatib의 대답을 크게 향상시킬 수 있습니다 . 이 재사용 가능한 메소드는 뷰를 HorizontalScrollView의 보이는 중심으로 부드럽게 스크롤합니다.

private final void focusOnView(final HorizontalScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int vLeft = view.getLeft();
            int vRight = view.getRight();
            int sWidth = scroll.getWidth();
            scroll.smoothScrollTo(((vLeft + vRight - sWidth) / 2), 0);
        }
    });
}

수직 ScrollView사용

...
int vTop = view.getTop();
int vBottom = view.getBottom();
int sHeight = scroll.getBottom();
scroll.smoothScrollTo(((vTop + vBottom - sHeight) / 2), 0);
...

3
코드를 조정해야합니다. 스크롤 뷰의 너비를 가져 와서 수식을 sv.smoothScrollTo ((((vLeft + vRight) / 2)-(svWidth / 2), 0);로 변경해야합니다.
초등학교

2
그렇지 않으면 scrollView의 뷰가 중앙에 위치하지 않습니다. 벌써 편집 했어
초등학교

오래된 것이 나를 위해 잘 작동했기 때문에 공식이 정확하게 작동하는지 확인 했습니까?
ahmadalibaloch

1
@ Elementary Yours와 ahmads 공식은 동일한 대수 표현의 단순화되고 확장 된 버전이므로 조정할 필요가 없습니다.
k29

1
@ k29 여기에 두 개의 보이는 공식을 언급합니까? 그들은 나에게서 왔으며, 나는 내 의견의 공식을 더 단순화하고 그에 따라 답을 편집했습니다. 그러나 나머지는 모두 원래의 대답과 동일하며 나에게 매우 도움이되었습니다.
초등학교

51

이것은 나를 위해 잘 작동합니다 :

  targetView.getParent().requestChildFocus(targetView,targetView);

public void RequestChildFocus (자식보기, 초점보기)

아이 - 초점을 원하는이 ViewParent의 아이입니다. 이 뷰에는 포커스 된 뷰가 포함됩니다. 실제로 포커스가있는 것은 아닙니다.

집중 -실제로 초점이있는 자녀의 후손


부드러운 스크롤로 점프 종류보기
silentsudo

32

내 의견으로는 주어진 사각형으로 스크롤하는 가장 좋은 방법은 View.requestRectangleOnScreen(Rect, Boolean)입니다. View스크롤하려는 화면에서 호출 하고 화면에 표시하려는 로컬 사각형을 전달해야합니다. 두 번째 매개 변수는 false부드러운 스크롤 및 true즉각적인 스크롤을위한 것이어야합니다 .

final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, false);

1
뷰 페이저 내에서 나를 위해 일한 스크롤보기가 있으며 여기에 언급 된 코드를 사용하여 현재 작동하는 뷰로 스크롤해야합니다.
Pankaj 2016 년

2
분명히 이것은 대답이 받아 들여 져야합니다. scrollview.smoothScrollTo필요한보기가 화면 밖에서 작동하지 않으면 작동하지 않습니다 (sdk 버전 26의 Android 에뮬레이터에서 시도)
Sony

@Michael 포장해야합니까 new Handler().post()?
Johnny Five

@JohnnyFive이 코드를 사용하는 컨텍스트에 따라 다릅니다.
Michael

12

나는 WarrenFaith의 응답을 기반으로 작은 유틸리티 방법을 만들었습니다.이 코드는 스크롤보기에서 해당보기가 이미 표시되어 있는지 고려합니다.

public static void scrollToView(final ScrollView scrollView, final View view) {

    // View needs a focus
    view.requestFocus();

    // Determine if scroll needs to happen
    final Rect scrollBounds = new Rect();
    scrollView.getHitRect(scrollBounds);
    if (!view.getLocalVisibleRect(scrollBounds)) {
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                scrollView.smoothScrollTo(0, view.getBottom());
            }
        });
    } 
}

메소드 scrollBounds의 매개 변수가되어야한다고 생각 합니다 scrollView.getHitRect.
Vijay C


7

내 EditText는 내 ScrollView 내부에 여러 레이어로 중첩되어 있으며 레이아웃 자체는 루트 뷰가 아닙니다. getTop ()과 getBottom ()은 뷰에 포함 된 좌표를보고하는 것처럼 보였으므로 EditText의 부모를 반복하여 ScrollView의 상단에서 EditText의 상단까지의 거리를 계산했습니다.

// Scroll the view so that the touched editText is near the top of the scroll view
new Thread(new Runnable()
{
    @Override
    public
    void run ()
    {
        // Make it feel like a two step process
        Utils.sleep(333);

        // Determine where to set the scroll-to to by measuring the distance from the top of the scroll view
        // to the control to focus on by summing the "top" position of each view in the hierarchy.
        int yDistanceToControlsView = 0;
        View parentView = (View) m_editTextControl.getParent();
        while (true)
        {
            if (parentView.equals(scrollView))
            {
                break;
            }
            yDistanceToControlsView += parentView.getTop();
            parentView = (View) parentView.getParent();
        }

        // Compute the final position value for the top and bottom of the control in the scroll view.
        final int topInScrollView = yDistanceToControlsView + m_editTextControl.getTop();
        final int bottomInScrollView = yDistanceToControlsView + m_editTextControl.getBottom();

        // Post the scroll action to happen on the scrollView with the UI thread.
        scrollView.post(new Runnable()
        {
            @Override
            public void run()
            {
                int height =m_editTextControl.getHeight();
                scrollView.smoothScrollTo(0, ((topInScrollView + bottomInScrollView) / 2) - height);
                m_editTextControl.requestFocus();
            }
        });
    }
}).start();

5

다른 변형은 다음과 같습니다.

scrollView.postDelayed(new Runnable()
{
    @Override
    public void run()
    {
        scrollView.smoothScrollTo(0, img_transparent.getTop());
    }
}, 2000);

또는 post()방법을 사용할 수 있습니다 .


5

나는 이것이 더 나은 대답을하기에는 너무 늦을 수도 있지만 원하는 완벽한 솔루션은 포지셔너와 같은 시스템이어야합니다. 내 말은, 시스템이 Editor 필드의 위치를 ​​지정할 때 UI / UX 규칙처럼 완벽하게 키보드까지 필드를 배치합니다.

아래 코드는 안드로이드 방식을 부드럽게 배치하는 것입니다. 우선 현재 스크롤 포인트를 참조 포인트로 유지합니다. 두 번째는 편집기에 가장 적합한 위치 지정 스크롤 포인트를 찾고이를 위해 맨 위로 스크롤 한 다음 ScrollView 구성 요소가 최상의 위치 지정을 수행하도록 편집기 필드를 요청하는 것입니다. 가차! 우리는 최고의 위치를 ​​배웠습니다. 이제 우리가 할 일은 이전 지점에서 새로 찾은 지점으로 부드럽게 스크롤하는 것입니다. 원하는 경우 smoothScrollTo 대신 scrollTo 를 사용하여 부드러운 스크롤을 생략 할 수 있습니다. .

참고 : 메인 컨테이너 ScrollView는 scrollViewSignup이라는 멤버 필드입니다. 필자의 예제는 가입 화면이기 때문에 많이 알 수 있습니다.

view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(final View view, boolean b) {
            if (b) {
                scrollViewSignup.post(new Runnable() {
                    @Override
                    public void run() {
                        int scrollY = scrollViewSignup.getScrollY();
                        scrollViewSignup.scrollTo(0, 0);
                        final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
                        view.requestRectangleOnScreen(rect, true);

                        int new_scrollY = scrollViewSignup.getScrollY();
                        scrollViewSignup.scrollTo(0, scrollY);
                        scrollViewSignup.smoothScrollTo(0, new_scrollY);
                    }
                });
            }
        }
    });

모든 EditText 인스턴스 에이 블록을 사용 하고이를 스크린 코드와 신속하게 통합 하려는 경우 . 아래와 같이 횡단자를 간단하게 만들 수 있습니다. 이를 위해 기본 OnFocusChangeListener를 focusChangeListenerToScrollEditor 라는 멤버 필드로 만들고 아래와 같이 onCreate 중에 호출합니다.

traverseEditTextChildren(scrollViewSignup, focusChangeListenerToScrollEditor);

그리고 메소드 구현은 다음과 같습니다.

private void traverseEditTextChildren(ViewGroup viewGroup, View.OnFocusChangeListener focusChangeListenerToScrollEditor) {
    int childCount = viewGroup.getChildCount();
    for (int i = 0; i < childCount; i++) {
        View view = viewGroup.getChildAt(i);
        if (view instanceof EditText)
        {
            ((EditText) view).setOnFocusChangeListener(focusChangeListenerToScrollEditor);
        }
        else if (view instanceof ViewGroup)
        {
            traverseEditTextChildren((ViewGroup) view, focusChangeListenerToScrollEditor);
        }
    }
}

여기서 우리가 한 일은 모든 EditText 인스턴스 자식을 리스너에 초점을 맞추도록 만드는 것입니다.

이 솔루션에 도달하기 위해 여기서 모든 솔루션을 확인하고 더 나은 UI / UX 결과를위한 새로운 솔루션을 생성했습니다.

다른 모든 답변 덕분에 많은 영감을 얻었습니다.

3

ScrollView가 ChildView의 직접적인 부모이면 위의 답변이 제대로 작동합니다. ChildView가 ScrollView의 다른 ViewGroup에 래핑되는 경우 View.getTop ()이 부모와 관련된 위치를 가져 오기 때문에 예기치 않은 동작이 발생합니다. 이 경우 다음을 구현해야합니다.

public static void scrollToInvalidInputView(ScrollView scrollView, View view) {
    int vTop = view.getTop();

    while (!(view.getParent() instanceof ScrollView)) {
        view = (View) view.getParent();
        vTop += view.getTop();
    }

    final int scrollPosition = vTop;

    new Handler().post(() -> scrollView.smoothScrollTo(0, scrollPosition));
}

이것은 부모의 입장을 설명 할 때 가장 포괄적 인 솔루션입니다. 게시 해 주셔서 감사합니다!
Gustavo Baiocchi Costa


2

나는 더 우아하고 오류가 발생하기 쉬운 솔루션을 사용했다고 생각합니다.

ScrollView.requestChildRectangleOnScreen

관련된 수학이 없으며 다른 제안 된 솔루션과 달리 위아래로 스크롤을 올바르게 처리합니다.

/**
 * Will scroll the {@code scrollView} to make {@code viewToScroll} visible
 * 
 * @param scrollView parent of {@code scrollableContent}
 * @param scrollableContent a child of {@code scrollView} whitch holds the scrollable content (fills the viewport).
 * @param viewToScroll a child of {@code scrollableContent} to whitch will scroll the the {@code scrollView}
 */
void scrollToView(ScrollView scrollView, ViewGroup scrollableContent, View viewToScroll) {
    Rect viewToScrollRect = new Rect(); //coordinates to scroll to
    viewToScroll.getHitRect(viewToScrollRect); //fills viewToScrollRect with coordinates of viewToScroll relative to its parent (LinearLayout) 
    scrollView.requestChildRectangleOnScreen(scrollableContent, viewToScrollRect, false); //ScrollView will make sure, the given viewToScrollRect is visible
}

현재 변경되는 postDelayed경우를 대비하여 더 안정적으로 만들기 위해 감싸는 것이 좋습니다.ScrollView

/**
 * Will scroll the {@code scrollView} to make {@code viewToScroll} visible
 * 
 * @param scrollView parent of {@code scrollableContent}
 * @param scrollableContent a child of {@code scrollView} whitch holds the scrollable content (fills the viewport).
 * @param viewToScroll a child of {@code scrollableContent} to whitch will scroll the the {@code scrollView}
 */
private void scrollToView(final ScrollView scrollView, final ViewGroup scrollableContent, final View viewToScroll) {
    long delay = 100; //delay to let finish with possible modifications to ScrollView
    scrollView.postDelayed(new Runnable() {
        public void run() {
            Rect viewToScrollRect = new Rect(); //coordinates to scroll to
            viewToScroll.getHitRect(viewToScrollRect); //fills viewToScrollRect with coordinates of viewToScroll relative to its parent (LinearLayout) 
            scrollView.requestChildRectangleOnScreen(scrollableContent, viewToScrollRect, false); //ScrollView will make sure, the given viewToScrollRect is visible
        }
    }, delay);
}

1

안드로이드 소스 코드를 살펴보면 ScrollViewscrollToChild(View)– – 의 멤버 함수가 이미 요청 된 것과 정확히 일치한다는 것을 알 수 있습니다. 안타깝게도이 기능은 일부 모호한 이유로로 표시되어 private있습니다. 해당 함수를 기반으로 다음 함수를 작성 하여 매개 변수로 지정된 첫 번째 ScrollView위 를 찾아 View스크롤하여 ScrollView: 내에 표시되도록합니다 .

 private void make_visible(View view)
 {
  int vt = view.getTop();
  int vb = view.getBottom();

  View v = view;

  for(;;)
     {
      ViewParent vp = v.getParent();

      if(vp == null || !(vp instanceof ViewGroup))
         break;

      ViewGroup parent = (ViewGroup)vp;

      if(parent instanceof ScrollView)
        {
         ScrollView sv = (ScrollView)parent;

         // Code based on ScrollView.computeScrollDeltaToGetChildRectOnScreen(Rect rect) (Android v5.1.1):

         int height = sv.getHeight();
         int screenTop = sv.getScrollY();
         int screenBottom = screenTop + height;

         int fadingEdge = sv.getVerticalFadingEdgeLength();

         // leave room for top fading edge as long as rect isn't at very top
         if(vt > 0)
            screenTop += fadingEdge;

         // leave room for bottom fading edge as long as rect isn't at very bottom
         if(vb < sv.getChildAt(0).getHeight())
            screenBottom -= fadingEdge;

         int scrollYDelta = 0;

         if(vb > screenBottom && vt > screenTop) 
           {
            // need to move down to get it in view: move down just enough so
            // that the entire rectangle is in view (or at least the first
            // screen size chunk).

            if(vb-vt > height) // just enough to get screen size chunk on
               scrollYDelta += (vt - screenTop);
            else              // get entire rect at bottom of screen
               scrollYDelta += (vb - screenBottom);

             // make sure we aren't scrolling beyond the end of our content
            int bottom = sv.getChildAt(0).getBottom();
            int distanceToBottom = bottom - screenBottom;
            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
           }
         else if(vt < screenTop && vb < screenBottom) 
           {
            // need to move up to get it in view: move up just enough so that
            // entire rectangle is in view (or at least the first screen
            // size chunk of it).

            if(vb-vt > height)    // screen size chunk
               scrollYDelta -= (screenBottom - vb);
            else                  // entire rect at top
               scrollYDelta -= (screenTop - vt);

            // make sure we aren't scrolling any further than the top our content
            scrollYDelta = Math.max(scrollYDelta, -sv.getScrollY());
           }

         sv.smoothScrollBy(0, scrollYDelta);
         break;
        }

      // Transform coordinates to parent:
      int dy = parent.getTop()-parent.getScrollY();
      vt += dy;
      vb += dy;

      v = parent;
     }
 }

1

내 해결책은 다음과 같습니다.

            int[] spinnerLocation = {0,0};
            spinner.getLocationOnScreen(spinnerLocation);

            int[] scrollLocation = {0, 0};
            scrollView.getLocationInWindow(scrollLocation);

            int y = scrollView.getScrollY();

            scrollView.smoothScrollTo(0, y + spinnerLocation[1] - scrollLocation[1]);

1
 scrollView.post(new Runnable() {
                    @Override
                    public void run() {
                        scrollView.smoothScrollTo(0, myTextView.getTop());

                    }
                });

내 실제 프로젝트에서 응답합니다.

여기에 이미지 설명을 입력하십시오


0

내 경우에는, 그 아니에요 EditText,의 그 googleMap. 그리고 이것은 성공적으로 작동합니다.

    private final void focusCenterOnView(final ScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int centreX=(int) (view.getX() + view.getWidth()  / 2);
            int centreY= (int) (view.getY() + view.getHeight() / 2);
            scrollView.smoothScrollBy(centreX, centreY);
        }
    });
}

0

Que : 프로그래밍 방식으로 스크롤보기를 특정 편집 텍스트로 스크롤하는 방법이 있습니까?

Ans : recyclerview의 마지막 스크롤보기에 레코드 데이터가 추가되었습니다.

adapter.notifyDataSetChanged();
nested_scroll.setScrollY(more Detail Recycler.getBottom());

프로그래밍 방식으로 스크롤보기를 특정 편집 텍스트로 스크롤하는 방법이 있습니까?


왜 바닥입니까?
안드로이드 개발자

0

다음은 내가 사용하는 것입니다.

int amountToScroll = viewToShow.getBottom() - scrollView.getHeight() + ((LinearLayout.LayoutParams) viewToShow.getLayoutParams()).bottomMargin;
// Check to see if scrolling is necessary to show the view
if (amountToScroll > 0){
    scrollView.smoothScrollTo(0, amountToScroll);
}

뷰의 맨 아래에있는 여백을 포함하여 뷰의 맨 아래를 표시하는 데 필요한 스크롤 양을 가져옵니다.


0

세로 스크롤, 양식에 적합합니다. 대답은 Ahmadalibaloch 가로 스크롤을 기반으로합니다.

private final void focusOnView(final HorizontalScrollView scroll, final View view) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            int top = view.getTop();
            int bottom = view.getBottom();
            int sHeight = scroll.getHeight();
            scroll.smoothScrollTo(0, ((top + bottom - sHeight) / 2));
        }
    });
}

HorizontalScrollView이 방법으로 반드시 사용해야 합니까?
Shahood ul Hassan

0

다음 ObjectAnimator과 같이 사용할 수 있습니다 :

ObjectAnimator.ofInt(yourScrollView, "scrollY", yourView.getTop()).setDuration(1500).start();

0

Sherif의 답변을 바탕으로 다음은 내 유스 케이스에 가장 효과적이었습니다. 주목할만한 변경 사항은 getTop()대신 getBottom()smoothScrollTo()대신 scrollTo().

    private void scrollToView(final View view){
        final ScrollView scrollView = findViewById(R.id.bookmarksScrollView);
        if(scrollView == null) return;

        scrollView.post(new Runnable() {
            @Override
            public void run() {
                scrollView.smoothScrollTo(0, view.getTop());
            }
        });
    }

-1

scrlMain이 NestedScrollView 인 경우 다음을 사용하십시오.

scrlMain.post(new Runnable() {
                    @Override
                    public void run() {
                        scrlMain.fullScroll(View.FOCUS_UP);

                    }
                });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.