Фокусируемый EditText внутри ListView

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

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

расположение:

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

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

Хуже того, вызов editTextView.requestFocus() возвращает true, но на самом деле не дает фокуса EditText.

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

Любые участники?

Ответ 1

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

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

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

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

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

Я использую 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, он снова начал рисовать селектор.

Ответ 2

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

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

Ответ 3

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

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

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

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

Дополнительные 5. Из-за обновления адаптера (из-за изменения размера), когда EditText получает фокус на версиях до HoneyComb, я обнаружил проблему с обратными видами: получение представления для элемента 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);

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

Ответ 4

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

  • установите эту строку

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  • Затем в вашем манифесте в теге активности введите это →

    <activity android:windowSoftInputMode="adjustPan">

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

Ответ 5

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

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

Ответ 6

Этот пост соответствовал моим ключевым словам. У меня есть заголовок 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();
        }
    });

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

Ответ 7

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

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

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

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

Ответ 8

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

Ответ 9

Другим простым решением является определение вашего onClickListener в методе getView (..) вашего ListAdapter.

public View getView(final int position, View convertView, ViewGroup parent){
    //initialise your view
    ...
    View row = context.getLayoutInflater().inflate(R.layout.list_item, null);
    ...

    //define your listener on inner items

    //define your global listener
    row.setOnClickListener(new OnClickListener(){
        public void onClick(View v) {
            doSomethingWithViewAndPosition(v,position);
        }
    });

    return row;

Таким образом, ваша строка будет доступна для кликов, а также ваш внутренний вид:)

Ответ 10

Самая важная часть - заставить фокус работать в ячейке списка. Особенно для списка в Google TV это важно:

setItemsCanFocus метод просмотра списка делает трюк:

...
mPuzzleList = (ListView) mGameprogressView.findViewById(R.id.gameprogress_puzzlelist);
mPuzzleList.setItemsCanFocus(true);
mPuzzleList.setAdapter(new PuzzleListAdapter(ctx,PuzzleGenerator.getPuzzles(ctx, getResources(), version_lite)));
...

Мой список xml начинается следующим образом:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/puzzleDetailFrame"
             android:focusable="true"
             android:nextFocusLeft="@+id/gameprogress_lessDetails"
             android:nextFocusRight="@+id/gameprogress_reset"
...

nextFocusLeft/Right также важны для навигации по D-Pad.

Для получения дополнительной информации ознакомьтесь с замечательными другими ответами.

Ответ 11

Я нашел другое решение. Я считаю, что это скорее хак, чем решение, но оно работает на 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();
            }
     });

Ответ 12

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

android:windowSoftInputMode="adjustNothing"

в

активность

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