Запуск PopupMenu с помощью кнопок внутри элемента ListView - программирование
Подтвердить что ты не робот

Запуск PopupMenu с помощью кнопок внутри элемента ListView

Типы меню

Позвольте мне изложить различие между контекстными меню и всплывающими меню (взято из здесь):

  • PopupMenu - это модальное меню, привязанное к представлению

  • Контекстное меню [я говорю конкретно о плавающих контекстных меню] предлагает действия, которые влияют на конкретный элемент или контекстный фрейм в пользовательском интерфейсе. Вы можете предоставить контекстное меню для любого вида, но чаще всего они используются для элементов в ListView, GridView или других коллекциях представлений, в которых пользователь может выполнять прямые действия над каждым элементом.

В качестве руководства, документы различают их использование: - PopupMenu: "Предоставление меню стиля переполнения для действий, относящихся к определенному контенту (например, заголовкам электронной почты Gmail..." - ContextualMenu: "Для действий, влияющих на выбранный контент..."

Я после визуального просмотра и взаимодействия PopupMenu.

Моя настройка (и цель)

У меня есть ListView, где у каждого элемента есть кнопка справа.

--------------------
| Item 1        [ ]|
--------------------
--------------------
| Item ...      [ ]|
--------------------
--------------------
| Item n        [ ]|
--------------------

сохраняя onItemClick для каждого ListItem, я бы хотел использовать кнопку для запуска PopupMenu.

Действия в PopupMenu перечислены в my_menu.xml, поэтому единственное различие между каждым экземпляром меню - это элемент, на который он был нажат.

Что я пробовал

Я попытался переопределить getView() в моем ListAdapter, чтобы добавить OnClickListener для каждой кнопки, как этот пост. Результат был непоследовательным; не все клики регистрировались (возможно, из-за переработки - я еще не пробовал это исправление), но в целом подход казался сжатым, учитывая легкость определения контекста меню для просмотра списка.

Я также попытался добавить контекстное меню, которое было мертвым легко, за исключением того, что оно срабатывало только при длительном нажатии элемента списка.

Способ getView() - путь или есть что-то проще?

В моем списке, у меня есть кнопка мини-переполнения для каждого элемента, похожая на каждый трек в плейлисте для Play Music. Они отображают PopupMenu для каждого элемента там, когда вы нажимаете кнопку переполнения. Длительное нажатие ничего не делает.

Я бы хотел, но не уверен, как это сделать? На странице меню только описано, как включить всплывающее меню для отдельных просмотров.

registerForContextMenu(ListView lv) - единственное, что я видел, что я могу применить ко всему ListView, но похоже, что он работает только с длинными нажатиями всего списка. Есть ли способ связать это событие с кликом определенного вида в элементе списка (при сохранении элемента списка для остальной части элемента списка)?

Я попытался установить OnClickListener в getView(), как описано здесь, но он чувствует себя squisty для PopupMenu, и не все клики регистрировались каждый раз (это было последовательно противоречиво).

Обновление - с возвратом getView() снова

Когда я создаю StacksCursorAdapter, я передаю экземпляр StackViewFragment, который хранится как поле (поэтому его можно назначить представлениям в качестве прослушивателя кликов).

ViewHolder - класс с public final ImageView miniOverflow

StacksCursorAdapter (расширяет SimpleCursorAdapter):

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View row = super.getView(position, convertView, parent);
    ViewHolder holder = (ViewHolder) row.getTag();

    if (holder == null) {
        holder = new ViewHolder(row);
        row.setTag(holder);
    }
    holder.miniOverflow.setTag(R.id.tag_stack_position, position);
    holder.miniOverflow.setOnClickListener(listener);

    return row;
}

StackViewFragment (расширяет ListFragment, реализует View.OnClickListener):

@Override
public void onClick(View v) {
    Log.d(TAG, "mini overflow clicked");
    Log.d(TAG, "position:::::" + v.getTag(R.id.tag_stack_position));

    PopupMenu popup = new PopupMenu(getActivity(), v);
    MenuInflater inflater = popup.getMenuInflater();
    inflater.inflate(R.menu.menu_stackview_listitem, popup.getMenu());
    popup.show();
}

stack_list_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- a single row item for a Stacks listview -->
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:descendantFocusability="blocksDescendants"
        >

    ...
    <other views>
    ...


    <ImageView
            android:id="@+id/moreoverflow_btn"
            android:focusable="false"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:alpha="0.4"
            android:scaleType="fitCenter"
            android:src="@drawable/ic_menu_moreoverflow_black"
            android:layout_alignParentRight="true"
            />
</RelativeLayout>

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

Заключение (решение eskimoapps.com отмечено как правильно)

Таким образом, не было никаких проблем с несколькими кликами, за исключением того, что моя целевая точка касания не была в том месте, которое я ожидал, - ресурс изображения, который я использовал для мини-переполнения, взвешен вправо (так что три точки находятся рядом с правый край касательной цели), означающий, что я просто пропустил его половину времени (facepalm).

Переопределение getView(), как я показал выше, кажется, работает нормально, и на самом деле getView() можно изменить еще дальше, установив OnClickListener только в случае необходимости (но определенно, обновление тега должно выполняться для каждого а не только вновь созданные):

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View row = super.getView(position, convertView, parent);
    ViewHolder holder = (ViewHolder) row.getTag();

    if (holder == null) {
        holder = new ViewHolder(row);
        row.setTag(holder);
        holder.miniOverflow.setOnClickListener(listener);
    }
    holder.miniOverflow.setTag(R.id.tag_stack_position, position);

    return row;
}
4b9b3361

Ответ 1

Переопределение getView - это правильный подход, и вы на правильном пути, думая, что переработка просмотров усложнит ситуацию (если вы фактически пересматриваете представления).

Поскольку методов getOnClickListener нет, вам просто нужно будет установить новый OnClickListener для переработанных представлений, а также неинтегрированных (если вы не хотите создавать пользовательский вид, который реализует getOnClickListener).

Ответ 2

Если бы я хотел сделать это сегодня, я бы использовал шаблон HolderView.

Для шаблона HolderView требуется настраиваемая view/viewgroup (класс, расширяющий группу View/Viewgroup, которая является корнем вашего макета списка/сетки, с 3 стандартными конструкторами представлений).

Общая идея заключается в том, что адаптер не несет ответственности за непосредственное обновление содержимого представления; вместо этого он даст представление всей необходимой информации, чтобы обновить себя:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = createView(parent);
    }

    updateView((GalleryItemView) convertView, position);

    return convertView;
}

private View createView(ViewGroup parent) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());

    return inflater.inflate(R.layout.gallery_item_view, null, false);
}

private void updateView(GalleryItemView view, int position) {
    GalleryElement item = getItem(position);

    view.updateWith(position, item, listener);
}

На мой взгляд:

public class GalleryItemView extends ImageView {

    public GalleryItemView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    void updateWith(int position, final GalleryElement item, final GalleryAdapter.GalleryItemListener listener) {
        if (position % 2 == 0) {
            setBackgroundColor(getColor(android.R.color.holo_orange_dark));
        } else {
            setBackgroundColor(getColor(android.R.color.holo_orange_light));
        }

        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.onGalleryItemClicked(item.id);
            }
        });
    }

    @ColorInt
    private int getColor(@ColorRes int colorRes) {
        return getResources().getColor(colorRes);
    }

}