将ScrollView从自动滚动停止到EditText

时间:2011-05-30 12:38:43

标签: android android-edittext scrollview autoscroll

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

Button中有一堆观看(TextView s,ScrollView等),其中一个是EditText。点击ScrollView内的按钮后,ScrollView向下滚动到EditText(其关闭屏幕)。这是不可取的,因为您不希望其他元素滚动离开屏幕。

现在,我可以通过在ScrollView中显示其他可聚焦元素来首​​先显示屏幕时阻止这种情况发生。但是,一般问题仍然存在。用户手动向下滚动到EditText,输入一些数字,然后向上滚动到顶部(EditText关闭屏幕),他们点击ScrollView中的按钮,猜猜是什么? ScrollView向下滚动到该徽标EditText

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

如果可以,请帮忙。

我已经玩过EditText顶部有0高ScrollView之类的内容,将{Next {1}}中的其他项添加Next Focusable元素属性等。我认为一个“黑客”可能是让ScrollView在虚拟或手动键盘被隐藏或其他东西时失去焦点。

21 个答案:

答案 0 :(得分:35)

经过一段时间的努力解决这个问题之后,我找到了一个似乎不会太难看的解决方案。首先,请确保ViewGroup(直接)包含EditText descendantFocusability设置为“在后人之前”,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,因此不应该进行滚动。

答案 1 :(得分:19)

只需在linearlayout

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

单行解决问题

答案 2 :(得分:9)

我遇到了同样的问题。我正在使用一个技巧来处理这个问题:

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

答案 3 :(得分:8)

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

在AndroidManifest.xml中为Activity添加一个属性:

        <activity android:name=".MyActivity" android:windowSoftInputMode="adjustPan"> </activity>

答案 4 :(得分:7)

在:

中添加2个参数
android:focusable="true"
android:focusableInTouchMode="true"

那里有Main布局。

<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不会自动关注。

答案 5 :(得分: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>将所有其他视图集中在滚动视图中。这应该适合每次IMO

答案 6 :(得分:2)

thomas88wp回答,https://stackoverflow.com/a/6486348/528746为我工作。 但我有两个问题: 滚动时,我想隐藏键盘 2.我有很多EditText视图,并且不想为每一个编写视图 (我确实是getActivity()因为我在Fragment中写这个而不是活动)

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

    });

答案 7 :(得分:2)

我对这个最恐怖的错误的修复,(值得注意的是,只有在他们修改了fling方法而不是愚蠢的情况下,这只是API11之前的版本。)

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

答案 8 :(得分:1)

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

答案 9 :(得分: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>

答案 10 :(得分:1)

我遇到了类似的问题,最后还是开始工作了。我的滚动视图包含一系列自定义按钮,后跟EditText(通常具有焦点,但我不希望它失去焦点)。只要单击按钮,滚动视图就会自动滚动到焦点EditText。覆盖public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate)并始终返回falseViewGroup的默认行为)就可以了。希望它也有助于你的情况。

答案 11 :(得分: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>

答案 12 :(得分: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;
    }
});

答案 13 :(得分: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);
}

答案 14 :(得分:0)

对我来说,它无法覆盖ScrollView onTouch。也没有工作android:descendantFocusability =&#34; beforeDescendants&#34; 机器人:focusableInTouchMode =&#34;真&#34; 这个和另一个提到的解决方案只在第一次工作 - 只有当没有选择EditText时,但是一旦你选择它,scrollview会再次自动滚动。

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

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"

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

答案 15 :(得分: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);
        }
    }
}

答案 16 :(得分:0)

当我的应用处理方向更改时,我经常遇到此问题。

在这种情况下,我使用以下类型的代码:

@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);
        }
    });
    ...
}

答案 17 :(得分:0)

我对这种令人愤怒的缺陷略有不同。每当我点击EditTexts下面的一些RadioButton时,滚动位置就会跳跃,以适应Android确定的可见和聚焦的EditText。

所有尝试通过发出WebDriver driver = new FirefoxDriver(); Runnable的{​​{1}}保留当前所需的滚动位置都是由Android尽职尽责!

我分享我的解决方案,希望它可以节省别人8(8)浪费的时间。

ScrollView.scrollTo(x,y)

答案 18 :(得分: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;
        }
    });
}

所有积分均转至this original answer

答案 19 :(得分: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;
    }

}

答案 20 :(得分:0)

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

例如:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:descendantFocusability="blocksDescendants" >
相关问题