Is there a way to programmatically scroll a scroll view to a specific edit text?

AndroidViewAndroid EdittextScrollviewProgrammatically Created

Android Problem Overview


I have a very long activity with a scrollview. It is a form with various fields that the user must fill in. I have a checkbox half way down my form, and when the user checks it I want to scroll to a specific part of the view. Is there any way to scroll to an EditText object (or any other view object) programmatically?

Also, I know this is possible using X and Y coords but I want to avoid doing this as the form may changed from user to user.

Android Solutions


Solution 1 - Android

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

Solution 2 - Android

The answer of Sherif elKhatib can be greatly improved, if you want to scroll the view to the center of the scroll view. This reusable method smooth scrolls the view to the visible center of a 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);
		}
	});
}

For a vertical ScrollView use

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

Solution 3 - Android

This works well for me :

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

> public void RequestChildFocus (View child, View focused)

child - The child of this ViewParent that wants focus. This view will contain the focused view. It is not necessarily the view that actually has focus.

focused - The view that is a descendant of child that actually has focus

Solution 4 - Android

In my opinion the best way to scroll to a given rectangle is via View.requestRectangleOnScreen(Rect, Boolean). You should call it on a View you want to scroll to and pass a local rectangle you want to be visible on the screen. The second parameter should be false for smooth scrolling and true for immediate scrolling.

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

Solution 5 - Android

I made a small utility method based on Answer from WarrenFaith, this code also takes in account if that view is already visible in the scrollview, no need for scroll.

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

Solution 6 - Android

You should make your TextView request focus:

    mTextView.requestFocus();

Solution 7 - Android

Another varition would be:

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

or you can use the post() method.

Solution 8 - Android

My EditText was nested several layers inside my ScrollView, which itself isn't the layout's root view. Because getTop() and getBottom() were seeming to report the coordinates within it's containing view, I had it compute the distance from the top of the ScrollView to the top of the EditText by iterating through the parents of the 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();

Solution 9 - Android

The above answers will work fine if the ScrollView is the direct parent of the ChildView. If your ChildView is being wrapped in another ViewGroup in the ScrollView, it will cause unexpected behavior because the View.getTop() get the position relative to its parent. In such case, you need to implement this:

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

Solution 10 - Android

I know this may be too late for a better answer but a desired perfect solution must be a system like positioner. I mean, when system makes a positioning for an Editor field it places the field just up to the keyboard, so as UI/UX rules it is perfect.

What below code makes is the Android way positioning smoothly. First of all we keep the current scroll point as a reference point. Second thing is to find the best positioning scroll point for an editor, to do this we scroll to top, and then request the editor fields to make the ScrollView component to do the best positioning. Gatcha! We've learned the best position. Now, what we'll do is scroll smoothly from the previous point to the point we've found newly. If you want you may omit smooth scrolling by using scrollTo instead of smoothScrollTo only.

NOTE: The main container ScrollView is a member field named scrollViewSignup, because my example was a signup screen, as you may figure out a lot.

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

If you want to use this block for all EditText instances, and quickly integrate it with your screen code. You can simply make a traverser like below. To do this, I've made the main OnFocusChangeListener a member field named focusChangeListenerToScrollEditor, and call it during onCreate as below.

traverseEditTextChildren(scrollViewSignup, focusChangeListenerToScrollEditor);

And the method implementation is as below.

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

So, what we've done here is making all EditText instance children to call the listener at focus.

To reach this solution, I've checked it out all the solutions here, and generated a new solution for better UI/UX result.

Many thanks to all other answers inspiring me much.

Solution 11 - Android

yourScrollView.smoothScrollTo(0, yourEditText.getTop());

Just Do It ;)

Solution 12 - Android

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

Answering from my practical project.

enter image description here

Solution 13 - Android

I think I have found more elegant and less error prone solution using

> ScrollView.requestChildRectangleOnScreen

There is no math involved, and contrary to other proposed solutions, it will handle correctly scrolling both up and down.

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

It is a good idea to wrap it into postDelayed to make it more reliable, in case the ScrollView is being changed at the moment

/**
 * 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);
}

Solution 14 - Android

reference : https://stackoverflow.com/a/6438240/2624806

Following worked far better.

mObservableScrollView.post(new Runnable() {
			public void run() { 
				mObservableScrollView.fullScroll([View_FOCUS][1]); 
			}
		});

Solution 15 - Android

Examining Android source code, you can find that there already is a member function of ScrollViewscrollToChild(View) – that does exactly what is requested. Unfortunatelly, this function is for some obscure reason marked private. Based on that function I've written following function that finds the first ScrollView above the View specified as a parameter and scrolls it so that it becomes visible within the 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;
     }
 }

Solution 16 - Android

My solution is:

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

Solution 17 - Android

Vertical scroll, good for forms. Answer is based on Ahmadalibaloch horizontal scroll.

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

Solution 18 - Android

You can use ObjectAnimator like this:

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

Solution 19 - Android

In my case, that's not EditText, that's googleMap. And it works successfully like this.

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

Solution 20 - Android

Que:Is there a way to programmatically scroll a scroll view to a specific edittext?

Ans:Nested scroll view in recyclerview last position added record data.

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

https://stackoverflow.com/questions/6831671/is-there-a-way-to-programmatically-scroll-a-scroll-view-to-a-specific-edit-text

Solution 21 - Android

The following is what I'm using:

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

This gets the scroll amount necessary to show the bottom of the view, including any margin on the bottom of that view.

Solution 22 - Android

Based on Sherif's answer, the following worked best for my use case. Notable changes are getTop() instead of getBottom() and smoothScrollTo() instead of 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());
            }
        });
    }

Solution 23 - Android

If you want to scroll to a view when a soft keyboard is opened, then it might get a bit tricky. The best solution I've got so far is to use a combination of inset callbacks and requestRectangleOnScreen method.

First, you need to setup inset callbacks:

fun View.doOnApplyWindowInsetsInRoot(block: (View, WindowInsetsCompat, Rect) -> Unit) {
    val initialPadding = recordInitialPaddingForView(this)
    val root = getRootForView(this)
    ViewCompat.setOnApplyWindowInsetsListener(root) { v, insets ->
        block(v, insets, initialPadding)
        insets
    }
    requestApplyInsetsWhenAttached()
}

fun View.requestApplyInsetsWhenAttached() {
    if (isAttachedToWindow) {
        requestApplyInsets()
    } else {
        addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
            override fun onViewAttachedToWindow(v: View) {
                v.removeOnAttachStateChangeListener(this)
                v.requestApplyInsets()
            }

            override fun onViewDetachedFromWindow(v: View) = Unit
        })
    }
}

We are setting a callback on a root view to make sure we get called. Insets could be consumed before our view in question received them, so we have to do additional work here.

Now it's almost easy:

doOnApplyWindowInsetsInRoot { _, _, _ ->
    post {
        if (viewInQuestion.hasFocus()) {
            requestRectangleOnScreen(Rect(0, 0, width, height))
        }
    }
}

You can get rid of a focus check. It's there to limit number of calls to requestRectangleOnScreen. I use post to run an action after scrollable parent scheduled scroll to a focused view.

Solution 24 - Android

If anybody is looking for a Kotlin version you can do this with an extension function

fun ScrollView.scrollToChild(view: View, onScrolled: (() -> Unit)? = null) {
    view.requestFocus()
    val scrollBounds = Rect()
    getHitRect(scrollBounds)
    if (!view.getLocalVisibleRect(scrollBounds)) {
        findViewTreeLifecycleOwner()?.lifecycleScope?.launch(Dispatchers.Main) {
            smoothScrollTo(0, view.bottom - 40)
            onScrolled?.invoke()
        }
    }
}

There is a little callback that lets you do something after the scroll.

Solution 25 - Android

If scrlMain is your NestedScrollView, then use the following,

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

                    }
                });

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNotACleverManView Question on Stackoverflow
Solution 1 - AndroidSherif elKhatibView Answer on Stackoverflow
Solution 2 - AndroidahmadalibalochView Answer on Stackoverflow
Solution 3 - AndroidSwas_99View Answer on Stackoverflow
Solution 4 - AndroidMichaelView Answer on Stackoverflow
Solution 5 - AndroidTobrunView Answer on Stackoverflow
Solution 6 - AndroidOvidiu LatcuView Answer on Stackoverflow
Solution 7 - AndroidGoran Horia MihailView Answer on Stackoverflow
Solution 8 - AndroidInjured PlatypusView Answer on Stackoverflow
Solution 9 - AndroidSam FisherView Answer on Stackoverflow
Solution 10 - AndroidSuphi ÇEVİKER View Answer on Stackoverflow
Solution 11 - AndroidAlireza NooraliView Answer on Stackoverflow
Solution 12 - AndroidXpressGeekView Answer on Stackoverflow
Solution 13 - AndroidŠtarkeView Answer on Stackoverflow
Solution 14 - AndroidCoDeView Answer on Stackoverflow
Solution 15 - AndroidJaromír AdamecView Answer on Stackoverflow
Solution 16 - Androidw4ksklView Answer on Stackoverflow
Solution 17 - AndroidQamarView Answer on Stackoverflow
Solution 18 - AndroidTazeem SiddiquiView Answer on Stackoverflow
Solution 19 - AndroidZin Win HtetView Answer on Stackoverflow
Solution 20 - AndroidRavi BhalalaView Answer on Stackoverflow
Solution 21 - AndroidLukeWaggonerView Answer on Stackoverflow
Solution 22 - AndroiddatchungView Answer on Stackoverflow
Solution 23 - AndroidBogdan KornevView Answer on Stackoverflow
Solution 24 - AndroidOhhhThatVarunView Answer on Stackoverflow
Solution 25 - AndroidAshwin BalaniView Answer on Stackoverflow