Остановить ScrollView от автоматической прокрутки до EditText

Кажется, это обычная проблема без большого решения, которое я нашел. Цель состоит в том, чтобы остановить ScrollView от автоматической прокрутки до EditText (или любого вида в этом отношении) с фокусом.

У вас есть куча просмотров (кнопки, текстовые изображения и т.д.) в Scrollview, одним из которых является EditText. После нажатия кнопки "Кнопка" в списке прокрутки Scrollview прокручивается до EditText (его за пределами экрана). Это нежелательно, так как есть другие элементы, которые вы не хотите прокручивать с экрана.

Теперь я могу остановить это, когда экран сначала показывает, что в scrollview есть другие настраиваемые элементы. Однако общая проблема все еще существует. Пользователь прокручивается вручную в EditText, вводит некоторые цифры, затем прокручивается вверх (теперь экран EditText выключен), они нажимают кнопку в ScrollView и угадывают, что? ScrollView прокручивается до этого значка EditText.

Я подумываю о подклассификации Scrollview и переопределении некоторых частных методов, таких как findFocusableViewInBounds, но у меня есть чувство, что я просто получаю больше проблем.

Пожалуйста, помогите, если сможете.

Я играл с такими вещами, как "EditText" высотой 0 в верхней части моего Scrollview, добавляя свойства элемента Next Focusable к другим элементам в ScrollView и т.д. Я полагаю, что один "взлом" может состоять в том, чтобы получить EditText потерять фокус, когда виртуальная или ручная клавиатура спрятана или что-то в этом роде.

Ответ 1

После долгой борьбы с этой проблемой я нашел решение, которое, похоже, работает, не будучи слишком уродливым. Во-первых, убедитесь, что независимо от того, что ViewGroup (непосредственно) содержит ваш EditText, имеет свойство 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;
    }
});

Скажите, если это не исправить. Что должно произойти, так это то, что Layout получает фокус вместо EditText, поэтому прокрутки не должно происходить.

Ответ 2

Просто создайте пустой вид в верхней части linearlayout

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

Ответ 4

Проблема не в java-коде, а на коде манифеста.

В AndroidManifest.xml добавьте атрибут Activity:

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

Ответ 5

Вот что я сделал

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

Причина в том, что вам нужно сделать все другие виды фокуса внутри scrollview явным android:focusable="true", а затем <requestFocus></requestFocus>. Это должно работать каждый раз, когда IMO

Ответ 6

Добавив 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 не будет автоматически сфокусирован.

Ответ 7

thomas88wp answer, fooobar.com/questions/154383/... работал у меня. Но у меня были две проблемы: 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;
        }

    });

Ответ 8

Мое исправление для этой самой ужасной ошибки (стоит отметить, что это pre 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

Мы можем написать собственный 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);
}

Ответ 10

Другая версия кода 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;
    }
});

Ответ 12

У меня была аналогичная проблема и, наконец, она работала. В моем представлении прокрутки есть серия настраиваемых кнопок, а затем EditText (обычно фокус, но я не хочу, чтобы он терял фокус). При каждом нажатии на кнопки прокрутки автоматически прокручивается до сфокусированного EditText. Переопределение public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate) и всегда возвращение false (поведение по умолчанию ViewGroup) сделали трюк. Надеюсь, это поможет и в вашей ситуации.

Ответ 13

Только для меня этот код работает:

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

Все кредиты переходят к этому оригинальному ответу.

Ответ 14

У меня часто возникает эта проблема, когда мои приложения обрабатывают изменение ориентации.

В этом случае я использую следующий код:

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

Ответ 15

Создайте собственный ScrollView (создайте класс и продолжите его HorizontalScrollView) и создайте сеттер для прокрутки. Затем переопределите computeScrollDeltaToGetChildRectOnScreen.

Как это работает: каждый раз, когда у андроида есть текст редактирования или что-то в фокусе, которое выключено, он вызывает метод 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>

Ответ 16

Для меня это не помогло переопределить ScrollView onTouch. Также не работал андроид: descendantFocusability = "beforeDescendants" андроид: focusableInTouchMode = "истина" Это и другие упомянутые решения работают только в первый раз - только когда 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"

Может быть, это не очень хорошее решение, но его работа.

Ответ 17

Мое решение ниже, чтобы проследить исходный код и переопределить некоторую функцию, чтобы остановить автоматическую прокрутку с помощью сфокусированного элемента.

Вы можете проверить, является ли 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);
        }
    }
}

Ответ 18

Лучшим решением является добавление параметров фокусировки для дочернего элемента вашего прокрутки:

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>

Ответ 19

У меня было несколько другое возражение против этого бешенства. Всякий раз, когда я нажимал один из нескольких RadioButtons под EditTexts, позиция прокрутки подпрыгивала, чтобы разместить то, что 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;
    }
});