ScrollView disable focus move
Asked Answered
W

11

29

I have a simple input form; it's a vertical LinearLayout with EditTexts inside a ScrollView.

<ScrollView android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <LinearLayout android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical">
            <LinearLayout android:layout_width="fill_parent"
                  android:layout_height="wrap_content"
                  android:padding="10dip"
                  android:gravity="center_vertical"
                  android:orientation="horizontal">
                  <TextView style="@style/Text"
                         android:text="Name"/>
                  <EditText style="@style/EditBox"/>
            </LinearLayout>
            <View style="@style/Divider"/>
            <LinearLayout android:layout_width="fill_parent"
                  android:layout_height="wrap_content"
                  android:padding="10dip"
                  android:gravity="center_vertical"
                  android:orientation="horizontal">
                  <TextView style="@style/Text"
                         android:text="Password"/>
                  <EditText style="@style/EditBox"/>
            </LinearLayout>               
            ... 
     </LinearLayout>
</ScrollView>

When the user scrolls the form, it automatically moves its focus to the visible EditText. It is possible to disable such behavior and always keep focus on the EditText currently selected by touch?

I understand that this may be a feature, but I need to disable it.

Thanks!

Weiland answered 21/3, 2011 at 9:36 Comment(5)
Have you tried to play with requestChildFocus()?Gunmaker
No, where appropriate place for play with it?Weiland
It's a ScrollView method. I mean, you can try to define your own ScrollView with overriden requestChildFocus() to handle child focus change. It's just a guess though.Gunmaker
Ok, I will try and tell you about results. Thanks!Weiland
I've tried. We don't have information about how child get focus, only that it take place. So requestChildFocus is not suitable.Weiland
N
49

Just thought I'd share my solution to this. Even though some of the other answer's comments state that you cannot override this behavior, that is not true. This behavior stops as soon as you override the onRequestFocusInDescendants() method. So simply create your ScrollView extension to do this:

public class NonFocusingScrollView extends ScrollView {

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

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

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

  @Override
  protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
    return true;
  }

}

And you're done. The ScrollView will mess with your focus no more.

Necaise answered 18/8, 2011 at 21:10 Comment(6)
Nice, works as expected. Note however, that when using this class in XML, you have to supply the full path (including package name, like com.myapp.stuff.NonFocusingScrollView) - otherwise, it cannot be inflated.Baccalaureate
my EditText Views are still getting focus even when I use this. The onRequestFocusInDescendants method never gets called.Sero
Dunno what to tell you. Maybe there's another attribute on your scrollview that's messing with the focus?Necaise
@musselwhizzle: Depending on how you are using the ScrollView, you may need to override other methods. In my case, I needed to override fullScroll() and pageScroll().Apogee
@Apogee :: I assume that you were overriding fullScroll() because you were actually using the method? What did you do to allow the function to still work properly without moving focus to descendant Views?Livvyy
It might be good to know that this also solved my problem of children (of ScrollView) EditText views receiving focus upon activity start up.Livvyy
D
15

I have had such a problem too. The only way that helped me is to extend scroll view and to override neigher

@Override
public ArrayList<View> getFocusables(int direction) {
    return new ArrayList<View>();
}

or

@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
    return true;
}

but to override ViewGroup.requestChildFocus(View child, View focused) method as following:

    @Override
    public void requestChildFocus(View child, View focused) {
        // avoid scrolling to focused view
        // super.requestChildFocus(child, focused);
    }
Dott answered 23/10, 2012 at 10:55 Comment(2)
The other two didn't work for me either. The problem with this method is that it breaks Activity.getCurrentFocus(). To get around this, inside your requestChildFocus(View child, View focused) method, call super.requestChildFocus(child, child), tricking it into thinking that the newly focused view was already focused, so no scroll needed.Gyromagnetic
Just works, as rus says, if you get the focus from a child you must to remove the swing movement. So works perfect.Foursome
L
11

What worked for me was combining @dmon's and @waj's answers.

Only overriding onRequestFocusInDescendants() worked great when I was only dealing with EditTexts inside of the ScrollView, but when I started added multiple View types, it didn't work so well.

Only overriding getFocusables() did not work at all.

Overriding both onRequestFocusInDescendants() AND getFocusables() seems to work beautifully in all scenarios.

public class FixedFocusScrollView extends ScrollView {

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

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

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

  @Override
  public ArrayList<View> getFocusables(int direction) {
      return new ArrayList<View>();
  }

  @Override
  protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
    return true;
  }

}
Livvyy answered 9/6, 2015 at 16:24 Comment(0)
A
7

I tried all solutions posted here but either didn't work on certain Android versions or it messed up with some other behavior like when switching between touch and non-touch mode (when you click buttons or use the trackpad).

I finally found that overriding the method getFocusables did the trick:

public class FixedFocusScrollView extends ScrollView {

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

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

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

  @Override
  public ArrayList<View> getFocusables(int direction) {
    return new ArrayList<View>();
  }

}
Appliance answered 29/5, 2012 at 22:36 Comment(0)
L
4

Try to cut the problem from the source (edit: i.e. move it to an XML file).

First, there must be a focusable element for that to happen. Make all focusable elements contained in that scroll into non-focusable elements. Make them focusable after the view is inflated and is visible.

android:focusable="false"
android:focusableInTouchMode="false"
Lounge answered 4/4, 2012 at 12:15 Comment(2)
by far the best solution. No need of "hacking" scrollview, simplier and cleaner.Compression
Although this does prevent the ScrollView itself from being brought into focus, it does not prevent its descendants from receiving focus.Livvyy
M
2

This method is very effective,you can overload computeScrollDeltaToGetChildRectOnScreen and return 0

public class NoScrollFocusScrollView extends ScrollView {

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

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

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

    @Override
    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
        return 0;
    }
}

Mobocracy answered 2/9, 2020 at 7:28 Comment(1)
I dunno how to thank you! Your solutions literally saved my life as I need to submit the project to customer in several hours. God bless you and your family!Schaaf
E
1

I found another easy solution that works for my problem.

I got a ScrollView with an EditText at the top, and after it, a big list of TextViews, RatingBars and Buttons. The Buttons launch AlertDialogs, and when they pops up, the ScrollView moves to the top, to the EditText that is the one that still has the focus.

To solve it, I set in the onClick method of the Activity the requestFocusFromTouch() to the view clicked (in this case the Button).

public void onClick(View v) {
    v.requestFocusFromTouch();
    ...
}

So now when I click on a Button, the ScrollView moves and put that Button on the center of the screen, that was just what I wanted.

I hope it help.

Enterpriser answered 29/6, 2012 at 16:24 Comment(1)
This is what worked for me, nice and clean and works on all Android versions.Adorno
K
0

For such behavior use nextFocusUp and nextFocusDown.

<EditText
    android:id="@+id/my_edit_text"
    android:nextFocusDown="@id/my_edit_text"
    android:nextFocusUp="@id/my_edit_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
</EditText>
Karttikeya answered 21/3, 2011 at 10:31 Comment(5)
But if you then want to use a trackball to change the focus?Gunmaker
korovyansk wrote: "I understand, that may be it is a feature, but I need to disable it."Karttikeya
He said about scrolling, not about changing focus directly with trackball ;)Gunmaker
I agree with user639183, it may cause another problem. When a user press 'Next' on soft keyboard, does the focus move to next EditText?Weiland
Unfortunately it's hard coded in ScrollView: codesearch.google.com/codesearch/p?hl=pl#uX1GffpyOZk/core/java/…Karttikeya
A
0

Happened to me today, basically I was adding a View programmatically between some existing views, and Scroll automatically moved to focus that view. What I just did, is the following:

container.setFocusable( false );
Assr answered 28/1, 2014 at 7:59 Comment(0)
W
0

This solved the issue for me. It may not be applicable in all situations, but works well for mine where the scrolling is done programmatically:

View originalFocus = getCurrentFocus();
scroller.fullScroll(View.FOCUS_DOWN);
if (originalFocus!=null) {originalFocus.requestFocusFromTouch();}
Whitefaced answered 23/6, 2020 at 20:11 Comment(0)
I
0

Jack's response was excellent! If you're specifically looking to prevent automatic scrolling for EditTexts only, then this solution is perfect.

override fun computeScrollDeltaToGetChildRectOnScreen(rect: Rect?): Int {
    // Find the focused view
    val focusedView = findFocus()
    // Check if the focused view is an EditText
    if (focusedView is EditText) {
        // Return 0 to prevent automatic scrolling
        return 0
    }
    // Call the super implementation to handle other cases
    return super.computeScrollDeltaToGetChildRectOnScreen(rect)
}
Invertase answered 17/2 at 22:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.