Custom ListView and context menu. How to get it?

不问归期 提交于 2019-11-28 04:25:07

I got solution, my friend helped me! Hope this information will helpful to someone. This is complete class code with ArrayAdapter and complex list layout and context menu.

   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
        }

    }
}

let me know if someone will find better approach. Thanks!

Following segments of code from nested class ComplexObjectAdapter, listed into Georgy Gobozov's answer, are not really needed:

    @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
}

It just works because inside the function setOnCreateContextMenuListener() from class View, it calls the function 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;
}

It means the problem can be solved setting the Long Clickable property for each child item, after it is created:

    @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;
    }

or it also can be solved setting this property in the XML layout file of the list view child elements, for example:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:longClickable="true">

    <!-- Child elements -->

</LinearLayout>
user2663241

Actually, you just need to make View long clickable by calling

v.setLongClickable(true);

It's not needed to set dummy setOnCreateContextMenuListener, because it's does just that - set's the item long clickable.

I don't think you want to attach a context menu to the specific listView items. By calling registerForContextMenu(getListView()) you should get that functionality for free. I would hook your application up to a debugger after you remove the contextMenu hooks from the adapter code and set a breakpoint inside of onCreateContextMenu(). My suspicion is that it is getting called but the layout that is being inflated is not what you expect.

The basic problem is that the item is being drawn on by the second item.xml layout - so the root element (LinearLayout) is what is being long pressed rather than what the original ListView provided. As such, when you inflate the item.xml layout, you need to call the setOnCreateContextMenuListener as you have in fact done in the second example. The problem with this is that there is no way for the layout from item.xml (which is a LinearLayout) to communicate back to the Activity which position was chosen. This is because the LinearLayout does not override the getContextMenuInfo() method which in a ListView returns a AdapterView.AdapterContextMenuInfo (as everyone seems to coerce their ContextMenuInfo to).

So ideally, you want to create your own LinearLayout descendant that makes the getContextMenuInfo public, creates a fake one if there isn't one there, and when onCreateContextMenu is called in your custom adapter, it grabs it from your custom LinearLayout and puts the position/id in there, which your activity can pull out.

This is what I have done in my own application and it works very nicely and is a generic solution - you can in fact put anything you like in there as long as it implements the ContextMenuInfo interface (which is just a marker interface).

I don't now why, it is necessary to set a null OnCreateContextMenuListener on every list row (in addition to registerForContextMenu(...) and implement onCreateContextMenu(...) and onContextItemSelected(...)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!