MenuItemCompat.getActionView всегда возвращает ноль

142

Я только что внедрил v7 AppCompatбиблиотеку поддержки, но MenuItemCompat.getActionViewвсегда возвращаю ноль в каждой протестированной версии Android (4.2.2, 2.3.4 ....)

SearchViewОтображаются в панели действий , но он не реагирует на прикосновение действия и не расширяется , чтобы показать его EditTextи так же , как простой значок.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);

    MenuItem searchItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    if (searchView != null) {
        SearchViewCompat.setOnQueryTextListener(searchView, mOnQueryTextListener);
        searchView.setIconifiedByDefault(false);
        Log.d(TAG,"SearchView not null");
    } else
        Log.d(TAG, "SearchView is null");
    }
    return super.onCreateOptionsMenu(menu);
}

menu.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_search"
          app:showAsAction="always|collapseActionView"
          android:icon="@drawable/abc_ic_search"
          android:title="@string/action_bar_search"
          android:actionViewClass="android.support.v7.widget.SearchView"/>

    <item android:id="@+id/action_refresh"
          android:icon="@drawable/refresh"
          android:title="@string/action_bar_refresh"
          app:showAsAction="ifRoom"/>
</menu>
Мохсен Афшин
источник

Ответы:

296

Наконец я нашел решение.

  1. Изменение пространства имен actionViewClassс android:actionViewClassнаapp:actionViewClass

  2. Реализация android.support.v7.widget.SearchView.OnQueryTextListenerинтерфейса для текущей деятельности.

  3. Непосредственно используйте setOnQueryTextListenerвместоSearchViewCompat.setOnQueryTextListener

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
      MenuInflater inflater = getMenuInflater();
      inflater.inflate(R.menu.menu, menu);
    
      MenuItem searchItem = menu.findItem(R.id.action_search);
      SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
      if (searchView != null) {
         searchView.setOnQueryTextListener(this);
      }
    
      return super.onCreateOptionsMenu(menu);
    }
Мохсен Афшин
источник
3
Если это решило вашу проблему, вы, вероятно, должны принять ваш ответ. Я также хотел бы указать другим людям с похожими проблемами на еще одну ветку, обсуждающую подобные проблемы: stackoverflow.com/q/18407171/1108032 . Если текущий поток не решает ваши проблемы, попробуйте поискать решения там.
Борис Странджев
4
Отличный ответ! Возможно, стоит также уточнить, что «app» в app: actionViewClass также требует дополнительного объявления xmlns для пространства имен «app».
13:30
3
Надо сказать, что android.support.v7.widget.SearchViewкласс не следует путать с классом 'android.support.v4.widget.SearchViewCompat' (который известен как распространенная ошибка при использовании библиотеки ActionBarCompat)
Алекс Семенюк
1
@jklp Я пытаюсь добавить объявление xmlns, <menu xmlns:app="http://schemas.android.com/apk/res/android" >но получаю ошибку Attribute is missing the Android namespace prefix. Вы получаете это, и как вы это исправляете?
антифафе
5
Также обратите внимание, что android: showAsAction необходимо изменить на app: showAsAction. Также убедитесь, что ваша тема для действия (а не только для приложения) ссылается на тему appcompat. Последнее, что нужно объявить в приложении: « schemas.android.com/apk/res-auto »
Kalel Wade,
85

В моем случае это был файл ProGuard. Вам необходимо добавить эту строку:

-keep class android.support.v7.widget.SearchView { *; }
Иван Важнов
источник
2
Вау, почему это так? Это было единственное, что сработало для меня.
Клод
2
+1. Довольно очевидно, но я все же упомяну - если вы расширили SearchView для другого класса, оставьте путь к этому классу в proguard!
user2520215
42

Для меня неправильный menu.xmlимпорт пространства имен вызвал эту проблему.

Мой оригинал menu.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/tools">
        <item android:id="@+id/action_search"
              android:title="@string/map_option_search"
              android:icon="@drawable/ic_action_search"
              app:showAsAction="collapseActionView|ifRoom"
              app:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>

Похоже, что xmlns:app="http://schemas.android.com/tools"заставлял MenuItemCompat.getActionView()вернуться null. Изменение этого импорта, чтобы xmlns:app="http://schemas.android.com/apk/res-auto"исправить проблему.

Новая работа menu.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
       <item android:id="@+id/action_search"
              android:title="@string/map_option_search"
              android:icon="@drawable/ic_action_search"
              app:showAsAction="collapseActionView|ifRoom"
              app:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>
Шон Барбо
источник
5

Я думаю, что проблема в том, что вы используете SearchView из пакета Support V7, и, возможно, ваш уровень API установлен на ..... 22 ??.

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

menu.xml

<?xml version="1.0" encoding="UTF-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item 
        android:id="@+id/action_search"
        android:icon="@drawable/actionbar_button_search"
        android:title="Search"
        android:showAsAction="always"
        android:actionViewClass="android.widget.SearchView" />
</menu> 
Мишель Каджано
источник
1
Не могли бы вы объяснить, почему пример кода, который вы предоставили, решает проблему? (Я предполагаю, что это так.)
Эдгар Н
4

Я был с той же ошибкой, мой метод getActionView()всегда возвращал ноль. Итак, я сделал следующие вещи:

<item android:id="@+id/action_search"
      android:icon="@drawable/abc_ic_search"
      android:title="@string/search_title"
      android:showAsAction="always"
      android:actionViewClass="android.widget.SearchView"/>

В некоторых сообщениях я видел, что люди используют app: или yourapp, но я использовал их нормально android:ActionVewClass.

По моему onCreateOptionsMenuметоду:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.feed, menu);

    // Associate searchable configuration with the SearchView
    SearchManager searchManager = 
        (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView = (SearchView) menu.findItem(R.id.action_search)
            .getActionView();
    searchView.setSearchableInfo(searchManager
            .getSearchableInfo(getComponentName()));

    return true;
}

И не забудьте поставить в onCreateметоде:

// enabling action bar app icon and behaving it as toggle button
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);

Это работает очень хорошо для моей деятельности, "расширяющейся" для FragmentActivityи ActionBarActivity.

Франце младший
источник
2

Ответ Мохсена Афшина выше был моей отправной точкой, и я сделал несколько настроек, чтобы заставить его работать с моей установкой:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    MenuItem searchItem = menu.findItem(R.id.action_search);
    // SearchView searchView = (SearchView) MenuItemCompat
    //    .getActionView(searchItem);
    SearchView searchView = (SearchView) searchItem.getActionView();
    if (searchView != null) {
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                // do something with s, the entered string
                query = s;
                Toast.makeText(getApplicationContext(), 
                    "String entered is " + s, Toast.LENGTH_SHORT).show();
                return true;
            }
            @Override
            public boolean onQueryTextChange(String s) {
                return false;
            }
        });
    }
    return super.onCreateOptionsMenu(menu);
}

menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity" >

<item android:id="@+id/action_search"
    android:orderInCategory="5"
    android:title="Search"
    android:icon="@drawable/ic_action_search"
    android:showAsAction="ifRoom|collapseActionView"
    android:actionViewClass="android.widget.SearchView" />
</menu>
kasiahayden
источник
1

У меня был тот же код, но вместо того, чтобы использовать импорт, который android.support.v7.widget.SearchView;я использовал import android.widget.SearchView;. Это исправило мою проблему со nullзначением. Так что просто измените этот код в поисковой активности, и он будет работать, а также поменять пространство имен в XML-файле.

marcelocolombo
источник
Это была наша проблема, у нас была смесь android.widget.SearchView в Activity, но мы частично использовали v7 в нашем файле menu_main.xml. Просто убедитесь, что файл .xml также использует app: actionViewClass = "android.support.v7.widget.SearchView".
gmcc051
0

Вот фрагмент того, как обрабатывать searchView из библиотеки поддержки v7:

@Override
public void onCreateOptionsMenu(final Menu menu,final MenuInflater inflater)
  {
  menu.clear();
  getActivity().getMenuInflater().inflate(...,menu);
  _searchView=(SearchView)MenuItemCompat.getActionView(_searchMenuItem);
  _searchView.setQueryHint(...);

  if(VERSION.SDK_INT<VERSION_CODES.HONEYCOMB)
    {
    final EditText searchTextView=(EditText)searchView.findViewById(R.id.search_src_text);
    if(searchTextView!=null)
      {
      searchTextView.setScroller(new Scroller(_context));
      searchTextView.setMaxLines(1);
      searchTextView.setVerticalScrollBarEnabled(true);
      searchTextView.setMovementMethod(new ScrollingMovementMethod());
      searchTextView.setTextColor(_context.getResources().getColor(App.getResIdFromAttribute(_context,android.R.attr.textColorPrimary)));
      }
    }
  _searchView.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener()
    {
    ...
    });
  MenuItemCompat.setActionView(_searchMenuItem,_searchView);
  MenuItemCompat.setOnActionExpandListener(_searchMenuItem,new OnActionExpandListener()
    {
    ...
    });
  super.onCreateOptionsMenu(menu,inflater);
  }


public static int getResIdFromAttribute(final Activity activity,final int attr)
  {
  if(attr==0)
    return 0;
  final TypedValue typedvalueattr=new TypedValue();
  activity.getTheme().resolveAttribute(attr,typedvalueattr,true);
  return typedvalueattr.resourceId;
  }

Также, если вы используете Proguard, добавьте это в его конфигурацию:

-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v7.widget.SearchView { *; }
-keepattributes *Annotation*
разработчик Android
источник
Для тех, кто хотел отформатировать код, это очень известное форматирование под названием «WhiteSmith». Переход на другой формат не делает его лучше, так как это дело вкуса.
Android-разработчик
@JJD Да. Верный. Это не так часто, как другие, но я использовал это в течение очень долгого времени. Вы можете установить его на Eclipse, если хотите.
Android-разработчик
Спасибо, что поделился. Я остаюсь со стандартным, так как он избегает многих нежелательных дискуссий с членами команды или участниками.
JJD
@JJD Это тоже правда. в офисе мы работаем в несколько ином стиле, чем стиль по умолчанию. В любом случае, хороший разработчик должен уметь работать со всеми распространенными стилями форматирования, и, если это «ранит глаза», вы всегда можете скопировать код и отформатировать его с помощью различных инструментов форматирования (онлайн или в среде IDE).
Android-разработчик
0

У меня была очень похожая проблема с той разницей, что я пытался использовать класс, который android.widget.ImageView

Если вы используете ProGuard, вам нужно указать, чтобы разрешить методы, используемые в этом классе.

-keep public class * extends android.widget.ImageView{
  public <init>(android.content.Context);
  public <init>(android.content.Context, android.util.AttributeSet);
  public <init>(android.content.Context, android.util.AttributeSet, int);
  public void set*(...);
}

http://proguard.sourceforge.net/manual/examples.html

Это говорит: «Разрешить все необходимые конструкторы, которые могут быть вызваны из xml, и разрешить любые пользовательские сеттеры, которые он также использует (добавьте больше по мере необходимости)»

ElliotM
источник
0

Я сделал это вручную, установленным в коде Java:

<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/user_info"
        android:title="@string/user_name_title"
        app:actionLayout="@layout/menu_item_username"
        android:showAsAction="always" />
</menu>

Файл макета:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/usr_name_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:contentDescription="@string/user_info_image_des"
        android:padding="5dp"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:text="@string/user_name_title"
        android:textStyle="bold"
        android:visibility="visible" />
</LinearLayout>

Тогда в коде Java:

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.connect_menu, menu);
        // show user name on the top of menu
        Log.e("menu", "Size: " + menu.size());
        MenuItem item = menu.getItem(0);
        item.setActionView(R.layout.menu_item_username);
        View v = item.getActionView();
        if (null == v) {
            Log.e("NULL POINTER EX", "NULL MENU VIEW");
        } else {
            TextView usrNameTitle = v.findViewById(R.id.usr_name_title);
            if (null != usrName && usrName.length() > 0) {
                usrNameTitle.setText(usrName);
            }
        }
        return true;
    }

введите описание изображения здесь

nobjta_9x_tq
источник
-1

Удалить код: открытый класс DemoActivity расширяет ActionBarActivity

Заменить на: открытый класс DemoActivity расширяет активность

Хьеу
источник
2
Зачем ему это делать? Пожалуйста, объясните ваш ответ немного подробнее.
Робин Эллеркманн
Когда я расширяю ActionBarActivity, он всегда возвращает null. Но я распространяю только активность, она работает нормально
Хиеу
Вы должны использовать свое собственное пространство имен при использовании ActionBarActivity, поскольку оно является частью библиотеки поддержки. Так как вы используете android: showAsAction в вашем xml, он работает с Activity (который не из библиотеки поддержки) и не работает с ActionBarActivity
Деннис K