在ListView项中触发带有按钮的PopupMenu



菜单类型

让我首先概述上下文菜单和弹出菜单之间的区别(取自此处):

  • PopupMenu是一个模式菜单锚定到View

  • 上下文菜单[我说的是浮动上下文菜单]提供了影响UI中特定项目或上下文框架的操作。您可以为任何视图提供上下文菜单,但它们最常用于ListView、GridView或其他视图集合中的项,用户可以在这些视图集合中对每个项执行直接操作。

作为指导,这些文档区分了它们的用途:-PopupMenu:"为特定内容相关的操作提供溢出式菜单(如Gmail的电子邮件标题……"-ContextualMenu:"对于影响所选内容的操作…"

我关注的是PopupMenu的视觉外观和交互。

我的设置(和目标)

我有一个ListView,每个项目的右边都有一个按钮。

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

在为每个ListItem保留onItemClick的同时,我想利用该按钮来触发PopupMenu

PopupMenu中的操作列在my_menu.xml中,因此菜单的每个实例之间的唯一区别是单击的项目。

我尝试过的

根据这篇文章,我已经尝试在ListAdapter中重写getView(),为每个按钮添加一个OnClickListener。结果不一致;并不是所有的点击都在注册(也许是因为回收——我还没有尝试过这个修复程序),但考虑到为列表视图指定上下文菜单的方便性,总的来说,这种方法似乎很糟糕。

我还尝试添加了一个上下文菜单,这非常容易,只是它只有在长按列表项时才会触发。

getView()方法是可行的,还是有更简单的方法?

在我的列表视图中,我为每个项目都有一个迷你溢出按钮,类似于播放音乐播放列表中的每个曲目。当你点击溢出按钮时,它们会为每个项目显示一个弹出菜单。长按毫无作用。

我想要,但不确定怎么做?菜单页面仅详细说明如何为各个视图启用弹出菜单。

registerForContextMenu(ListView lv)是我见过的唯一可以应用于整个ListView的东西,但它似乎只适用于长按整个列表。是否有方法将该事件挂接到列表项中特定视图的单击(同时仍保持列表项对列表项其余部分的单击)?

我曾尝试在getView()中设置onClickListener,如这里所述,但对于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;
}

重写getView是正确的方法,而且您认为回收视图会使事情复杂化(如果您实际上是在回收视图),这是正确的做法。

由于没有getOnClickListener方法,您只需要为回收的视图和未关联的视图设置一个新的OnClickListeners(如果您不想创建实现getOnClick监听器的自定义视图)。

如果我今天想这样做,我会使用HolderView模式。

HolderView模式需要一个自定义视图/视图组(一个扩展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);
    }
}

相关内容

  • 没有找到相关文章

最新更新