Когда вызывать findViewById с идентификатором пункта меню, чтобы убедиться, что он не равен нулю? - PullRequest
0 голосов
/ 12 июня 2018

Я надуваю меню и пытаюсь найти вид одного из пунктов меню следующим образом:

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

Конечно, это решение очень грязное и минимальная задержка неизвестна.Есть ли способ зарегистрировать какой-нибудь обратный вызов для меню раздутого события.Другими словами: как я могу вызвать findViewById с идентификатором пункта меню, чтобы убедиться, что представление уже существует и этот вызов не вернет null?

Ответы [ 9 ]

0 голосов
/ 01 августа 2018

Таким образом, вы получаете идентификатор пункта меню и его 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;
}
0 голосов
/ 01 августа 2018

Вы должны вызывать метод 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;
     }
0 голосов
/ 31 июля 2018

В onCreateOptionsMenu(), когда вы вызываете

findViewById(R.id.action_hello)

, этот поиск в иерархии View начинается с вашего «корня контента».Поскольку накаченное вами меню еще не было прикреплено к корню контента, скорее всего, это вернет null.

. Вы сможете разместить Runnable на Handler, который найдетView вы хотите.Это следует вызывать после того, как вы вернетесь с onCreateOptionsMenu() и Android прикрепит представления меню к корню контента.Вам не нужно задерживаться.Вам просто нужно подождать, пока фреймворк завершит создание меню опций.

0 голосов
/ 01 августа 2018

Раздувание вашего меню не является асинхронным, поэтому вы можете найти элемент именно там, где вы это делаете - хотя onPrepareOptionsMenu, вероятно, является более правильным местом для этого.

То, что вы не можете сделать, это использовать findItemById, который выглядит в текущем отображаемом макете (не в свернутом меню), вместо этого вы должны использовать menu.findItem() (или menu.getItem())

Есливам действительно нужно работать с видом элемента (против объекта MenuItem), который вы можете использовать menu.findItem().getActionView()

0 голосов
/ 30 июля 2018

Существует два способа найти представление элемента меню.

Первый способ : -

Добавить actionViewClass в ваш элемент меню, чтобы вы могли получить представлениевозвращается getActionView.Так как getActionView () работает, только если есть actionView, определенный для menuItem.

Добавьте это в свой элемент меню xml: -

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

В методе onCreateOptionsMenu: -

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

Второй способ : -

Второй способ - использовать обработчик.При использовании этого метода вам не нужно будет указывать время задержки.Проверьте ответ, данный @davehenry здесь

0 голосов
/ 27 июля 2018

Вы не упоминаете, почему хотите найти представление пункта меню, и это может иметь какое-то отношение к ответу, который вы ищете.Однако, если вы хотите использовать findViewById(), чтобы найти представление меню, это один из способов сделать это.Следующий пример просто меняет значок меню с «X» на галочку.

ViewTreeObserver.OnGlobalLayoutListener будет вызываться сразу после размещения панели инструментов в следующем коде.Это то же самое, что и задержка, но это приемлемый способ выполнения этого типа обработки.

Альтернативно, программа может вызывать menu.findItem(R.id.action_hello) в onPrepareOptionsMenu().К сожалению, на данный момент панель инструментов сформирована не полностью, поэтому findViewById() не будет работать.

MainActivity.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;
    }
}

main.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>

activity_main.xml

<?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>
0 голосов
/ 26 июля 2018

Создайте глобальную переменную для будущего использования:

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.

Результат в LOGCAT:

07-25 17: 55: 07.138 9491-9491 /?Я / представление: :: android.widget.ImageButton {5542a5c VFED..C .. ...... I.0,0-0,0 # 7f080011 приложение: id / action_hello}

0 голосов
/ 26 июля 2018

Отправка Runnable в очередь Handler обычно приводит к выполнению Runnable после того, как основной поток пользовательского интерфейса завершает текущую часть выполняемого метода в жизненном цикле 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;
}
0 голосов
/ 12 июня 2018

Просто переопределить public void onPrepareOptionsMenu(Menu menu).

Документация гласит:

Это вызывается непосредственно перед отображением меню, каждый раз, когда оно отображается.Вы можете использовать этот метод, чтобы эффективно включать / отключать элементы или иным образом динамически изменять содержимое.

Представление для Menu создается после вызова onCreateOptionsMenu(Menu), поэтому вы не можете получить к нему доступ к подпредставлениям..

...