Фокусная проблема с несколькими EditTexts

У меня есть активность с двумя EditText s. Я вызываю requestFocus во втором поле EditText, поскольку по умолчанию фокус переходит к первому. Фокус, как представляется, находится во втором поле (второй получает выделенную границу), но если мы попытаемся ввести какие-либо символы с помощью аппаратной клавиатуры, текст появится в первом элементе управления EditText. Любые идеи, почему это произойдет?

Ответы

Ответ 1

Трудно сказать, была ли это ваша проблема, но это маловероятно.

TL; DR: Никогда не вызывайте методы смены фокуса, такие как requestFocus() изнутри вызова onFocusChanged().

Проблема заключается в ViewGroup.requestChildFocus(), который содержит следующее:

// We had a previous notion of who had focus. Clear it.
if (mFocused != child) {
    if (mFocused != null) {
        mFocused.unFocus();
    }

    mFocused = child;
}

Внутри частного поля mFocused ViewGroup сохраняет дочернее представление, которое в настоящее время имеет фокус, если оно есть.

Предположим, у вас есть ViewGroup VG, который содержит три настраиваемых вида (например, EditTexts) A, B и C.

Вы добавили OnFocusChangeListener в A, который (возможно, не напрямую, а где-то внутри внутри) вызывает B.requestFocus(), когда A теряет фокус.

Теперь представьте, что A имеет фокус, и пользователь нажимает на C, вызывая A и C, чтобы получить фокус. Поскольку VG.mFocused в настоящее время A, вышеуказанная часть VG.requestChildFocus(C, C) затем переводит на это:

if (A != C) {
    if (A != null) {
        A.unFocus();          // <-- (1)
    }

    mFocused = C;             // <-- (3)
}

A.unFocus() делает здесь две важные вещи:

  • Он отмечает A как не имеющий фокуса больше.

  • Он вызывает слушателя изменения фокуса.

В этом слушателе вы теперь вызываете B.requestFocus(). Это приведет к тому, что B будет отмечен как фокус, а затем вызовет VG.requestChildFocus(B, B). Поскольку мы все еще глубоко внутри вызова, отмеченного знаком (1), значение mFocused все еще A, и, таким образом, этот внутренний вызов выглядит следующим образом:

if (A != B) {
    if (A != null) {
        A.unFocus();
    }

    mFocused = B;             // <-- (2)
}

На этот раз вызов A.unFocus() ничего не делает, потому что A уже отмечен как нефокусированный (в противном случае здесь будет бесконечная рекурсия). Кроме того, ничего не происходит, что отмечает C как не сфокусированное, это представление, которое фактически имеет фокус прямо сейчас.

Теперь наступает (2), который устанавливает mFocused в B. После некоторого количества вещей мы, наконец, вернемся из вызова в (1) и, таким образом, в (3) значение mFocused теперь установлено на C, перезаписав предыдущее изменение.

Итак, теперь мы оказываемся в неустойчивом состоянии. B и C оба считают, что они имеют фокус, VG считает, что C является сфокусированным ребенком.

В частности, нажатия клавиш заканчиваются на C, и пользователю невозможно переключить фокус обратно на B, потому что B считает, что он уже имеет фокус и, следовательно, t делать что-либо в запросах фокуса; самое главное, он не вызывает VG.requestChildFocus.

Следствие. Вы также не должны полагаться на результаты вызовов hasFocus(), находясь внутри обработчика OnFocusChanged, потому что информация о фокусе несовместима во внутреннем вызове.

Ответ 2

Я нашел решение.

Внутри onFocusChange не вызывается непосредственно requestFocus, вместо этого отправляйте runnable для запроса фокуса. например ниже моего кода,

editText.setOnFocusChangeListener(new OnFocusChangeListener() {
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus == false) {
                  editText.post(new Runnable() {
                        @Override
                        public void run() {
                            editText.requestFocus();
                        }
                  });
             }
        }
    });

Ответ 3

У меня возникла одна и та же проблема: у одного из моих EditText есть OnFocusListener, и когда он теряет фокус, я делаю некоторые преобразования, но если что-то пойдет не так, я снова попробую FFFocus и позволю пользователю решить проблему. Здесь, когда проблема возникает, я заканчиваю с EditText с фокусом, я попытался выполнить поиск с фокусом, но findFocus() вернул null. Единственное решение, которое я нашел, это создать переменную EditText

private EditText requestFocus;

Если какая-либо проблема, я устанавливаю свой EditText для этой переменной, и вот что мне не нравится, но она работает, я устанавливаю OnFocusListener в другие представления моей активности. Когда они получают фокус, я делаю это

@Override
public void onFocusChange(View v, boolean hasFocus) {

    if (hasFocus)
        if(requestFocus != null){
            v.clearFocus();
            requestFocus.requestFocus();
            requestFocus = null;
        }
}

Я очищаю фокус от его вида, я запрашиваю Focus и устанавливаю переменную requestFocus null.

Я считаю, что это происходит потому, что в то время, когда я запрашиваю Фокус, у меня еще нет фокуса, мой EditText возвращает фокус, и действие фокусируется на следующем представлении. Надеюсь, это кому-то поможет.