代码之家  ›  专栏  ›  技术社区  ›  Piotr Aleksander Chmielowski

什么时候用菜单项id调用findViewById以确保它不为空?

  •  6
  • Piotr Aleksander Chmielowski  · 技术社区  · 6 年前

    我正在对菜单进行充气,并尝试按以下方式查找其中一个菜单项的视图:

     @Override
     public boolean onCreateOptionsMenu(final Menu menu) {
         getMenuInflater().inflate(R.menu.main, menu);
    
         // will print `null`
         Log.i("TAG", String.valueOf(findViewById(R.id.action_hello)));
         return true;
     }
    

    在结果中 null 是用Logcat打印的。但是如果我在打电话之前加上一些延迟 findViewById View 对象:

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(final Void... voids) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }
    
            @Override
            protected void onPostExecute(final Void aVoid) {
                // will print correctly android.support.v7.view.menu.ActionMenuItemView...
                Log.i("TAG", String.valueOf(findViewById(R.id.action_hello)));
            }
        }.execute();
        return true;
    }
    

    当然,这个解决方案非常脏,最小延迟是未知的。是否有方法为菜单膨胀事件注册一些回调。换言之:我怎么能打电话 芬兰 使用菜单项id确保视图已经存在并且此调用不会返回 无效的 ?

    9 回复  |  直到 6 年前
        1
  •  1
  •   Nominalista    6 年前

    只是覆盖 public void onPrepareOptionsMenu(Menu menu) .

    文件上说:

    每次显示菜单时,都会在菜单显示之前调用它。 您可以使用此方法有效地启用/禁用项或 否则动态修改内容。

    的视图 Menu 在调用后创建 onCreateOptionsMenu(Menu) ,这就是为什么不能访问它的子视图。

        2
  •  1
  •   Nainal    6 年前

    有两种方法可以找到菜单项视图。

    第一条路 :-

    将其添加到菜单项xml中:-

    <item
        android:id="@+id/menuAdd"
        android:icon="@android:drawable/ic_menu_add"
        android:title="Add"
        app:showAsAction="always"
        app:actionViewClass="android.widget.ImageButton" />
    

    在onCreateOptions菜单方法中:-

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_item,menu);
        View menuView=menu.findItem(R.id.menuAdd).getActionView();
        Log.e("TAG", "onCreate: "+menuView );
        return true;
    }
    

    第二条路 :-

    here

        3
  •  1
  •   danny117    6 年前

    当代码完成时,或者事情没有按预期工作时,必须调用super方法。

    @Override
         public boolean onCreateOptionsMenu(final Menu menu) {
             getMenuInflater().inflate(R.menu.main, menu);
        super.onCreationOptionsMenu(menu);
             // calling the super completes the method now you code.
             Log.i("TAG", String.valueOf(findViewById(R.id.action_hello)));
             return true;
         }
    
        4
  •  1
  •   user8959091 user8959091    6 年前

    这样就可以得到菜单项的id及其actionview:

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
    
        MenuItem mi = menu.findItem(R.id.action_hello);
    
        int id = mi.getItemId();
    
        Log.i("TAG", String.valueOf(id));
    
        View actionView = mi.getActionView();
    
        if (actionView == null) {
            Log.i("TAG", "ActionView is null");
        } else {
            Log.i("TAG", "ActionView is NOT null");
        }
    
        return true;
    }
    
        5
  •  0
  •   ahasbini    6 年前

    邮寄 Runnable Handler 排队的人通常都会这样 可运行 在主UI线程完成当前正在执行的方法部分后执行 Activity 的生命周期,因此这将是一个很好的机会得到你想要的。我需要注意的是,这是一个可能失败的伎俩,如果低估和没有很好的测试,但它已经为我工作以来,我想出来了。

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        new Handler(getMainLooper()).post(new Runnable() {
    
            @Override
            public void run() {
                Logger.LogI(TAG, "run: " + findViewById(R.id.action_hello));
            }
        });
        return true;
    }
    
        6
  •  0
  •   Red M    6 年前

    创建全局变量以供将来使用:

    private ImageButton actionHelloView;
    

    onCreateOptionsMenu :

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            super.onCreateOptionsMenu(menu);
            getMenuInflater().inflate(R.menu.menu_main, menu);
            actionHelloView = (ImageButton) menu.findItem(R.id.action_hello).getActionView();
    
    
            Log.i("the view is: ", String.valueOf(actionHelloView));
            return true;
        }
    

    把这个放到XML中:

    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        tools:context="com.test.teststack.MainActivity">
        <item
            android:id="@+id/action_hello"
            android:title="@string/action_settings"
            app:showAsAction="never"
            app:actionViewClass="android.widget.ImageButton"/>
    </menu>
    

    注:
    根据您的API版本,您可以在 app:actionViewClass android:actionViewClass 在您的xml中。

    07-25 17:55:07.138 9491-9491/?I/theviewis::android.widget.ImageButton{5542a5c VFED..C。。。。。。一、 0,0-0,0 f080011应用程序:id/action_hello}

        7
  •  0
  •   Cheticamp    6 年前

    您没有提到为什么要查找菜单项视图,这可能与您要查找的答案有关。但是,如果您想使用 findViewById() 要找到菜单视图,这是一种方法。下面的示例只是将菜单图标从“X”更改为复选标记。

    ViewTreeObserver.OnGlobalLayoutListener 将在下列代码中的工具栏布局之后立即调用。这和你的延迟是一样的,但这是一种可以接受的处理方式。

    或者,程序可以调用 menu.findItem(R.id.action_hello) 在里面 onPrepareOptionsMenu() . 不幸的是,此时工具栏尚未完全形成,因此 会失败的。

    主活动.xml

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            final Toolbar toolbar = findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
            setTitle("");
            toolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    ActionMenuItemView view = toolbar.findViewById(R.id.action_hello);
                    if (view != null) {
                        // onGlobalLayout may be called before toolbar is fully defined.
                        Log.d("onGlobalLayout", "<<<<view is not null");
                        // Uncomment this view to make the change to the icon here. Android Studio
                        // will complain about a library group, but that can be ignored for this demo.
                        // view.animate() might be a better demo here.
                        view.setIcon(getResources().getDrawable(R.drawable.ic_check));
                        toolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    }
                }
            });
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
    
        @Override
        public boolean onPrepareOptionsMenu(Menu menu) {
            // Uncomment the following line to change the icon here.
    //        menu.findItem(R.id.action_hello).setIcon(getResources().getDrawable(R.drawable.ic_check));
            return true;
        }
    }
    

    主.xml

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
        <item
            android:id="@+id/action_hello"
            android:icon="@drawable/ic_x"
            android:title="Item1"
            app:showAsAction="ifRoom" />
    </menu>
    

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:layoutDirection="ltr"
            android:padding="0px"
            android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
            app:contentInsetEnd="0px"
            app:contentInsetEndWithActions="0px"
            app:contentInsetLeft="0px"
            app:contentInsetRight="0px"
            app:contentInsetStart="0px"
            app:contentInsetStartWithNavigation="0px"
            app:logo="@null"
            app:title="@null"
            app:titleMargin="0px"
            app:titleTextColor="#757575"
            tools:ignore="UnusedAttribute"
            tools:title="toolbar">
        </android.support.v7.widget.Toolbar>
    
    </FrameLayout>
    
        8
  •  0
  •   David Wasser    6 年前

    onCreateOptionsMenu() ,当你打电话

    findViewById(R.id.action_hello)
    

    这将在 View null .

    Runnable 给一个 Handler 会发现 查看 onCreateOptions菜单() Android已经将菜单视图附加到内容根目录。你不应该耽搁的。您只需要等到框架完成选项菜单的创建。

        9
  •  0
  •   Nick Cardoso    6 年前

    扩展菜单并不是异步的,因此您可以在执行此操作的地方找到该项-尽管 onPrepareOptionsMenu 可能是比较正确的地方。

    你不能做的是利用 findItemById ,它将在当前显示的布局(而不是折叠的菜单)中查找,而您必须使用 menu.findItem() (或 menu.getItem() )

    如果确实需要使用项的视图(与MenuItem对象相比),可以使用 menu.findItem().getActionView()