Как запустить onListItemClick в Listactivity с помощью кнопок в списке?

89

У меня есть простой ListActivity, который использует настраиваемый ListAdapter для создания представлений в списке. Обычно ListAdapter просто заполняет представления TextViews, но теперь я хочу разместить там и кнопку.

Однако, как я понимаю и по опыту, размещение фокусируемого представления в элементе списка предотвращает срабатывание onListItemClick () в ListActivity при щелчке по элементу списка. Кнопка по-прежнему нормально функционирует в элементе списка, но когда нажимается что-то помимо кнопки, я хочу, чтобы сработал onListItemClick.

Как я могу заставить это работать?

CodeFusionMobile
источник
5
ваше решение с потомком Focusability действительно полезно, вы должны добавить его в качестве ответа и принять!
Максим Гонтарь
@Max Причина, по которой я этого не делаю, в том, что это действительно плохая практика, обходной путь. Если бы я когда-нибудь нашел постоянное здоровое решение, я бы дал на него ответ (если я помню, что написал этот вопрос год назад :))
CodeFusionMobile
Я также хотел бы увидеть обходной путь, который у вас есть. Я пытался установить фокус потомка и не могу заставить его работать с кнопками. Также я пытался поместить GridView (с ImageViews) в строку списка, и у него есть аналогичные проблемы.
MrPurpleStreak
ИМХО ответ, который я дал, - это гораздо более элегантное решение проблемы, чем то, которое было предложено Ramps и Praveen. Ps: Не пытаюсь оживить здесь забытый вопрос, но я вижу, что ты еще не принял никакого ответа; D
Эвоки
@CodeFusionMobile Не могли бы вы принять ответ Эвокса? Ответ, получивший наибольшее количество голосов, является ошибочным, потому что он отключает анимацию onclick для элемента ListView (где он становится синим). Это сэкономило бы другим разработчикам время, потраченное на то, чтобы пробовать лучший ответ, обнаруживать, что он ошибочен, а затем обращаться к Эвокам.
1 '16

Ответы:

99

как я писал в предыдущем комментарии, решение - установитьFocusable (false) на ImageButton.

Есть еще более элегантное решение: попробуйте добавить элемент списка android:descendantFocusability="blocksDescendants" в корневой макет . Это сделает клики на ListItem возможными, и отдельно вы можете обрабатывать клики Button или ImageButton.

Надеюсь, это поможет ;)

Ура

Эвоки
источник
3
android:descendantFocusability="blocksDescendants"было прекрасно.
Мэтью Митчелл,
1
ты, мой друг, мой личный герой. ты должен быть лучшим! это намного лучше почти для любых случаев!
OschtärEi
Это должен быть принятый ответ. У меня это сработало отлично. Похоже, что все представления в элементе списка должны быть нефокусируемыми, и этот делает именно это, не настраивая каждый отдельно.
rushinge 07
1
Мне кажется, это не работает. Под корневым макетом вы имеете в виду макет строки или то, где определено представление списка? Во всяком случае, я перепробовал все возможные комбинации .. не работает :(
GreenBee
«Корневой макет элемента списка», как я уже писал ... Итак, допустим, у вас есть список, состоящий из макетов (назовите их корневым макетом элемента списка), которые имеют несколько кнопок, ImageButton, TextVeiw ..
Ewoks
59

Я надеюсь, что смогу помочь здесь. Я предполагаю, что у вас есть собственный макет для элементов listView, и этот макет состоит из кнопки и некоторых других представлений, таких как TextView, ImageView или что-то еще. Теперь вы хотите, чтобы при нажатии кнопки запускалось другое событие, а при нажатии на все остальные события запускалось другое событие.

Вы можете добиться этого без использования onListItemClick () вашего ListActivity.
Вот что вам нужно сделать:

Вы используете собственный макет, поэтому, вероятно, вы переопределяете метод getView () своего настраиваемого адаптера. Хитрость заключается в том, чтобы установить разных слушателей для вашей кнопки и разных для всего представления (вашей строки). Взгляните на пример:

private class MyAdapter extends ArrayAdapter<String> implements OnClickListener {

    public MyAdapter(Context context, int resource, int textViewResourceId,
            List<String> objects) {
        super(context, resource, textViewResourceId, objects);
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        String text = getItem(position);
        if (null == convertView) {
            convertView = mInflater.inflate(R.layout.custom_row, null);
        }
        //take the Button and set listener. It will be invoked when you click the button.
        Button btn = (Button) convertView.findViewById(R.id.button);
        btn.setOnClickListener(this);
        //set the text... not important     
        TextView tv = (TextView) convertView.findViewById(R.id.text);
        tv.setText(text);
        //!!! and this is the most important part: you are settin listener for the whole row
        convertView.setOnClickListener(new OnItemClickListener(position));
        return convertView;
    }

    @Override
    public void onClick(View v) {
        Log.v(TAG, "Row button clicked");
    }
}

Ваш класс OnItemClickListener можно объявить, как здесь:

private class OnItemClickListener implements OnClickListener{       
    private int mPosition;
    OnItemClickListener(int position){
        mPosition = position;
    }
    @Override
    public void onClick(View arg0) {
        Log.v(TAG, "onItemClick at position" + mPosition);          
    }       
}

Конечно, вы, вероятно, добавите еще несколько параметров в конструктор OnItemClickListener.
И одна важная вещь - реализация getView, показанная выше, довольно уродлива, обычно вы должны использовать шаблон ViewHolder, чтобы избежать вызовов findViewById ... но вы, вероятно, уже это знаете.
Мой файл custom_row.xml - это RelativeLayout с кнопкой с идентификатором «кнопка», TextView с идентификатором «текст» и ImageView с идентификатором «изображение» - просто чтобы прояснить ситуацию.
С уважением!

Рампы
источник
9
Мое решение пошло другим путем. Я решил проблему, установив свойство Focusability потомка макета строки списка, чтобы блокировать потомков, и внезапно это сработало. Теперь я использую simpleCursorAdapter с прикрепленным к нему настраиваемым видоискателем, чтобы выполнять настройку событий и другие необычные вещи, связанные с кнопками.
CodeFusionMobile
3
Я использовал это решение. Однако предупреждение: если ваш обработчик onClick вызывает изменение содержимого списка, что приведет к вызову getView И, если ваш виджет имеет состояние (например, ToggleButton или CheckBox), И getView устанавливает это состояние, вы можете захотеть установитьOnClickListener (null) перед изменением состояния в onView, чтобы избежать эффекта цикла.
Пьер-Люк Паур
3
как насчет моего решения? У меня сложилось впечатление, что это более элегантно и может быть легко применено, когда нам это нужно ..
Ewoks
первый комментарий следует принять как ответ, чтобы людям вроде меня не потребовалось несколько часов, чтобы найти его
keybee
Благодарность! Это было полезно. Мне он нравится как более локализованная альтернатива подходу focus: blockDescendants, описанному в других потоках.
Йоав Шапира
18

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

Например:

public class ExtendedCheckBoxListView extends LinearLayout {

    private TextView mText;
    private CheckBox mCheckBox;

    public ExtendedCheckBoxListView(Context context, ExtendedCheckBox aCheckBoxifiedText) {
         super(context);
         …
         mText.setFocusable(false);
         mText.setFocusableInTouchMode(false);

         mCheckBox.setFocusable(false);
         mCheckBox.setFocusableInTouchMode(false);
        …       
    }
}
Раби
источник
3
Мне нравится ваше решение: если есть только один «интерактивный» виджет на строку, то отказать им в фокусируемости - изящное решение. Это означает, что нажатие в любом месте строки будет соответствовать, но это полезное поведение, когда виджет является переключателем или флажком. То же самое может быть достигнуто в XML, используя android: focusable = "false" и android: focusableInTouchMode = "false".
Пьер-Люк Паур,
1
способ xml, похоже, не работает, если вы измените видимость, поэтому нужно сделать это с помощью кода после установки "View" на visible
max4ever
13

У меня такая же проблема: OnListItemClick не сработал! [Решено]
Это происходит с классом, который расширяет ListActivity,
с макетом для ListActivity, в котором содержимое TextBox и ListView вложено в LinearLayout,
а другой макет для строк (CheckBox и TextBox вложены в LineraLayout).

Это код:

res / layout / configpage.xml (основной для ListActivity)

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent" > 
    <TextView  
        android:id="@+id/selection"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="pippo" /> 
    <ListView  
        android:id="@android:id/list"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:drawSelectorOnTop="false"  
        android:background="#aaFFaa" > 
    </ListView> 
<LinearLayout> 

res/layout/row.xml (layout for single row)  
<LinearLayout  
  xmlns:android="http://schemas.android.com/apk/res/android"  
  android:layout_width="fill_parent"  
  android:layout_height="wrap_content"> 
  <CheckBox  
      android:id="@+id/img"  
      android:layout_width="wrap_content"  
      android:layout_height="wrap_content"  
      **android:focusable="false"**  
      **android:focusableInTouchMode="false"** /> 

  <TextView  
      android:id="@+id/testo"  
      android:layout_width="wrap_content"  
      android:layout_height="wrap_content"  
      **android:focusable="false"**  
      **android:focusableInTouchMode="false"** /> 
</LinearLayout> 

src /.../.../ ConfigPage.java

public class ConfigPage extends ListActivity
{
    TextView selection;

    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.configpage);  
        // loaded from res/value/strings  
        String[] azioni = getResources().getStringArray(R.array.ACTIONS);  
        setListAdapter(new ArrayAdapter&lt;String&gt;(this, R.layout.row, 
                R.id.testo,   azioni));  
        selection = (TextView) findViewById(R.id.selection);  
    }       

    public void onListItemClick(ListView parent, View view, int position, long id)
    {
        selection.setText(" " + position);
    }
}

Это начало работать, когда я добавил row.xml

  • android:focusable="false"
  • android:focusableInTouchMode="false"

Я использую Eclipse 3.5.2
Android SDK 10.0.1
min версия SDK: 3

Надеюсь, это полезно
... и извините за мой английский :(

рфеллоны
источник
@fellons не знаю почему, но у меня это не сработало. Мой onListItemClick не вызывается.
likejudo
Вы добавили код свойств android: focusable = "false" и android: focusableInTouchMode = "false" код?
rfellons
2

просто добавьте android:focusable="false"как один из атрибутов вашей кнопки

Джон
источник
1

У меня была такая же проблема с ToggleButton. После полдня, когда я бился головой о стену, я наконец решил ее. Это так же просто, как сделать фокусируемый вид несфокусируемым, используя android: focusable. Вам также следует избегать игры с фокусируемостью и кликабельностью (я просто придумал слова) строки списка, просто оставьте для них значение по умолчанию.

Конечно, теперь, когда ваши фокусируемые представления в строке списка не фокусируются, у пользователей, использующих клавиатуру, могут возникнуть проблемы, ну, с их фокусировкой. Вряд ли это будет проблемой, но на всякий случай, если вы хотите написать 100% безупречные приложения, вы можете использовать событие onItemSelected, чтобы сделать элементы выбранной строки доступными для фокусировки, а элементы ранее выбранной строки - не фокусируемыми.

Смешной
источник
Я использовал этот подход. Это самый простой способ, если вы можете обойти проблему невозможности сосредоточиться на отдельных элементах. Это также сохраняет возможности отображения состояний для элементов списка в том виде, в каком они должны быть, чего не делают некоторые другие обходные пути.
Джонатан С.
1
 ListView lv = getListView();
lv.setTextFilterEnabled(true);

lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
    int position, long id) {
  // When clicked, show a toast with the TextView text
  Toast.makeText(getApplicationContext(), ((TextView) view).getText(),
      Toast.LENGTH_SHORT).show();
}
});
Конфуций
источник
-1

Я использовал getListAdapter (). GetItem (position) для создания экземпляра объекта, который содержит мои значения в элементе

MyPojo myPojo = getListAdapter().getItem(position);

затем использовал метод получения из myPojo, он вызовет свои правильные значения в элементе.

Майки
источник