Stop ScrollView from auto-scrolling to an EditText

2019-01-08 08:47发布

Seems to be a common problem without a great solution that I have found. Goal is to stop a ScrollView from auto-scrolling to an EditText (or any view for that matter) that has focus.

You have a bunch of views (Buttons, TextViews, etc) in an ScrollView, one of which is an EditText. Upon clicking say a Button within the ScrollView, the ScrollView scrolls down to the EditText (its off screen). This is not desired, as there are other elements that you don't want scrolled off the screen.

Now I can stop this from happening when the screen first shows by having other focusable elements in the ScrollView. However, the general problem still exists. The user scrolls down manually to the EditText, enters some numbers, then scrolls up to the top (EditText off screen now), they click a button in the ScrollView, and guess what? The ScrollView scrolls down to that darn EditText.

I'm thinking about extending the ScrollView and overriding some of the methods there like findFocusableViewInBounds, but I have a feeling I'll just be getting myself into more trouble.

Please help if you can.

I've played around with things like having an 0 height EditText at the top of my ScrollView, adding Next Focusable element properties to the other items in the ScrollView, etc. I suppose one "hack" might be to get the EditText to lose focus when the virtual or manual keyboard gets hidden or something.

20条回答
ゆ 、 Hurt°
2楼-- · 2019-01-08 09:16

The best Solution is to add focus options for the child of your scrollview :

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

Then your xml file will 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" />

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

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


    </LinearLayout>

</ScrollView>
查看更多
仙女界的扛把子
3楼-- · 2019-01-08 09:19

Just create an empty view at the top of linearlayout

<View android:layout_width="fill_parent" android:id="@+id/focus_view" android:layout_height="0dp" android:focusable="true" android:focusableInTouchMode="true"><requestFocus/></View>

Single line solves the problem

查看更多
姐就是有狂的资本
4楼-- · 2019-01-08 09:19

I made a test project to experiment with the various solutions if anyone wants to play with it. https://github.com/marchold/EditText-ErrorPopup-Scroll-View

查看更多
干净又极端
5楼-- · 2019-01-08 09:23

Try this one :)

public class CustomHorizontalScrollView extends HorizontalScrollView {

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

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

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
    return super.onTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    return super.onInterceptTouchEvent(ev);
    }

    @Override
    public void scrollTo(int x, int y) {
    super.scrollTo(x, y);
    }


    //Custom smooth scroll method since norm is final and cannot be overridden
    public final void smooothScrollToIfEnabled(int x, int y) {
    smoothScrollTo(x, y);
    }

    @Override
    protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect rect) {
/*  if (getContext() != null && getContext() instanceof Activity) {
        Activity activity = (Activity) getContext();
        if (!activity.isFinishing()) {
        View view = activity.getCurrentFocus();
        if (view != null) {
            if (view instanceof EditText) {
            return 0;
            }
        }
        }
    }
    return super.computeScrollDeltaToGetChildRectOnScreen(rect);

*/
return 0;
    }

}
查看更多
我想做一个坏孩纸
6楼-- · 2019-01-08 09:26

I had a slightly different objection to this infuriating deficiency. Whenever I tapped one of a number of RadioButtons below the EditTexts, the scroll position jumped to accommodate what Android determined to be the visible and focused EditText.

All attempts to retain the current desired scroll position via a Runnable that issued ScrollView.scrollTo(x,y) were dutifully IGNORED by Android!

I share my solution in the hope that it may save someone else 8 (eight) wasted hours.

/* This interesting little 'hack' prevents unwanted scroll 'jump' occurring when
 user touches a RadioButton for example
[ Causes focus to change - but maybe this is a lesser evil! ] */
mScrollView.setOnTouchListener(new View.OnTouchListener() {
    @Override public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() != MotionEvent.ACTION_UP) 
            return false;

        mScrollView.clearFocus();
        return false;
    }
});
查看更多
狗以群分
7楼-- · 2019-01-08 09:27

The issue is not on the java code, but on the manifest code.

In your AndroidManifest.xml add an attribute to the Activity:

        <activity android:name=".MyActivity" android:windowSoftInputMode="adjustPan"> </activity>
查看更多
登录 后发表回答