【问题标题】:Stop ScrollView from auto-scrolling to an EditText停止 ScrollView 自动滚动到 EditText
【发布时间】:2011-09-04 19:20:02
【问题描述】:

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

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

现在我可以通过在ScrollView 中添加其他可聚焦元素来阻止屏幕首次显示时发生这种情况。但是,普遍的问题仍然存在。用户手动向下滚动到EditText,输入一些数字,然后向上滚动到顶部(EditText 现在不在屏幕上),他们单击ScrollView 中的一个按钮,你猜怎么着? ScrollView 向下滚动到该死的 EditText。

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

如果可以,请提供帮助。

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

【问题讨论】:

标签: android android-edittext scrollview autoscroll


【解决方案1】:

在解决这个问题很长一段时间后,我找到了一个似乎可行而又不会太难看的解决方案。首先,确保无论ViewGroup(直接)包含您的EditText,都将descendantFocusability 设置为“在后代之前”,focusable 设置为“true”,focusableInTouchMode 设置为“true”。这不是ScrollView 本身,而是您拥有各种视图的内部布局。接下来将onTouchListener 添加到您的ScrollView 中,每当触摸到EditText 时,它就会从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,因此不应发生滚动。

【讨论】:

【解决方案2】:

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

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

单行解决问题

【讨论】:

  • 这是一个很好的解决方案!它使屏幕无法获得可见的聚焦视图。不错!
【解决方案3】:

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

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

【讨论】:

  • 这似乎有效,虽然按钮突出显示,但我想我可以在 requestFocusFromTouch() 调用之后立即进行 clearFocus() 调用。
  • 这是最好的!谢谢。
【解决方案4】:

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

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

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

【讨论】:

【解决方案5】:

通过添加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 将不会自动聚焦。

【讨论】:

    【解决方案6】:

    这就是我所做的

     <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" 然后 &lt;requestFocus&gt;&lt;/requestFocus&gt; 使滚动视图内的所有其他视图成为焦点。这应该在每次 IMO 时都有效

    【讨论】:

    • 我是说你需要明确地让请求关注滚动视图中的视图,以防止自动滚动
    • 这是解决问题的最佳 1 班轮。我希望一个mod会删除其余的。这一定正是他要找的东西。在页面加载时您想要关注的任何内容的结束括号之后,它对我来说非常有用&lt;requestFocus /&gt;。
    • 确实有效,但需要focusableInToucheMode,它有一个很大的缺点:您必须点击两次控件才能触发它......所以不是很好的解决方案......
    • works-horizo​​ntal scrollview,编辑表格行的最后一个单元格,位置保持..谢谢!!需要,android:focusable="true" android:focusableInToucheMode="true" 和
    【解决方案7】:

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

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

    【讨论】:

      【解决方案8】:

      我对这个最可怕的错误的修复,(值得注意的是,这只是在 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;
          }
      }
      

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

          我们可以编写一个自定义的 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);
          }
          

          【讨论】:

            【解决方案11】:

            当我的应用处理方向变化时,我经常遇到这个问题。

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

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

            【讨论】:

              【解决方案12】:

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

              【讨论】:

              • 这是一个比上面更简洁的实现。谢谢。需要注意的是,如果使用片段,您可能希望从片段中获取 rootView,而不是尝试从活动中获取滚动视图。
              【解决方案13】:

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

              【讨论】:

                【解决方案14】:

                创建一个自定义 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>
                

                【讨论】:

                • 非常感谢!你拯救了我的一天:)
                【解决方案15】:

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

                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>
                

                【讨论】:

                  【解决方案16】:

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

                  例如:

                  <LinearLayout
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:orientation="vertical"
                      android:descendantFocusability="blocksDescendants" >
                  

                  【讨论】:

                  • 太好了,我一直在寻找一种解决方案,以解决选择文本时 ScrollView 的跳转问题。
                  【解决方案17】:

                  对我来说,重写 ScrollView onTouch 是行不通的。也没有工作 android:descendantFocusability="beforeDescendants" 安卓:focusableInTouchMode="true" 这个和另一个提到的解决方案仅在第一次起作用 - 仅当未选择 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"
                  

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

                  【讨论】:

                    【解决方案18】:

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

                    你可以检查focusedView是TextView还是它的子是TextView, 通过使用focusedView.findViewById(R.id.textview_id_you_defined) != null 或focusedView 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);
                            }
                        }
                    }
                    

                    【讨论】:

                      【解决方案19】:

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

                      所有通过发出ScrollView.scrollTo(x,y) 的Runnable 保持当前所需滚动位置的尝试都被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;
                          }
                      });
                      

                      【讨论】:

                        【解决方案20】:

                        只有这段代码对我有用:

                        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。

                        【讨论】:

                          【解决方案21】:

                          试试这个:)

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

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2021-09-03
                            • 2016-03-06
                            • 2012-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多