自定义 ListView 和上下文菜单。如何获取它?

16
我在我的应用程序中有两个布局文件。同时,我有一个继承自ListActivity的Activity。这个活动的每个项目都会考虑item.xml布局文件。我试图在对项目进行长按时获取上下文菜单,但我没有看到它。
在我的活动中,我尝试注册上下文菜单(getListView())并重写两种方法。
  @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle bundle = this.getIntent().getExtras();
        registerForContextMenu(getListView());
        new PopulateAdapterTask().execute(ACTION_SELECT);   
     }

    @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.context_menu, menu);
        }


        @Override
        public boolean onContextItemSelected(MenuItem item) {
            AdapterView.AdapterContextMenuInfo info;
            try {
                info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
            } catch (ClassCastException e) {
                return false;
            }
            long id = getListAdapter().getItemId(info.position);
            Log.d(TAG, "id = " + id);
            return true;
        }

Main.xml

    <?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@android:id/tabhost"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent">
    <LinearLayout
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:padding="5dp">
        <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"/>
        <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:padding="5dp">
            <ListView
                    android:id="@+id/list"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    />

        </FrameLayout>

    </LinearLayout>

</TabHost>

Item.xml

<?xml version="1.0" encoding="utf-8"?>
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
        >
    <ImageView
            android:id="@+id/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
    <TextView
            android:id="@+id/info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="15sp"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:scrollHorizontally = "true"
            android:maxWidth="200dp"
            />


    <LinearLayout
             android:layout_width="fill_parent"
             android:layout_height="fill_parent"
             android:gravity="right"
            >
        <ImageButton
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="fill_parent"
                android:background="@null"
                android:paddingRight="10dp"                
                android:paddingLeft="10dp"


                />
    </LinearLayout>

</LinearLayout>

这一切都不起作用。也许原因在LinearLayout中?我还发现了类似的主题Android: Context menu doesn't show up for ListView with members defined by LinearLayout?,但我的列表项更加复杂。

如何在我的情况下获取上下文菜单?

同时,在我的活动中,我有一个内部类扩展ArrayAdapter。在这个类的getView方法中,我可以在每个View上设置OnCreateContextMenuListener,之后上下文菜单就会出现,但我不知道如何处理项目点击。如果我尝试在onContextItemSelected方法中执行此操作,则item.getMenuInfo()对象始终为null,因此无法从中获取任何信息。

private class ChannelAdapter extends ArrayAdapter<Channel> {

        private List<Channel> channels;

        public ChannelAdapter(Context context, int textViewResourceId, List<Channel> objects) {
            super(context, textViewResourceId, objects);
            this.channels = objects;
        }


        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.station_item, null);
            }


                v.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
                    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
                        MenuInflater inflater = getMenuInflater();
                        inflater.inflate(R.menu.context_menu, menu);
                    }
                });

谢谢。希望你能帮助我。

6个回答

33

我得到了解决方案,是我的朋友帮助我的!希望这个信息能对某些人有所帮助。 这是完整的类代码,包括ArrayAdapter、复杂列表布局和上下文菜单。

   public class ComplexListActivity extends ListActivity {
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setListAdapter(new ComplexObjectAdapter(this, R.layout.item, getComplexObjects()));
        registerForContextMenu(getListView());
    }

    private List getComplexObjects() {
        List<ComplexObject> list = new ArrayList<ComplexObject>();
        list.add(new ComplexObject("1", "1", getResources().getDrawable(R.drawable.icon)));
        list.add(new ComplexObject("2", "2", getResources().getDrawable(R.drawable.icon)));
        list.add(new ComplexObject("3", "3", getResources().getDrawable(R.drawable.icon)));
        list.add(new ComplexObject("4", "4", getResources().getDrawable(R.drawable.icon)));
        list.add(new ComplexObject("5", "5", getResources().getDrawable(R.drawable.icon)));
        list.add(new ComplexObject("6", "6", getResources().getDrawable(R.drawable.icon)));
        list.add(new ComplexObject("7", "7", getResources().getDrawable(R.drawable.icon)));
        list.add(new ComplexObject("8", "8", getResources().getDrawable(R.drawable.icon)));
        list.add(new ComplexObject("9", "9", getResources().getDrawable(R.drawable.icon)));
        return list;
    }


    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.context_menu, menu);
    }


    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterView.AdapterContextMenuInfo info;
        try {
            info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        } catch (ClassCastException e) {
            Log.e("", "bad menuInfo", e);
            return false;
        }
        long id = getListAdapter().getItemId(info.position);
        Log.d("", "id = " + id);
        Toast.makeText(this, "id = " + id, Toast.LENGTH_SHORT).show();
        return true;
    }

    private class ComplexObjectAdapter extends ArrayAdapter<ComplexObject> implements View.OnCreateContextMenuListener {

        private List<ComplexObject> objects;

        public ComplexObjectAdapter(Context context, int textViewResourceId, List<ComplexObject> objects) {
            super(context, textViewResourceId, objects);
            this.objects = objects;
        }


        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.item, null);
            }
            final ComplexObject o = objects.get(position);
            if (o != null) {

                TextView textlInfo = (TextView) v.findViewById(R.id.info);
                textlInfo.setText(o.getName());

                ImageView channelIcon = (ImageView) v.findViewById(R.id.icon);
                channelIcon.setAdjustViewBounds(true);
                channelIcon.setMaxHeight(30);
                channelIcon.setMaxWidth(30);
                channelIcon.setImageDrawable(o.getLogo());


                ImageButton button = (ImageButton) v.findViewById(R.id.button);
                button.setImageResource(R.drawable.icon);
                v.setOnCreateContextMenuListener(this);

            }
            return v;
        }

         public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
          // empty implementation
        }

    }
}

如果有更好的方法,请告诉我。谢谢!


7
我发现了一个小改进 - v.setOnCreateContextMenuListener(null) - m039

7

以下代码段来自嵌套类ComplexObjectAdapter,列在Georgy Gobozov的答案中,实际上并不需要:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;
        if (v == null) {
            LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.item, null);
        }
        final ComplexObject o = objects.get(position);
        if (o != null) {

            TextView textlInfo = (TextView) v.findViewById(R.id.info);
            textlInfo.setText(o.getName());

            ImageView channelIcon = (ImageView) v.findViewById(R.id.icon);
            channelIcon.setAdjustViewBounds(true);
            channelIcon.setMaxHeight(30);
            channelIcon.setMaxWidth(30);
            channelIcon.setImageDrawable(o.getLogo());


            ImageButton button = (ImageButton) v.findViewById(R.id.button);
            button.setImageResource(R.drawable.icon);
            // NOT NEEDED
            v.setOnCreateContextMenuListener(this);

        }
        return v;
    }

// NOT NEEDED
public void onCreateContextMenu(ContextMenu contextMenu, View view,  ContextMenu.ContextMenuInfo contextMenuInfo) {
            // empty implementation
}

这是因为在View类的setOnCreateContextMenuListener()函数内部,它调用了setLongClickable(true)函数:

/**
 * Register a callback to be invoked when the context menu for this view is
 * being built. If this view is not long clickable, it becomes long clickable.
 *
 * @param l The callback that will run
 *
 */
public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
    if (!isLongClickable()) {
        setLongClickable(true);
    }
    mOnCreateContextMenuListener = l;
}

这意味着问题可以通过为每个子项设置“长按可点击(Lonng Clickable)”属性来解决,子项创建后即可设置。
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;
        if (v == null) {
            LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.item, null);
            // SET LONG CLICKABLE PROPERTY
            v.setLongClickable(true);
        }
        final ComplexObject o = objects.get(position);
        if (o != null) {

            TextView textlInfo = (TextView) v.findViewById(R.id.info);
            textlInfo.setText(o.getName());

            ImageView channelIcon = (ImageView) v.findViewById(R.id.icon);
            channelIcon.setAdjustViewBounds(true);
            channelIcon.setMaxHeight(30);
            channelIcon.setMaxWidth(30);
            channelIcon.setImageDrawable(o.getLogo());


            ImageButton button = (ImageButton) v.findViewById(R.id.button);
            button.setImageResource(R.drawable.icon);
            // NOT NEEDED
            // v.setOnCreateContextMenuListener(this);

        }
        return v;
    }

或者也可以通过在列表视图子元素的xml布局文件中设置此属性来解决问题,例如:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:longClickable="true">

    <!-- Child elements -->

</LinearLayout>

很好,你知道是否可以让它在点击和长按时也高亮显示吗? - NoBugs

1

实际上,您只需要调用 View 的长按点击方法即可使其变为可长按。

v.setLongClickable(true);

不需要设置虚拟的 setOnCreateContextMenuListener,因为它只会使项目可长按。

0
基本问题是第二个item.xml布局正在对其进行绘制 - 因此根元素(LinearLayout)是被长按的,而不是原始ListView提供的内容。因此,在填充item.xml布局时,您需要调用setOnCreateContextMenuListener,就像您在第二个示例中所做的那样。但是,这样做的问题在于,item.xml布局(它是一个LinearLayout)无法向Activity通信哪个位置被选择。这是因为LinearLayout没有覆盖getContextMenuInfo()方法,在ListView中返回AdapterView.AdapterContextMenuInfo(因为每个人似乎都将他们的ContextMenuInfo强制转换为此类型)。
因此,理想情况下,您需要创建自己的LinearLayout后代,使getContextMenuInfo公开,如果没有,则创建一个虚拟的ContextMenuInfo,并且当在您的自定义适配器中调用onCreateContextMenu时,它从您的自定义LinearLayout中获取并将位置/ ID放入其中,您的Activity可以提取出来。
这就是我在自己的应用程序中所做的,它非常好用,是一种通用解决方案 - 实际上,您可以将任何实现ContextMenuInfo接口(这只是一个标记接口)的内容放入其中。

0
我认为您不想将上下文菜单附加到特定的listView项。通过调用registerForContextMenu(getListView()),您应该免费获得该功能。在从适配器代码中删除contextMenu钩子并在onCreateContextMenu()内设置断点后,我建议您将应用程序连接到调试器。我的猜测是它正在被调用,但被填充的布局不是您所期望的。

不,我不这么认为。我在onCreateContextMenu方法中设置了断点,并在调试模式下运行我的应用程序,但应用程序的执行没有到达任何断点。 - Georgy Gobozov

0

我不知道为什么,在每个列表行上设置一个null OnCreateContextMenuListener是必要的(除了registerForContextMenu(...)和实现onCreateContextMenu(...)和onContextItemSelected(...))。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接