2011-12-15 5 views
13

У меня есть ListView с моим собственным пользовательским адаптером, полученным из BaseAdapter. Каждый элемент в ListView имеет подпункты, такие как ImageView и TextView.Как узнать, какой вид внутри определенного элемента ListView был нажат

Как узнать, какой из этих подпунктов пользователь нажал? Можно ли, например, подключить слушателя к функции getView, или это может быть проблемой?

/Henrik

Edit: В настоящее время я в деятельности, которая содержит ListView в onItemClick. Есть ли какой-либо хороший способ узнать, какой элемент в конкретном элементе ListView был нажат, проверив параметры в onItemClick.

@Override 
public void onItemClick(AdapterView<?> a, View v, int pos, long id) { 
. 
. 
} 

ответ

3

Вы можете это сделать. Вам необходимо изменить метод getView:

@Override 
public View getView(final int position, View row, final ViewGroup parent) { 
    ...  
    YourWrapper wrapper = null; 
    if (row == null) { 
     row = getLayoutInflater().inflate(R.layout.your_row, parent, false); 
     wrapper = new YourWrapper(row); 
     row.setTag(wrapper); 
    } else { 
     wrapper = (YourWrapper) row.getTag(); 
    } 

    wrapper.yourSubView.setOnClickListener(new View.OnClickListener() 
    {    
    @Override 
    public void onClick(View v) { 
     // do something 
    } 
    ... 
} 
+0

Это работало нормально, по проблемам я переключился на ImageButton. И это не сработало, как ожидалось. Для других см. Этот вопрос также: http://stackoverflow.com/questions/6116583/android-listview-custom-adapter-imagebutton – Henrik

0

Поскольку у вас есть только ImageView и TextView, вы можете изменить ImageView на ImageButton. Затем вы можете добавить слушателя в ImageButton, который вызывается, если пользователь нажимает на изображение. Если он щелкнет в другом месте объекта (включая TextView), вызывается onItemclicklistener. Это гораздо проще, я думаю.

+0

Я потерял, что onItemClickListener обратного вызова, то есть она не называется, только слушателя для этого ImageButton называется – Henrik

3

ListView перерабатывает объекты просмотра строк и присваивает им новые данные при вызове getView, поэтому подход к использованию заключается в добавлении слушателя в функцию getView. Вот пример кода из приложения, который показывает, как это делается:

private class DeletePlayerAdapter extends ArrayAdapter<Player> { 
     Context context; 
     int layoutResourceId; 
     ArrayList<Player> data; 

     public DeletePlayerAdapter(Context context, int layout, 
       ArrayList<Player> list) { 
      super(context, layout, list); 
      this.layoutResourceId = layout; 
      this.context = context; 
      this.data = list; 
     } 

     @Override 
     public View getView(final int position, View convertView, 
       ViewGroup parent) { 
      View row = convertView; 
      PlayerHolder holder = null; 
      if (row == null) { 
       LayoutInflater inflater = ((Activity) context) 
         .getLayoutInflater(); 
       row = inflater.inflate(layoutResourceId, parent, false); 
       holder = new PlayerHolder(); 
       holder.player_name = (TextView) row 
         .findViewById(R.id.player_name); 
       holder.player_number = (TextView) row 
         .findViewById(R.id.player_number); 
       holder.seeded_button = (ImageButton) row 
         .findViewById(R.id.delete_toggle); 
       holder.player_name.setTypeface(tf); 
       holder.player_number.setTypeface(tf); 
       row.setTag(holder); 
       players_array.get(position).marked_for_delete = false; 

      } else { 
       Log.d("PLAYER_ADAPTER", "NOT_NULL ROW"); 
       holder = (PlayerHolder) row.getTag(); 
      } 
      holder.seeded_button.setOnClickListener(new OnClickListener() { 
       // 
       // Here is the magic sauce that makes it work. 
       // 
       private int pos = position; 

       public void onClick(View v) { 
        ImageButton b = (ImageButton) v; 
        if (b.isSelected()) { 
         b.setSelected(false); 
         players_array.get(pos).marked_for_delete = false; 
        } else { 
         b.setSelected(true); 
         players_array.get(pos).marked_for_delete = true; 
        } 
       } 
      }); 
      Player p = data.get(position); 
      holder.player_name.setText(p.name); 
      holder.player_number.setText(String.valueOf(position+1)); 
      holder.seeded_button 
        .setSelected(players_array.get(position).marked_for_delete); 
      return row; 
     } 

    } 

    static class PlayerHolder { 
     TextView player_number; 
     TextView player_name; 
     ImageButton seeded_button; 
    } 
+0

Если мне это нравится, я получаю щелчок для этого изображения ImageView, но onItemClick исчезает, то есть щелчок всех других областей элемента (mListView.setOnItemClickListener (this);) Можно ли комбинировать эти два без добавления элемента-слушателя для ll в этом конкретном элементе? – Henrik

+0

Нет причин, по которым вы не можете использовать оба подхода. Но имейте в виду, что нет способа узнать, какой ребенок был нажат в itemClicked, потому что ti просто дает вам представление и позицию. Таким образом, вы получите ложные срабатывания. Но не слишком много работы, чтобы добавить слушателей к другим дочерним представлениям в строке. Такой подход я бы предложил, если функция itemSelect не должна быть вызвана во всех случаях, в том числе при щелчке по области с кликом clickListener. –

0

Я придумал универсальное и многоразовое решение. Вместо расширения конкретного адаптера списка и модификации метода getView() я создал новый класс, реализующий интерфейс ListAdapter, который слепо пересылает почти все на другой ListAdapter, за исключением getView(). Это выглядит следующим образом:

public class SubClickableListAdapter implements ListAdapter { 

    public static interface OnSubItemClickListener { 
     public void onSubItemClick(View subView, int position); 
    } 
    private ListAdapter other; 

    private SparseArray<OnSubItemClickListener> onClickListeners; 

    public SubClickableListAdapter(ListAdapter other) { 
     this.other = other; 
     onClickListeners = new SparseArray<OnSubItemClickListener>(); 
    } 

    public void setOnClickListener(int id, OnSubItemClickListener listener) { 
     onClickListeners.put(id, listener); 
    } 

    public void removeOnClickListener(int id) { 
     onClickListeners.remove(id); 
    } 

    @Override 
    public View getView(final int position, View convertView, ViewGroup parent) { 
     View view = other.getView(position, convertView, parent); 
     for(int i = 0; i < onClickListeners.size(); i++) { 
      View subView = view.findViewById(onClickListeners.keyAt(i)); 
      if (subView != null) { 
       final OnSubItemClickListener listener = onClickListeners.valueAt(i); 
       if (listener != null) { 
        subView.setOnClickListener(new OnClickListener() { 
         @Override 
         public void onClick(View v) { 
          listener.onSubItemClick(v, position); 
         } 
        }); 
       } 
      } 
     } 
     return view; 
    } 

    // other implemented methods 
} 

Другие реализованные методы просто выглядеть следующим образом одно:

@Override 
public Object getItem(int position) { 
    return other.getItem(position); 
} 

Чтобы использовать его, просто экземпляр его подачи любого другого ListAdapter (будь то ArrayAdapter или SimpleCursorAdapter или что-нибудь еще). Затем вызовите setOnClickListener() для каждого вида, где вы хотите прослушивать клики, указав свой идентификатор в параметре id, и ваш слушатель в параметре listener. Чтобы получить идентификатор строки для выбранной строки, вызовите метод ListView (getItemIdAtPosition(position)), который вам нужно получить другим способом, поскольку он не указан как параметр для вашего обратного вызова, но это не должно быть большой проблемой в большинстве случаев).

Преимущество этого решения в том, что его можно использовать с любым ListAdapter. Поэтому, если ваше приложение имеет несколько ListView, каждый из которых использует разные основные виды или даже различные адаптеры, вам не нужно создавать новый класс адаптера для каждого из них.

Проблема с этим будет такой же, как со всеми другими решениями: OnItemClick() из ListView не будет вызываться, если вы нажмете на представление, на которое вы зарегистрировали прослушиватель. Для просмотров, которые вы не зарегистрировали слушателя, этот обратный вызов будет вызван.Так, например, у вас есть активность для вашего элемента списка, который содержит два текстовых поля и кнопку, и вы регистрируете прослушиватель для кнопки, а затем нажатие на кнопку не вызывает OnItemClick() из ListView, но вместо этого ваш обратный вызов , Нажав нигде, звоните OnItemClick().

10

Я использовал идею от Miga's Hobby Programming.

Ключ вызывает функцию performItemClick() из нового слушателя onClick. Это позволяет перейти к onItemClick(), который уже используется для списка. Это так быстро и легко, я чувствую, что обманываю.

Вот GetView(), из адаптера списка:

@Override 
public View getView(final int position, View convertView, final ViewGroup parent) { 

    // Check if an existing view is being reused, otherwise inflate the view 
    if (convertView == null) { 
     convertView = LayoutInflater.from(getContext()).inflate(R.layout.one_line, parent, false); 
    } 

    // This chunk added to get textview click to register in Fragment's onItemClick() 
    // Had to make position and parent 'final' in method definition 
    convertView.findViewById(R.id.someName).setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      ((ListView) parent).performItemClick(v, position, 0); 
     } 
    }); 
    // do stuff... 
} 

И в onItemClick():

@Override 
public void onItemClick(AdapterView adapterView, View view, int position, long id) { 

    long viewId = view.getId(); 

    if (viewId == R.id.someName) { 
     Toast.makeText(getActivity(), "someName item clicked", Toast.LENGTH_SHORT).show(); 
    } 
    else { 
     Toast.makeText(getActivity(), "ListView clicked: " + id, Toast.LENGTH_SHORT).show(); 

    } 
} 
+0

чистое решение, которое отделяет распространение событий от обработки событий – younes0

Смежные вопросы