Подтвердить что ты не робот

Как узнать, какой вид внутри определенного элемента ListView, который был нажат

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

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

/Хенрик

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

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

Ответ 1

Вы можете это сделать. Вам нужно изменить метод 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
    }
    ...
}

Ответ 2

Я использовал идею из Miga 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 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();

   }
}

Ответ 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;
    }

Ответ 4

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

Ответ 5

Я придумал универсальное и многоразовое решение. Вместо расширения конкретного адаптера списка и модификации метода 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. Чтобы получить идентификатор строки для щелкнувшей строки, вызовите метод getItemIdAtPosition(position) вашего ListView (который вы должны получить другим способом, потому что он не указан как параметр для вашего обратного вызова, но это не должно быть большой проблемой в большинстве случаев).

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

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