Есть ли способ программно прокрутить представление прокрутки до определенного текста редактирования?

206

У меня очень долгая активность с прокруткой. Это форма с различными полями, которые пользователь должен заполнить. У меня есть флажок на полпути вниз по моей форме, и когда пользователь проверяет его, я хочу прокрутить до определенной части представления. Есть ли способ программной прокрутки до объекта EditText (или любого другого объекта просмотра)?

Кроме того, я знаю, что это возможно с использованием координат X и Y, но я хочу избежать этого, так как форма может меняться от пользователя к пользователю.

NotACleverMan
источник
3
the form may changed from user to userно вы можете просто использоватьmEditView.getTop();
небкать
1
если кто-то использует NestedScrollView в CoordinatorLayout, вы можете перейти к определенному представлению с помощью stackoverflow.com/questions/52083678/…
user1506104

Ответы:

452
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 ()) приводит к лучшему результату (см. Прокрутку до нужного представления), но кроме этого - отличный ответ.
Музикант
18
@ xmenW.K. Краткий ответ: поскольку пользовательский интерфейс выполняет свою работу, основываясь на очереди вещей, и вы в основном говорите потоку пользовательского интерфейса, когда можете, после того, как вы сделали все, что должны были сделать до сих пор, сделайте это (прокрутка) , Вы в основном помещаете прокрутку в очередь и позволяете потоку делать это, когда это возможно, соблюдая порядок действий, которые он делал до того, как запросил его.
Мартин Маркончини
37
Также возможно опубликовать Runnable в самом ScrollView вместо создания нового обработчика:your_scrollview.post(...
Sherif elKhatib
Есть ли способ получить центр зрения, а не getBottom ()? Потому что мой случай googleMap вместо EditText.
Зин Вин Хетт
5
getBottom () и getTop () относятся к родителю
cambuncsive
57

Ответ Шерифа эль-Хатиба может быть значительно улучшен, если вы хотите прокрутить вид к центру вида прокрутки . Этот повторно используемый метод плавно прокручивает вид до видимого центра 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);
...
ahmadalibaloch
источник
3
Вы должны настроить свой код. Вы должны получить ширину вида прокрутки и изменить формулу на sv.smoothScrollTo (((vLeft + vRight) / 2) - (svWidth / 2), 0);
Элементарный
2
В противном случае представление в scrollView не будет центрировано. Я уже сделал редактирование для вас.
Элементарный
Вы проверили формулу, работающую точно, потому что старшая работала хорошо для меня
ахмадалибалоч
1
@ Элементарные формулы Yours и ahmads являются упрощенными и расширенными версиями одного и того же алгебраического выражения, нет необходимости в корректировках.
k29
1
@ k29 Вы ссылаетесь на две видимые формулы здесь? Они оба от меня, я еще больше упростил формулу своего комментария и соответственно отредактировал ответ. Но все остальное - то же самое от оригинального ответа и также было очень полезно для меня.
Элементарный
51

Это хорошо работает для меня:

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

public void RequestChildFocus (Просмотреть ребенка, Просмотреть сосредоточен)

child - дочерний элемент этого ViewParent, который хочет сосредоточиться. Это представление будет содержать сфокусированное представление. Это не обязательно точка зрения, которая на самом деле имеет фокус.

сосредоточенный - вид, который является потомком ребенка, который на самом деле имеет фокус

Swas_99
источник
Просматривайте виды прыжков в любой плавной прокрутке
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
Работал для меня в пейджере вида, там есть scrollview и нужно прокрутить до вида, который сейчас работает, используя код, упомянутый здесь.
Панкадж
2
Определенно этот ответ должен быть принят, scrollview.smoothScrollToон не будет работать, если требуемый вид находится за пределами экрана (пробовал в эмуляторе Android с SDK версии 26)
Sony
@ Майкл Должен ли я завернуть new Handler().post()?
Джонни
@JohnnyFive Это зависит от контекста, в котором вы используете этот код.
Майкл
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());
            }
        });
    } 
}
Tobrun
источник
Я думаю, scrollBoundsдолжен быть параметр для scrollView.getHitRectметода.
Виджей C
10

Вы должны TextViewсфокусировать ваш запрос:

    mTextView.requestFocus();
Овидиу Латку
источник
7

Мой EditText был вложен в несколько слоев внутри моего ScrollView, который сам по себе не является корневым представлением макета. Поскольку getTop () и getBottom (), по-видимому, сообщали координаты в пределах содержащего их представления, я сделал так, чтобы он вычислял расстояние от вершины ScrollView до вершины EditText, просматривая родительские элементы 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

Я знаю, что может быть слишком поздно для лучшего ответа, но желаемым идеальным решением должна быть система, подобная позиционеру. Я имею в виду, что когда система делает позиционирование для поля редактора, она помещает поле только на клавиатуре, так что, согласно правилам UI / UX, оно идеально.

То, что приведено ниже, делает способ позиционирования Android плавным. Прежде всего мы сохраняем текущую точку прокрутки в качестве контрольной точки. Во-вторых, чтобы найти лучшую точку прокрутки позиционирования для редактора, для этого мы прокручиваем вверх, а затем запрашиваем поля редактора, чтобы компонент ScrollView делал лучшее позиционирование. Гатча! Мы узнали лучшую позицию. Теперь то, что мы сделаем, - это плавная прокрутка от предыдущей точки до точки, которую мы нашли заново. Если вы хотите, вы можете пропустить плавную прокрутку, используя scrollTo вместо smoothScrollTo только.

ПРИМЕЧАНИЕ . Основным контейнером 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 и быстро интегрировать его с вашим кодом экрана. Вы можете просто сделать traverser как ниже. Для этого я сделал основной 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 переносится в другую ViewGroup в ScrollView, это вызовет неожиданное поведение, потому что 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));
}
Сэм Фишер
источник
Это наиболее полное решение, так как оно учитывает и позиции родителей. Спасибо за публикацию!
Густаво Байокки Коста
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);
}
STARKE
источник
1

Изучение исходного кода Android, вы можете обнаружить , что уже есть функция члена ScrollView- scrollToChild(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]);
w4kskl
источник
1
 scrollView.post(new Runnable() {
                    @Override
                    public void run() {
                        scrollView.smoothScrollTo(0, myTextView.getTop());

                    }
                });

Отвечаю из моего практического проекта.

введите описание изображения здесь

XpressGeek
источник
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: Есть ли способ программно прокрутить представление прокрутки до определенного текста редактирования?

Ответ: Вложенное представление прокрутки в окне просмотра последней позиции добавило данные записи.

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

Есть ли способ программно прокрутить представление прокрутки до определенного текста редактирования?

Рави Бхалала
источник
Почему это дно?
Android-разработчик
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);
}

Получается сумма прокрутки, необходимая для отображения нижней части представления, включая любое поле в нижней части этого представления.

LukeWaggoner
источник
0

Вертикальная прокрутка, хорошо подходит для форм. Ответ основан на горизонтальной прокрутке Ахмадалибалоч.

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

Исходя из ответа Шерифа, для моего варианта использования лучше всего подойдет следующее. Заметные изменения 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());
            }
        });
    }
datchung
источник
-1

Если scrlMain - это ваш NestedScrollView, используйте следующее:

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

                    }
                });
Эшвин Балани
источник