首先,我要概述上下文菜单和弹出菜单(取自here)之间的区别:
PopupMenu是一个模式菜单锚定到视图
上下文菜单[我特别谈论浮动上下文菜单]提供了影响UI中特定项目或上下文框架的操作。您可以为任何视图提供上下文菜单,但它们通常用于ListView,GridView或其他视图集合中的项目,用户可以在其中对每个项目执行直接操作。
作为指导,文档区分了它们的用途: - PopupMenu:“为与特定内容相关的操作提供溢出式菜单(例如Gmail的电子邮件标题......” - ContextualMenu:“对于影响所选内容的行动......”
我正在追踪 PopupMenu 的视觉外观和互动。
我有一个ListView,其中每个项目右侧都有一个按钮。
--------------------
| Item 1 [ ]|
--------------------
--------------------
| Item ... [ ]|
--------------------
--------------------
| Item n [ ]|
--------------------
同时保留每个ListItem的onItemClick
,我想利用该按钮触发PopupMenu
。
PopupMenu
中的操作列在my_menu.xml
中,因此菜单的每个实例之间的唯一区别就是它被点击的项目。
根据this post,我已尝试在getView()
覆盖ListAdapter
为每个按钮添加OnClickListener
。结果不一致;并非所有的点击都在注册(可能是由于回收 - 我还没有tried this fix)但总的来说这种方法似乎很狡猾,考虑到为列表视图指定上下文菜单的简便性。
我还尝试添加一个上下文菜单,这个菜单很简单,只有当列表项被长按时才触发。
getView()
方法是可行的还是更简单?
在我的列表视图中,我为每个项目设置了一个迷你溢出按钮,类似于播放音乐的播放列表中的每个曲目。当您单击溢出按钮时,它们会为每个项目显示PopupMenu。长按什么都不做。
我喜欢那个但不确定怎么样?菜单页面仅详细说明了如何为各个视图启用弹出菜单。
registerForContextMenu(ListView lv)
是我见过的唯一可以应用于整个ListView的东西,但这似乎只适用于整个列表的长按。有没有办法将该事件挂钩到单击列表项中的特定视图(同时仍然保持列表项单击列表项的其余部分)?
我已尝试在onClickListener
中设置getView()
,如此处所述,但PopupMenu感觉不好,而且并非所有点击都是每次都注册(一直不一致)。
当我创建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>
此结点的问题是,我必须偶尔点击几次以点击注册(在按钮上)。这是没有滚动的,因此它与视图回收无关。
因此,除了我的触摸目标不在我期望的位置之外,多次点击没有问题 - 我用于迷你溢出的图像资源被加权到右边(这样三个点就在附近了触摸目标的右边缘)意味着我只是缺少它的一半时间(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;
}
答案 0 :(得分:3)
覆盖getView是正确的方法,并且您正在认为回收视图会使事情变得复杂(如果您实际上是回收视图)。
由于没有getOnClickListener方法,您只需为回收的视图以及未经过充当的视图设置新的OnClickListener(如果您不想创建实现getOnClickListener的自定义视图)。
答案 1 :(得分:1)
如果我今天想这样做,我会使用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);
}
}