Stop ScrollView from setting focus on EditText
Asked Answered
B

6

51

Android's ScrollView (when scrolled or fling'd) likes to set the focus of an EditText when it's one of it's children. It happens when you scroll and then release. Is there anyway to stop this behavior? I've tried nearly everything I can think of and everything I've read on StackOverflow. Nothing has worked for me. Below is an example layout If you'd like to test it out. I'm desperate to stop this dumb behavior.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

<ScrollView 
    android:id="@+id/scrollView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

<LinearLayout 
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_marginRight="50dp"
    android:focusable="true"
    android:focusableInTouchMode="true"
    >

    <EditText 
        android:layout_width="fill_parent" 
        android:layout_height="100dp" 
        android:text="TestApp 1"
        />


    <Button 
        android:id="@+id/btn"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="TestApp 1"
        />

    <Button 
        android:id="@+id/btn"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="TestApp 1"
        />

    <Button 
        android:id="@+id/btn"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="TestApp 1"
        />

    <EditText 
        android:layout_width="fill_parent" 
        android:layout_height="100dp" 
        android:text="Bleh...."
        />

    <Button 
        android:id="@+id/btn"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="TestApp 1"
        />
    <EditText 
        android:layout_width="fill_parent" 
        android:layout_height="100dp" 
        android:text="TestApp 1"
        />


    <Button 
        android:id="@+id/btn"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="TestApp 1"
        />

    <Button 
        android:id="@+id/btn"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="MeeEEEEEEEEEEEEE"
        />

    <Button 
        android:id="@+id/btn"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:text="TestApp 1"
        />



</LinearLayout>
</ScrollView>

</LinearLayout>
Barbiturate answered 11/11, 2011 at 22:39 Comment(0)
B
116

This works. Not sure if it's the best solution or not.

// SCROLL VIEW HACK
    // BOGUS
    ScrollView view = (ScrollView)findViewById(R.id.scrollView);
    view.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
    view.setFocusable(true);
    view.setFocusableInTouchMode(true);
    view.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            v.requestFocusFromTouch();
            return false;
        }
    });
Barbiturate answered 12/11, 2011 at 0:47 Comment(8)
only this piece of code is working as exactly what I wanted.Rebekah
Loved this answerMaishamaisie
After spending hell of time, at last found the solutionLaundryman
I was about 2 hours on this. This works for me. ThanksJourney
Thank stackoverflow for existingHesperidium
This brings focus to the whole ScrollView and also results in a weird change in the background color.Iredale
@Iredale yes i noticed that too... may be a bug? All my cards view are in grey color using dark themeMathematical
@Iredale i found a solution.... instead of v.requestFocusFromTouch(), use v.requestFocus() it still works perfect and the background color isn't affected. Cheers :)Mathematical
S
30

First, you can remove the parent of the ScrollView. Second, add focus options for the child(only one child per ScrollView):

android:descendantFocusability="beforeDescendants"
android:focusable="true"
android:focusableInTouchMode="true"

This is how your xml should look like:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginRight="50dp"
        android:descendantFocusability="beforeDescendants"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:orientation="vertical" >

        <EditText
            android:id="@+id/editText_one"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="TestApp 1" />

        <Button
            android:id="@+id/btn_one"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TestApp 1" />

        <Button
            android:id="@+id/btn_two"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TestApp 1" />

        <Button
            android:id="@+id/btn_three"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TestApp 1" />

        <EditText
            android:id="@+id/editText_two"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="Bleh...." />

        <Button
            android:id="@+id/btn_four"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TestApp 1" />

        <EditText
            android:id="@+id/editText_three"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="TestApp 1" />

        <Button
            android:id="@+id/btn_five"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TestApp 1" />

        <Button
            android:id="@+id/btn_six"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="MeeEEEEEEEEEEEEE" />

        <Button
            android:id="@+id/btn_seven"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TestApp 1" />
    </LinearLayout>

</ScrollView>

Also, notice that you have multiple id's with the same name. Try giving them unique names.


If you want only to hide the soft-keyboard, and let the EditText still have it's focus, you can do this by adding the following property to your activity in manifest:

android:windowSoftInputMode="stateHidden"
Shilling answered 27/1, 2014 at 11:51 Comment(1)
Adding focusable, focusableInTouchMode = true and descendantFocusability to the immediate child of ScrollView is what worked for me. Thanks!Hellman
H
5

ah it is very late. But I still want to add this solution to help newbies like me.

ScrollView sv = (ScrollView)findViewById(R.id.scrollView);
sv.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            sv.clearFocus();
            return false;
        }
    });

yes it works, just set a listener to clearfocus. and it is better than ing requestFocusFromTouch to another view 'x', which will lead to the same problem-- srolling to the view 'x'.

Hertford answered 12/12, 2016 at 7:57 Comment(1)
This is the best answer. If you have a complex view as i had which consist of multiple views inside of scroll view along with recycler view. This clearFocus() clear the focus from the parent and every child view as well. Thanks.. You saved my day. <3Kola
B
3

I just added a scroll listener and got the focused view from the activity and checked if it's visible in the scroll view or if it's scrolled beyond the screen and if it is scrolled beyond the screen, I just clear the focus on the view

setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
            val focusedView = (context as? FragmentActivity)?.currentFocus
            val viewRect = Rect()
            this.getHitRect(viewRect)

            if(focusedView?.getLocalVisibleRect(viewRect) == false) {
                focusedView.clearFocus()
            }
        }
Brewing answered 23/9, 2020 at 19:45 Comment(1)
This is the best answer here in my experience. The others still had some unacceptably strange behavior.Primer
A
0

If none of the answers works, I would suggest to use:

yourView.post {
    yourView.requestFocus()
    scrollView.scrollY = 0
}

This way the scrollView will go back scroll 0 in case you need to scroll to start.

If not then you need to change it to the proper coordinate

Archducal answered 7/6, 2021 at 10:55 Comment(0)
R
0

It maybe too late but for the sake of others who have the same problem :

    scrollView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
    scrollView.setFocusable(true);
    scrollView.setFocusableInTouchMode(true);
    scrollView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    });

Another way is to use NestedScrollView instead of ScrollView. (Recommended)

Racklin answered 17/11, 2021 at 10:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.