阻止ScrollView自动滚动到EditText

52

似乎这是一个常见的问题,而我尚未找到一个很好的解决方案。目标是防止ScrollView自动滚动到已聚焦的EditText(或任何其他视图)。

您在一个ScrollView中有许多视图(如ButtonTextView等),其中一个是EditText。在ScrollView内点击一个按钮时,ScrollView会向下滚动到EditText(它在屏幕外)。但这不是期望的结果,因为还有其他元素不希望滚动到屏幕之外。

现在,当屏幕首次显示时,我可以通过在ScrollView中添加其他可聚焦元素来阻止此情况发生。然而,一般问题仍然存在。用户手动向下滚动到EditText,输入一些数字,然后向上滚动到顶部(EditText现在已经不在屏幕上),他们在ScrollView中点击一个按钮,你猜怎么着?ScrollView又会滚动到那个可恶的EditText

我正在考虑扩展ScrollView并覆盖一些方法,例如findFocusableViewInBounds,但我觉得这只会让自己陷入更多麻烦。

如果您能帮忙,敬请赐教。

我尝试过各种方法,例如在我的ScrollView顶部添加高度为0的EditText、将其他项目中的Next Focusable属性添加到ScrollView中等。也许一个“hack”的方法是当虚拟或手动键盘隐藏或其他情况时使EditText失去焦点。


尝试这个:https://dev59.com/_mkw5IYBdhLWcg3wg6yg - faeze saghafi
22个回答

38

在经过相当一段时间的煎熬后,我找到了一个看起来不太丑陋却能正常工作的解决方案。首先,请确保包含你的 EditText 的任何 ViewGroup(直接)都将其 descendantFocusability 设置为“Before Descendants”,focusable 设置为“true”,focusableInTouchMode 设置为“true”。这不会是 ScrollView 本身,而是内部布局中拥有各种视图的位置。接下来,将一个 onTouchListener 添加到你的 ScrollView 上,在每次触摸时从 EditText 中移除焦点,像这样:

ScrollView scroll = (ScrollView)findViewById(R.id.scrollView1);
scroll.setOnTouchListener(new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (myEditText.hasFocus()) {
            myEditText.clearFocus();
        }
        return false;
    }
});

如果这样不起作用,请告诉我。应该发生的是布局获得焦点,而不是 EditText,因此不应该发生滚动。


3
如果仍然不起作用,请尝试这个解决方案:https://dev59.com/6Wsz5IYBdhLWcg3wHUS0 - Cuong Thai

27
只需在LinearLayout顶部创建一个空视图。
<View android:layout_width="fill_parent" android:id="@+id/focus_view" android:layout_height="0dp" android:focusable="true" android:focusableInTouchMode="true"><requestFocus/></View>

一行代码解决问题


这是一个很好的解决方案!它可以避免屏幕出现可见的焦点视图。不错! - Mark Freeman

11

我曾经遇到过同样的问题。这里有一个技巧,我正在使用它来解决这个问题:

public void onClick(View v) {
    button.requestFocusFromTouch(); //prevents from loosing focus and scrolling view down
    ....
}

1
这似乎是有效的,尽管按钮仍然被突出显示,我想我可以在requestFocusFromTouch()调用之后立即进行clearFocus()调用。 - Fraggle
这是最好的!谢谢。 - Ruchira Randana

9
问题不在Java代码上,而在清单文件上。
在AndroidManifest.xml中,给Activity添加一个属性:
        <activity android:name=".MyActivity" android:windowSoftInputMode="adjustPan"> </activity>

ScrollView在一个活动中时,这个方法是有效的。但是当它包含一个需要滚动的片段时,我使用了在滚动时取消焦点的解决方案 - Diederik

7

通过添加两个参数:

android:focusable="true"
android:focusableInTouchMode="true"

在哪个主要布局中存在。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layMain"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/background"
    android:focusable="true"
    android:focusableInTouchMode="true">

这样EditText就不会自动聚焦。


4

这是我所做的:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" style="@style/measurementTableRowStyle"
    android:focusable="true" android:layout_height="fill_parent">
    <requestFocus></requestFocus>
    <LinearLayout android:id="@+id/linearLayout1"
        android:orientation="horizontal" android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <TextView android:id="@+id/desc_text" android:text="Value : "
            style="@style/attributeNameTextStyle" android:layout_width="wrap_content"
            android:focusable="true" android:layout_height="wrap_content">
            <requestFocus></requestFocus>
        </TextView>

        <TextView style="@style/attributeValueStyle" android:id="@+id/value_text"
            android:text="TextView" android:layout_width="wrap_content"
            android:layout_height="wrap_content"></TextView>
    </LinearLayout>

在这种情况下,你需要通过显式添加 android:focusable="true",使滚动视图内的所有其他视图可聚焦,然后加上 <requestFocus></requestFocus>。我认为这样应该每次都行得通。


我想说的是,在滚动视图中,为了防止自动滚动,您需要明确在视图上请求焦点。 - SaKet
1
这是解决问题的最佳一行代码。我希望管理员能删除其余部分。这一定是他在寻找的东西。在任何你想要在页面加载时获得焦点的结束括号后面加上<requestFocus />,它对我非常有效。 - Mark
1
这确实可以工作,但需要focusableInTouchMode属性,它有一个很大的缺点:您必须点击两次控件才能触发它...因此不是好的解决方案... - Softlion
水平滚动视图的工作,编辑表格行中的最后一个单元格时,位置保持不变。谢谢!需要使用以下代码: android:focusable="true" android:focusableInTouchMode="true" 以及<requestFocus></requestFocus> - Arun Jose

2

thomas88wp的回答https://dev59.com/32025IYBdhLWcg3wKSiP#6486348对我有用。 但是我遇到了两个问题: 1. 滚动时,我想隐藏键盘
2. 我有很多EditText视图,不想为每个视图都编写代码
(由于我是在Fragment中编写而不是在Activity中编写,所以我使用getActivity())

    ScrollView scroll = (ScrollView)view.findViewById(R.id.layout_scroll);
    scroll.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // Check if the view with focus is EditText
            if (getActivity().getCurrentFocus() instanceof EditText)
            {
                EditText ed = (EditText)getActivity().getCurrentFocus();
                if (ed.hasFocus()) {

                    // Hide the keyboard
                    InputMethodManager inputManager = (InputMethodManager)
                            getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); 
                    inputManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(),
                            InputMethodManager.HIDE_NOT_ALWAYS);
                    // Clear the focus
                    ed.clearFocus();
                }
            }
            return false;
        }

    });

2

我修复了这个最可怕的bug,需要注意的是这仅适用于API11之前的版本,因为在那个版本中,他们修改了fling方法以避免出现愚蠢的错误。

旧版fling方法查找下一个它将到达的焦点..但这并不真正有帮助。此类的其他版本并没有真正发挥作用,因为当用户从键盘穿越表单时,它们会停止焦点工作。

public class NonFocusingScrollView extends ScrollView {

    private boolean mBlockRequestFocusOnFling = false;

    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
    public ArrayList<View> getFocusables(int direction) {
        if(mBlockRequestFocusOnFling)
            return new ArrayList<View>();
        return super.getFocusables(direction);
    }

    @Override
    public void requestChildFocus(View child, View focused) {
        if(!mBlockRequestFocusOnFling)
        super.requestChildFocus(child, focused);
    }


    @Override
    public void fling(int velocityY) {
        mBlockRequestFocusOnFling = true;
        super.fling(velocityY);
        mBlockRequestFocusOnFling = false;
    }
}

2
我曾经遇到过类似的问题,最终找到了解决方法。我的滚动视图包含一系列自定义按钮,其后紧跟一个EditText(通常具有焦点,但我不希望它失去焦点)。每当按钮被点击时,滚动视图会自动滚动到聚焦的EditText。重写public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate)并始终返回falseViewGroup的默认行为)可以解决问题。希望这对你的情况也有所帮助。

1
我经常在我的应用程序处理方向更改时遇到这个问题。
在这种情况下,我使用以下类型的代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // to avoid the scrollview to scroll to this element automatically
    mEditTextSearch.setFocusable(false);
    // Get the saved scroll position
    final int scrolly = savedInstanceState.getInt("scrolly");
    mScrollView.post(new Runnable() {
        @Override
        public void run() {
            mScrollView.scrollTo(0, scrolly);
            // Restore the initial state of the EditText
            mEditTextSearch.setFocusable(true);
            mEditTextSearch.setFocusableInTouchMode(true);
            mEditTextSearch.setClickable(true);
        }
    });
    ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接