Остановить ScrollView от автоматической прокрутки до EditText - PullRequest
41 голосов
/ 30 мая 2011

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

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

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

Я думаю о расширении ScrollView и переопределении некоторых из методов, таких как findFocusableViewInBounds, но у меня есть ощущение, что я простоУ меня больше проблем.

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

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

Ответы [ 21 ]

1 голос
/ 19 февраля 2014

Создайте собственный 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>
1 голос
/ 16 декабря 2013

Я сделал тестовый проект, чтобы поэкспериментировать с различными решениями, если кто-то захочет поиграть с ним.https://github.com/marchold/EditText-ErrorPopup-Scroll-View

1 голос
/ 14 декабря 2016

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

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>
1 голос
/ 30 октября 2014

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

0 голосов
/ 24 февраля 2019

Я решил эту проблему, добавив атрибут descendantFocusability к ScrollView, содержащему LinearLayout со значением blockDescendants.

Например:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:descendantFocusability="blocksDescendants" >
0 голосов
/ 19 апреля 2018

Попробуйте это:)

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

}
0 голосов
/ 25 июля 2017

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

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

Все кредиты идут на этот оригинальный ответ .

0 голосов
/ 08 марта 2017

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

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

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

@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);
        }
    });
    ...
}
0 голосов
/ 13 июня 2015

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

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