I have added a scrollview and the subchilds inside the scrollview. At some point i need to scroll to a specific view.
1.
I think I have found more elegant and error prone solution using
ScrollView.requestChildRectangleOnScreen
No math involved, and contrary to other proposed solutions, it will handle correctly scrolling both ways up and down.
void scrollToRow(ScrollView scrollView, LinearLayout linearLayout, TextView textViewToShow) {
Rect textRect = new Rect(); //coordinates to scroll to
textViewToShow.getHitRect(textRect); //fills textRect with coordinates of TextView relative to its parent (LinearLayout)
scrollView.requestChildRectangleOnScreen(linearLayout, textRect, false); //ScrollView will make sure, the given textRect 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
private void scrollToRow(final ScrollView scrollView, final LinearLayout linearLayout, final TextView textViewToShow) {
long delay = 100; //delay to let finish with possible modifications to ScrollView
scrollView.postDelayed(new Runnable() {
public void run() {
Rect textRect = new Rect(); //coordinates to scroll to
textViewToShow.getHitRect(textRect); //fills textRect with coordinates of TextView relative to its parent (LinearLayout)
scrollView.requestChildRectangleOnScreen(linearLayout, textRect, false); //ScrollView will make sure, the given textRect is visible
}
}, delay);
}
Just nice isn`t?
If child that we need to scroll to is not a direct child then above solutions don't work
I have used below solution in my project, sharing it may be helpful to others
/**
* Used to scroll to the given view.
*
* @param scrollViewParent Parent ScrollView
* @param view View to which we need to scroll.
*/
private void scrollToView(final ScrollView scrollViewParent, final View view) {
// Get deepChild Offset
Point childOffset = new Point();
getDeepChildOffset(scrollViewParent, view.getParent(), view, childOffset);
// Scroll to child.
scrollViewParent.smoothScrollTo(0, childOffset.y);
}
/**
* Used to get deep child offset.
* <p/>
* 1. We need to scroll to child in scrollview, but the child may not the direct child to scrollview.
* 2. So to get correct child position to scroll, we need to iterate through all of its parent views till the main parent.
*
* @param mainParent Main Top parent.
* @param parent Parent.
* @param child Child.
* @param accumulatedOffset Accumulated Offset.
*/
private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset) {
ViewGroup parentGroup = (ViewGroup) parent;
accumulatedOffset.x += child.getLeft();
accumulatedOffset.y += child.getTop();
if (parentGroup.equals(mainParent)) {
return;
}
getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset);
}
try this:
ScrollView sv = // your scrollview
View insideView = // find the View that you need to scroll to which is inside this ScrollView
sv.scrollTo(0, (int)insideView.getY());
insideView.getY()
will not work below API Level 11, for API Level below 11 you can replace insideView.getY()
with insideView.getTop()
Here rootView is your parent view and childView is a view where you want to scroll your view
public static int getRelativeTop(View rootView, View childView) {
if(childView.getParent() == rootView) return childView.getTop();
else return childView.getTop() + getRelativeTop(rootView, (View) childView.getParent());
}
I use this like a extension
fun ScrollView.focusOnView(toView: View){
Handler().post(Runnable {
this.smoothScrollTo(0, toView.top)
})
}
Usage:
myScrollView.focusOnView(myView)
Here's a solution that works if the target view is not a direct child of your ScrollView:
public int findYPositionInView (View rootView, View targetView)
{
return findYPositionInView (rootView, targetView, 0);
}
// returns -1 if targetView not found
private int findYPositionInView (View rootView, View targetView, int yCumulative)
{
if (rootView == targetView)
return yCumulative;
if (rootView instanceof ViewGroup)
{
ViewGroup parentView = (ViewGroup)rootView;
for (int i = 0; i < parentView.getChildCount (); i++)
{
View child = parentView.getChildAt (i);
int yChild = yCumulative + (int)child.getY ();
int yNested = findYPositionInView (child, targetView, yChild);
if (yNested != -1)
return yNested;
}
}
return -1; // not found
}
Use it like this:
int yScroll = findYPositionInView (scrollView, targetView);
scrollView.scrollTo (0, yScroll);
Further, if you wish to set focus, do this:
targetView.requestFocus ();
And, if you want the keyboard to show, do this:
if (targetView instanceof EditText)
{
targetView.post (new Runnable ()
{
@Override public void run ()
{
InputMethodManager imm = (InputMethodManager)context.getSystemService (Context.INPUT_METHOD_SERVICE);
imm.showSoftInput (targetView, InputMethodManager.SHOW_FORCED);
}
});
}