49

似乎是一个常见的问题,但我没有找到一个很好的解决方案。目标是阻止ScrollView自动滚动到EditText具有焦点的(或任何视图)。

您在 an 中有一堆视图(Buttons、TextViews 等)ScrollView,其中一个是 an EditText。单击 中的按钮后ScrollViewScrollView向下滚动到EditText(其关闭屏幕)。这是不希望的,因为您不希望将其他元素滚动到屏幕之外。

现在我可以通过在ScrollView. 但是,普遍的问题仍然存在。用户手动向下滚动到EditText,输入一些数字,然后向上滚动到顶部(EditText现在离开屏幕),他们单击 中的按钮ScrollView,猜猜怎么着?ScrollView向下滚动到那个该死的EditText

我正在考虑扩展ScrollView和覆盖那里的一些方法,例如findFocusableViewInBounds,但我有一种感觉,我只会让自己陷入更多麻烦。

如果可以的话请帮忙。

我玩过一些事情,比如EditText在我的顶部有一个 0 高度ScrollView,将 Next Focusable 元素属性添加到 中的其他项目ScrollView等等。我想一个“黑客”可能是EditText在虚拟或手动键盘被隐藏或其他东西。

4

21 回答 21

38

在为这个问题苦苦挣扎了一段时间之后,我找到了一个似乎可行而又不会太难看的解决方案。首先,确保任何ViewGroup(直接)包含您的内容EditTextdescendantFocusability设置为“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;
    }
});

告诉我这是否解决不了问题。应该发生的是 Layout 获得焦点而不是EditText,因此不应该发生滚动。

于 2011-06-26T19:56:28.127 回答
27

只需在线性布局顶部创建一个空视图

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

单行解决问题

于 2013-07-26T13:01:49.830 回答
10

我有同样的问题。我使用一个技巧来处理这个问题:

public void onClick(View v) {
    button.requestFocusFromTouch(); //prevents from loosing focus and scrolling view down
    ....
}
于 2011-05-30T14:01:28.000 回答
9

问题不在于 java 代码,而在于清单代码。

在您的 AndroidManifest.xml 中向 Activity 添加一个属性:

        <activity android:name=".MyActivity" android:windowSoftInputMode="adjustPan"> </activity>
于 2013-05-28T15:15:12.517 回答
7

通过添加 2 个参数:

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就不会自动聚焦。

于 2016-06-23T05:57:37.770 回答
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>

原因是在这种情况下,您必须通过显式的 then 使所有其他视图在滚动视图内成为android:focusable="true"焦点<requestFocus></requestFocus> 。这应该在每次 IMO 时都有效

于 2011-09-11T05:45:57.947 回答
2

我遇到了类似的问题,终于让它工作了。我的滚动视图包含一系列自定义按钮,后跟一个EditText(通常具有焦点,但我不希望它失去焦点)。每次单击按钮时,滚动视图都会自动滚动到焦点EditText。覆盖public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate)并始终返回false( a 的默认行为ViewGroup)起到了作用。希望它对你的情况也有帮助。

于 2014-10-30T20:24:13.107 回答
2

我对这个最可怕的错误的修复,(值得注意的是,这只是 API11 之前的版本,他们修改了投掷方法以使其不愚蠢)。

旧的投掷方法找到了它将到达的下一个焦点......这并不是那么有帮助。这个类的其他版本并没有真正起作用,因为当用户真正从键盘遍历表单时,它们会停止焦点工作。

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;
    }
}
于 2013-12-04T17:55:08.943 回答
2

thomas88wp 答案,https: //stackoverflow.com/a/6486348/528746为我工作。但是我有两个问题:1.滚动时,我想隐藏键盘
2.我有很多 EditText 视图,不想为每个视图都写它
(我确实 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;
        }

    });
于 2012-08-14T07:06:42.200 回答
1

我们可以编写一个自定义的 ScrollView 并覆盖onScrollChanged方法并从焦点视图中清除焦点并可选择隐藏键盘。

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    View v = getFocusedChild();
    if (v != null) {
        InputMethodManager imm = (InputMethodManager) getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
        v.clearFocus();
    }
    super.onScrollChanged(l, t, oldl, oldt);
}
于 2011-12-30T23:56:38.547 回答
1

如果有人想玩它,我做了一个测试项目来试验各种解决方案。 https://github.com/marchold/EditText-ErrorPopup-Scroll-View

于 2013-12-16T18:15:11.210 回答
1

thomas88wp 代码的另一个版本:

ScrollView scroll = (ScrollView)getActivity().findViewById(R.id.scrollView_addNewBill);
    scroll.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View arg0, MotionEvent arg1) {        
            View focussedView = getCurrentFocus(); 
            if( focussedView != null ) focussedView.clearFocus();
                
            return false;
    }
});
于 2013-04-04T13:17:08.437 回答
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);
        }
    });
    ...
}
于 2012-01-13T18:15:19.947 回答
1

创建一个自定义 ScrollView(创建一个类并让它扩展 Horizo​​ntalScrollView)并为可滚动创建一个 getter setter。然后覆盖 computeScrollDeltaToGetChildRectOnScreen。

工作原理:每当 android 有一个编辑文本或焦点不在屏幕上的东西时,它都会调用方法 computeScrollDeltaToGetChildRectOnScreen 将其显示出来。如果你覆盖它并在它被禁用时返回 0 比它不会滚动......

所以你会有一个像这样的自定义滚动视图:

    public class TrackableHorizontalScrollView extends HorizontalScrollView {


    // true if we can scroll (not locked)
    // false if we cannot scroll (locked)
    private boolean mScrollable = true;

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

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

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

    public void setScrollingEnabled(boolean enabled) {
        mScrollable = enabled;
    }

    public boolean isScrollable() {
        return mScrollable;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // if we can scroll pass the event to the superclass
                if (mScrollable) return super.onTouchEvent(ev);
                // only continue to handle the touch event if scrolling enabled
                return mScrollable; // mScrollable is always false at this point
            default:
                return super.onTouchEvent(ev);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // Don't do anything with intercepted touch events if
        // we are not scrollable
        if (!mScrollable) return false;
        else return super.onInterceptTouchEvent(ev);
    }

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


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

    @Override
    protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect rect){
        if (!mScrollable) return 0;
        return super.computeScrollDeltaToGetChildRectOnScreen(rect);
    }


}

您可以像这样在 XML 中使用它:

<com.your.package.ui.widget.TrackableHorizontalScrollView
            android:id="@+id/wi_et_credit_scroller"
            android:layout_toRightOf="@id/wi_et_credit_iv"
            android:layout_width="fill_parent"
            android:scrollbars="none"
            android:layout_height="wrap_content"
            android:paddingRight="5dp"
            android:layout_gravity="center_vertical">

<!--Whatever you have inside the scrollview-->

</com.your.package.ui.widget.TrackableHorizontalScrollView>
于 2014-02-19T15:25:11.200 回答
1

最好的解决方案是为您的滚动视图的子级添加焦点选项:

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

然后您的 xml 文件将如下所示:

<?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>
于 2016-12-14T06:20:16.490 回答
1

我解决了这个问题,将descendantFocusability属性添加到包含 LinearLayout 的 ScrollView,值为blocksDescendants.

例如:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:descendantFocusability="blocksDescendants" >
于 2019-02-23T21:37:18.430 回答
0

对我来说,覆盖 ScrollView onTouch 是行不通的。也没有工作 android:descendantFocusability="beforeDescendants" android:focusableInTouchMode="true" 这个和另一个提到的解决方案只在第一次工作 - 只有在没有选择 EditText 时,但是一旦你选择它,滚动视图会再次自动滚动。

因为我已经编写了一个代码来在触摸其他视图时隐藏键盘,所以我只添加了两行代码,它就像一个冠军一样工作:

public static void setupUI(final Activity activity, final View view) {
    //view is the parent view in your layout
    OnTouchListener mTouchListener = new OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            try {
                View vFocused = null;
                vFocused = activity.getCurrentFocus();

                if (vFocused != null) {
                    hideSoftKeyboard(activity, v);
                    if (vFocused instanceof EditText) {
                        vFocused.clearFocus();//this is the trick to avoid ScrollView autoscroll
                    }
                }
            } catch (Exception e) {
            }
            return false;
        }
    };

    // Set up touch listener for non-text box views to hide keyboard.
    if (!(view instanceof EditText) && !(view instanceof ViewGroup)) {
        view.setOnTouchListener(mTouchListener);
    }

    // If a layout container, iterate over children and seed recursion.
    if (view instanceof ViewGroup) {
        view.setOnTouchListener(mTouchListener);
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View innerView = ((ViewGroup) view).getChildAt(i);
            setupUI(activity, innerView);
        }
    }
}
public static void hideSoftKeyboard(Context context, View v) {
    InputMethodManager inputMethodManager = (InputMethodManager) context
            .getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}

还在根视图中添加了这个:

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

也许它不是很好的解决方案,但它的工作原理。

于 2014-08-05T07:26:53.203 回答
0

我的解决方案如下,跟踪源代码并覆盖某些功能以停止按焦点项目自动滚动。

您可以使用或检查是否focusedView是 TextView 或其子项是 TextView 。focusedView.findViewById(R.id.textview_id_you_defined) != nullfocusedView instanceof TextView == true

public class StopAutoFocusScrollView extends ScrollView {

    private View focusedView;
    private ScrollMonitorListener listener;

    public interface ScrollMonitorListener {
        public boolean enableScroll(View view);
    }
    public StopAutoFocusScrollView(Context context) {
        super(context);
    }

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

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

    public void setScrollMonitorListener(ScrollMonitorListener listener) {
        this.listener = listener;
    }

    @Override
    public void requestChildFocus(View child, View focused) {
        focusedView = focused
        super.requestChildFocus(child, focused);
    }
    //flow : requestChildFocus -> scrollToChild -> scrollBy
    //Therefore, you can give listener to determine you want scroll to or not
    @Override
    public void scrollBy(int x, int y) {
        if (listener == null || listener.enableScroll(focusedView)) {
            super.scrollBy(x, y);
        }
    }
}
于 2015-06-13T06:44:41.623 回答
0

试试这个:)

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;
    }

}
于 2018-04-19T10:30:18.003 回答
0

对于这种令人愤怒的缺陷,我的反对意见略有不同。每当我点击 EditTexts 下方的多个 RadioButtons 之一时,滚动位置就会跳跃以适应 Android 确定为可见且聚焦的 EditText 的内容。

Runnable所有通过发出的保留当前所需滚动位置的尝试ScrollView.scrollTo(x,y)都被 Android 尽职尽责地忽略了!

我分享我的解决方案,希望它可以为其他人节省 8(八)个小时的浪费时间。

/* 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;
    }
});
于 2017-03-08T03:15:55.887 回答
0

只有这段代码对我有用:

public static void preventScrollViewFromScrollingToEdiText(ScrollView view) {
    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;
        }
    });
}

所有学分都归于这个原始答案

于 2017-07-25T10:45:16.967 回答