Фокусируемый EditText внутри ListView - PullRequest
120 голосов
/ 21 апреля 2010

Пока я потратил около 6 часов на это, и ничего не делал, кроме блокпостов.Общая предпосылка заключается в том, что в ListView есть некоторая строка (независимо от того, сгенерирована ли она адаптером или добавлена ​​в качестве представления заголовка), которая содержит виджет EditText и Button.Все, что я хочу сделать, это уметь использовать джогбол / стрелки, чтобы перемещать селектор по отдельным элементам, как обычно, но когда я добираюсь до определенной строки - даже если мне нужно явно идентифицировать строку - которая имеет фокусируемыйДитя, я хочу, чтобы этот ребенок сфокусировался, а не указывал положение селектором.

Я испробовал много возможностей, и до сих пор не получилось.

макет:

<ListView
    android:id="@android:id/list" 
    android:layout_height="fill_parent" 
    android:layout_width="fill_parent"
    />

Вид заголовка:

EditText view = new EditText(this);
listView.addHeaderView(view, null, true);

Предполагая, что в адаптере есть другие элементы, использование клавиш со стрелками будет перемещать выбор вверх / вниз в списке, как и ожидалось;но когда вы попадаете в строку заголовка, она также отображается с помощью селектора, и невозможно фокусироваться на EditText с помощью джогбола.Примечание: при нажатии EditText будет фокусироваться на этом месте, однако это зависит от сенсорного экрана, что не является обязательным требованием.

ListView, по-видимому, имеет два режима в этомОбратите внимание:
1. setItemsCanFocus(true): селектор никогда не отображается, но EditText может получить фокус при использовании стрелок.Алгоритм поиска фокуса трудно предсказать, и нет никакой визуальной обратной связи (по любым строкам: наличие фокусируемых дочерних элементов или нет) о том, какой элемент выбран, и то и другое может дать пользователю неожиданный опыт.
2. setItemsCanFocus(false): селекторвсегда рисуется в режиме без касания, и EditText никогда не может сфокусироваться - даже если вы нажмете на него.

Что еще хуже, вызов editTextView.requestFocus() возвращает true, но на самом деле это не таксфокусировать EditText.

То, что я предполагаю, в основном представляет собой гибрид 1 & 2, где вместо настройки списка, если все элементы являются фокусируемыми или нет, я хочу установить фокусируемостьдля одного элемента в списке, чтобы селектор плавно переходил от выбора всей строки для нефокусируемых элементов и обхода дерева фокуса для элементов, содержащих фокусируемые дочерние элементы.

Любойберущая

Ответы [ 12 ]

101 голосов
/ 21 апреля 2010

Извините, ответил на мой вопрос.Возможно, это не самое правильное или самое элегантное решение, но оно работает для меня и дает довольно солидный пользовательский опыт.Я посмотрел код для ListView, чтобы понять, почему эти два поведения так различны, и натолкнулся на это из ListView.java:

    public void setItemsCanFocus(boolean itemsCanFocus) {
        mItemsCanFocus = itemsCanFocus;
        if (!itemsCanFocus) {
            setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        }
    }

Итак, при вызове setItemsCanFocus(false) он также устанавливает фокусируемость потомков так, чтони один ребенок не может получить фокус.Это объясняет, почему я не мог просто переключить mItemsCanFocus в OnItemSelectedListener ListView - потому что ListView тогда блокировал фокус для всех дочерних элементов.

Что у меня сейчас:

<ListView
    android:id="@android:id/list" 
    android:layout_height="match_parent" 
    android:layout_width="match_parent"
    android:descendantFocusability="beforeDescendants"
    />

Iиспользуйте beforeDescendants, потому что селектор будет отрисовываться только тогда, когда фокус у самого ListView (не дочернего), поэтому поведение по умолчанию должно состоять в том, чтобы ListView сначала фокусировался и рисовал селекторы.

Затем в OnItemSelectedListenerТак как я знаю, какое представление заголовка я хочу переопределить селектором (потребовалось бы больше усилий для динамического определения, содержит ли какая-либо заданная позиция фокусируемое представление), я могу изменить фокусируемость потомка и установить фокус на EditText.И когда я выхожу из этого заголовка, снова меняю его.

public void onItemSelected(AdapterView<?> listView, View view, int position, long id)
{
    if (position == 1)
    {
        // listView.setItemsCanFocus(true);

        // Use afterDescendants, because I don't want the ListView to steal focus
        listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        myEditText.requestFocus();
    }
    else
    {
        if (!listView.isFocused())
        {
            // listView.setItemsCanFocus(false);

            // Use beforeDescendants so that the EditText doesn't re-take focus
            listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
            listView.requestFocus();
        }
    }
}

public void onNothingSelected(AdapterView<?> listView)
{
    // This happens when you start scrolling, so we need to prevent it from staying
    // in the afterDescendants mode if the EditText was focused 
    listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}

Обратите внимание на закомментированные setItemsCanFocus вызовы.С этими вызовами я получил правильное поведение, но setItemsCanFocus(false) заставил фокус перескочить с EditText на другой виджет вне ListView, обратно к ListView и отобразил селектор на следующем выбранном элементе, и этот прыжковый фокус отвлекал,Удаление изменения ItemsCanFocus и просто переключение фокусировки на потомки дало мне желаемое поведение.Все элементы рисуют селектор как обычно, но когда он попадает в строку с EditText, он фокусируется на текстовом поле.Затем, продолжая выход из этого EditText, он снова начал рисовать селектор.

96 голосов
/ 04 февраля 2011

Это помогло мне.
В вашем манифесте:

<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
17 голосов
/ 04 апреля 2013

Моей задачей было реализовать ListView, который расширяется при нажатии. Дополнительное пространство показывает EditText, где вы можете ввести текст. Приложение должно работать на 2.2+ (до 4.2.2 на момент написания этой статьи)

Я пробовал многочисленные решения из этого поста и другие, которые я мог найти; проверил их на устройствах с 2.2 до 4.2.2. Ни одно из решений не было удовлетворительным на всех устройствах 2.2+, каждое решение было связано с различными проблемами.

Я хотел бы поделиться своим окончательным решением:

  1. установить просмотр списка на android:descendantFocusability="afterDescendants"
  2. установить просмотр списка на setItemsCanFocus(true);
  3. установите вашу активность на android:windowSoftInputMode="adjustResize" Многие люди предполагают, что adjustPan, но adjustResize дает намного лучший результат, просто проверьте это в вашем случае. С adjustPan вы получите, например, нижний список элементов. Документы предполагают, что («Это обычно менее желательно, чем изменение размера»). Также на 4.0.4 после того, как пользователь начинает печатать на мягкой клавиатуре, экран перемещается вверх.
  4. на 4.2.2 с adjustResize есть некоторые проблемы с фокусом EditText. Решение состоит в том, чтобы применить решение rjrjr из этой темы. Это выглядит шрамы, но это не так. И это работает. Просто попробуйте.

Дополнительно 5. Из-за обновления адаптера (из-за изменения размера представления), когда EditText фокусируется на предыдущих версиях HoneyComb, я обнаружил проблему с обращенными представлениями: получение View для элемента ListView / обратный порядок на 2.2; работает на 4.0.3

Если вы выполняете некоторые анимации, вы можете изменить поведение на adjustPan для предсотовых версий, чтобы изменение размера не срабатывало, а адаптер не обновлял представления. Вам просто нужно добавить что-то вроде этого

if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

Все это дает приемлемый укс на устройствах 2.2 - 4.2.2. Надеюсь, это сэкономит людям время, так как мне потребовалось не менее нескольких часов, чтобы прийти к такому выводу.

9 голосов
/ 18 октября 2015

Это спасло мне жизнь --->

  1. установить эту строку

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Тогда в вашем манифесте в теге активности введите это ->

    <activity android:windowSoftInputMode="adjustPan">

Ваше обычное намерение

7 голосов
/ 25 марта 2013

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

XML:

<RitalinLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
  <ListView
      android:id="@+id/cart_list"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:scrollbarStyle="outsideOverlay"
      />
</RitalinLayout>

Java:

/**
 * It helps you keep focused.
 *
 * For use as a parent of {@link android.widget.ListView}s that need to use EditText
 * children for inline editing.
 */
public class RitalinLayout extends FrameLayout {
  View sticky;

  public RitalinLayout(Context context, AttributeSet attrs) {
    super(context, attrs);

    ViewTreeObserver vto = getViewTreeObserver();

    vto.addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
      @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) {
        if (newFocus == null) return;

        View baby = getChildAt(0);

        if (newFocus != baby) {
          ViewParent parent = newFocus.getParent();
          while (parent != null && parent != parent.getParent()) {
            if (parent == baby) {
              sticky = newFocus;
              break;
            }
            parent = parent.getParent();
          }
        }
      }
    });

    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override public void onGlobalLayout() {
        if (sticky != null) {
          sticky.requestFocus();
        }
      }
    });
  }
}
4 голосов
/ 21 октября 2010

это сообщение точно соответствует моим ключевым словам. У меня есть заголовок ListView с поиском EditText и кнопкой поиска.

Чтобы сфокусироваться на EditText после потери начального фокуса, я нашел только один HACK:

    searchText.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View arg0) {
            // LOTS OF HACKS TO MAKE THIS WORK.. UFF...
            searchButton.requestFocusFromTouch();
            searchText.requestFocus();
        }
    });

Потерял много часов, и это ненастоящее решение. Надеюсь, это поможет кому-то жестко.

2 голосов
/ 03 февраля 2016

Если список динамический и содержит фокусируемые виджеты, то правильным вариантом будет использование RecyclerView вместо ListView IMO.

Обходные пути, которые устанавливают adjustPan, FOCUS_AFTER_DESCENDANTS или запоминают фокусированное положение вручную, на самом деле являются просто обходными путями. У них есть угловые случаи (прокрутка + проблемы с программной клавиатурой, изменение каретки в EditText). Они не изменяют тот факт, что ListView создает / уничтожает представления массово во время notifyDataSetChanged.

С помощью RecyclerView вы уведомляете об отдельных вставках, обновлениях и удалениях. Сфокусированное представление не воссоздается, поэтому нет проблем с элементами управления формой, теряющими фокус. В качестве дополнительного бонуса RecyclerView анимирует вставки и удаления элементов списка.

Вот пример из официальных документов о том, как начать работу с RecyclerView: Руководство разработчика - создание списка с помощью RecyclerView

1 голос
/ 25 ноября 2015

иногда, когда вы используете android:windowSoftInputMode="stateAlwaysHidden" в манифесте или в xml, в этот раз клавиатура теряет фокус. Итак, сначала проверьте это свойство в вашем XML и манифесте, если оно есть, просто удалите его. После этого добавьте эту опцию, чтобы манифестировать файл в побочном действии android:windowSoftInputMode="adjustPan" и добавить это свойство в просмотр списка в xml android:descendantFocusability="beforeDescendants"

0 голосов
/ 20 февраля 2016

Просто попробуйте это

android:windowSoftInputMode="adjustNothing"

в

активность

раздел вашего манифеста. Да, он настраивает ничего, что означает, что editText останется там, где он есть, когда IME открывается. Но это всего лишь небольшое неудобство, которое все еще полностью решает проблему потери фокуса.

0 голосов
/ 22 ноября 2013

Я только что нашел другое решение. Я считаю, что это скорее взлом, чем решение, но оно работает на Android 2.3.7 и Android 4.3 (я даже тестировал этот старый добрый D-pad)

Начните свой веб-просмотр как обычно и добавьте следующее: (спасибо Michael Bierman)

listView.setItemsCanFocus(true);

Во время вызова getView:

editText.setOnFocusChangeListener(
    new OnFocusChangeListener(View view,boolean hasFocus){
        view.post(new Runnable() {
            @Override
            public void run() {
                view.requestFocus();
                view.requestFocusFromTouch();
            }
     });
...