2010-04-21 5 views
112

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

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

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

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

Любые берущие?

ответ

98

Извините, ответил на мой собственный вопрос. Это может быть не самое правильное или самое изящное решение, но оно работает для меня и дает довольно солидный пользовательский интерфейс. Я посмотрел в код для 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" 
    /> 

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

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

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

+0

очень круто, еще не проверено. Вы тестировали на 1.5, 1.6 и 3.0? –

+0

Рафаэль Санчес: Я не касался проекта с 2.1, но в то время он был подтвержден, работая в 1.5, 1.6 и 2.1. Я не гарантирую, что он по-прежнему работает в версии 2.2 или более поздней. – Joe

+12

требуется только \t android: descendantFocusability = "afterDescendants" - в любом случае +1 – kellogs

4

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

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

+0

dint help me :( – Anitha

90

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

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

Я не уверен, что вижу релевантность. Настройка windowSoftInputMode просто изменяет способ, которым IME настраивает остальную часть содержимого окна, когда он открыт. Он не позволяет выборочно изменять тип фокуса ListView. Можете ли вы объяснить немного больше, как это относится к начальному варианту использования? – Joe

+1

@Joe Когда IME открывается, курсор - и, вероятно, фокус тоже - просто прыгает на моем экране, что делает невозможным ввод текст. Ваш 'OnItemSelectedListener' не меняет этого. Однако простое решение Iogan работает как шарм, спасибо! – Gubbel

+2

@Gubbel: Действительно, это не изменило бы это вообще, потому что исходный вопрос был о чем-то совершенно другом :) Glad Исправление logan работает для того, что вы искали, но просто не связано даже с вопросом. – Joe

0

Другое простое решение заключается в определении вашего 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; 

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

+1

Вопрос касается фокусировки, а не кликабельности. – Joe

0

Самая важная часть, чтобы получить фокус работает для ячейки списка. Специально для списка на Google ТВ это очень важно:

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.

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

7

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

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

Слегка изменены, это решение работает для меня после 2 дней * @ # (*: если (липкий = NULL) { sticky.RequestFocus(); sticky.RequestFocusFromTouch(); липких = NULL; }! –

17

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

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

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

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

Дополнительный 5. Благодаря адаптеру обновляется (из-за изменения размера зрения), когда EditText получает фокус на заранее версии Honeycomb я нашел проблему с обратными просмотров: getting View for ListView item/reverse order on 2.2; works on 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 устройствах. Надеюсь, что это спасет людей некоторое время, так как мне потребовалось не менее нескольких часов, чтобы прийти к такому выводу.

+1

только первый и второй шаги достаточно в моем случае –

+0

Работает отлично с моим xElement.setOnClickListener (..) в ArrayAdapter и ListView.setOnItemClickListener (...) - наконец-то! Big thx. – Javatar

0

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

инициализации вашего WebView, как обычно, и добавьте это: (спасибо Michael Бирман)

listView.setItemsCanFocus(true); 

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

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

Что такое просмотр здесь в view.requestFocus ? – NarendraJi

+0

Это старый ответ, но он ссылался на представление в области, приведенной в качестве аргумента для OnFocusChangeListener. Я считаю, – alaeri

+0

Это вызывает цикл изменения фокуса. –

8

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

  1. набор эта линия

    ListView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

  2. Затем в манифесте в деятельности типа тега это ->

    <activity android:windowSoftInputMode="adjustPan">

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

1

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

2

Если список является динамическим и содержит фокусируемые виджеты, то правильный вариант заключается в использовании RecyclerView вместо ListView ИМО.

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

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

Вот пример из официальных документов о том, как начать работу с RecyclerView: http://developer.android.com/training/material/lists-cards.html

0

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

android:windowSoftInputMode="adjustNothing" 

в деятельности

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