【问题标题】:Custom ListView and context menu. How to get it?自定义 ListView 和上下文菜单。如何得到它?
【发布时间】:2011-04-27 17:47:07
【问题描述】:

我的应用中有两个布局文件。我也有 Activity 扩展 ListActivity。此活动的每个项目看起来都考虑 item.xml 布局文件。我试图在长按项目时获取上下文菜单,但我没有看到它。

在我的活动中,我尝试 registerForContextMenu(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>

项目.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 方法的这个类中,我可以在每个视图上设置 OnCreateContextMenuListener,在出现上下文菜单之后,但我不知道如何处理项目点击。如果我试图在 onContextItemSelected 方法中执行此操作,item.getMenuInfo() 对象始终为空,我无法从中获取一些信息。

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

谢谢。希望得到您的帮助。

【问题讨论】:

    标签: android listview layout contextmenu


    【解决方案1】:

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

    【讨论】:

    • 不,我不这样认为。我在 onCreateContextMenu 方法中设置断点并在调试模式下运行我的应用程序,但应用程序执行没有到达任何断点。
    【解决方案2】:

    我得到了解决方案,我的朋友帮助了我!希望这些信息对某人有所帮助。 这是包含 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
            }
    
        }
    }
    

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

    【讨论】:

    • 我发现了一点改进 - v.setOnCreateContextMenuListener(null).
    • 谢谢@Georgy。这对我有帮助
    【解决方案3】:

    基本问题是第二个 item.xml 布局正在绘制该项目 - 所以根元素 (LinearLayout) 是被长按的,而不是原始 ListView 提供的。因此,当您扩展 item.xml 布局时,您需要调用 setOnCreateContextMenuListener,正如您在第二个示例中所做的那样。这样做的问题是 item.xml(它是一个 LinearLayout)中的布局无法将选择的位置传达回 Activity。这是因为 LinearLayout 不会覆盖在 ListView 中返回 AdapterView.AdapterContextMenuInfo 的 getContextMenuInfo() 方法(因为每个人似乎都将其 ContextMenuInfo 强制为)。

    因此,理想情况下,您希望创建自己的 LinearLayout 后代,使 getContextMenuInfo 公开,如果没有,则创建一个假的,当在您的自定义适配器中调用 onCreateContextMenu 时,它会从您的自定义 LinearLayout 中获取它并将位置/id 放在那里,您的活动可以将其拉出。

    这是我在自己的应用程序中所做的,它工作得非常好,是一个通用的解决方案——事实上,你可以把任何你喜欢的东西放在那里,只要它实现了 ContextMenuInfo 接口(这只是一个标记接口) .

    【讨论】:

      【解决方案4】:

      Georgy Gobozov 的答案中列出的嵌套类 ComplexObjectAdapter 中的以下代码段并不是真正需要的:

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

      这意味着在创建每个子项后,为每个子项设置 Long 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>
      

      【讨论】:

      • 很好,你知道是否可以让它在点击和长按时突出显示?
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        其实你只需要通过调用让View长点击就可以了

        v.setLongClickable(true);
        

        不需要设置 dummy setOnCreateContextMenuListener,因为它就是这样做的 - 设置是可长按的项目。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-12-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-21
          • 2021-06-04
          • 2022-06-13
          相关资源
          最近更新 更多