MenuItemCompat.getActionView sempre retorna nulo

142

Acabei de implementar a v7 AppCompatbiblioteca de suporte, mas o MenuItemCompat.getActionViewretorno sempre nulo em todas as versões do Android que testei (4.2.2, 2.3.4 ....)

O SearchViewé exibido na barra de ação, mas não responde às ações de toque e não se expande para mostrar suas EditTexte é como um ícone simples.

@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>
Mohsen Afshin
fonte

Respostas:

296

Finalmente encontrei a solução.

  1. Alterando o namespace de actionViewClassde android:actionViewClassparaapp:actionViewClass

  2. Implementando android.support.v7.widget.SearchView.OnQueryTextListenerinterface para a atividade atual.

  3. Use diretamente em setOnQueryTextListenervez deSearchViewCompat.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);
    }
    
Mohsen Afshin
fonte
3
Se isso resolveu seu problema, você provavelmente deve aceitar sua resposta. Eu também gostaria de apontar todas as outras pessoas com problemas semelhantes para mais um thread que discute problemas semelhantes: stackoverflow.com/q/18407171/1108032 . Se o encadeamento atual não resolver seus problemas, considere procurar as soluções lá.
Boris Strandjev 01/09
4
Ótima resposta! Também vale a pena esclarecer que o "aplicativo" no aplicativo: actionViewClass também requer uma declaração xmlns adicional para o espaço de nome "aplicativo".
Jklp # 31/13
3
É preciso dizer que android.support.v7.widget.SearchViewclasse não deve ser confundida com a classe 'android.support.v4.widget.SearchViewCompat' (que é conhecido como um erro comum ao usar ActionBarCompat biblioteca)
Alex Semeniuk
1
@ jklp Eu tento adicionar a declaração xmlns, <menu xmlns:app="http://schemas.android.com/apk/res/android" >mas obtém um erro Attribute is missing the Android namespace prefix. Você entende e como corrige?
Anticafe
5
Observe também que android: showAsAction também precisa ser alterado para app: showAsAction. Verifique também se o tema da atividade (não apenas o aplicativo) está referenciando um tema appcompat. Última coisa, a declartion aplicativo é " schemas.android.com/apk/res-auto "
Kalel Wade
85

No meu caso, era o arquivo ProGuard. Você precisa adicionar esta linha:

-keep class android.support.v7.widget.SearchView { *; }
Ivan Vazhnov
fonte
2
Uau, por que isso é uma coisa? Essa foi a única coisa que funcionou para mim.
Claud
2
+1. Muito óbvio, mas ainda vou mencionar - Caso você tenha estendido o SearchView para outra classe, mantenha o caminho para essa classe em progresso!
user2520215
42

Para mim, uma menu.xmlimportação incorreta de namespace causou esse problema.

Meu original 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>

Parece que xmlns:app="http://schemas.android.com/tools"estava causando o MenuItemCompat.getActionView()retorno null. Alterar essa importação para xmlns:app="http://schemas.android.com/apk/res-auto"corrigir o problema.

Novo trabalho 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>
Sean Barbeau
fonte
5

Eu acho que o problema é que você usa o SearchView do pacote Support V7 e talvez seu nível de API esteja definido como ..... 22 ??.

Alterando seu código para o seguinte, a fim de corrigir o problema:

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> 
Michele Caggiano
fonte
1
Você poderia explicar por que o exemplo de código que você forneceu resolve o problema? (Presumo que ele faz.)
Edgar N
4

Eu estava com o mesmo erro, meu método getActionView()sempre retornava nulo. Então, eu fiz o seguinte:

<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"/>

Vi em alguns posts que as pessoas estão usando o app: ou yourapp, mas usei normalmente android:ActionVewClass.

No meu onCreateOptionsMenumétodo:

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

E não se esqueça de colocar no onCreatemétodo:

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

Isso funciona muito bem para minha atividade "estendendo" para FragmentActivitye ActionBarActivity.

Franzé Jr.
fonte
2

A resposta de Mohsen Afshin acima foi o meu ponto de partida e fiz alguns ajustes para fazê-lo funcionar com minha configuração:

@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
fonte
1

Eu tinha o mesmo código, mas em vez de usar a importação android.support.v7.widget.SearchView;que estava usando import android.widget.SearchView;. Isso corrigiu meu problema com o nullvalor. Portanto, basta alterar esse código na sua atividade de pesquisa e ele funcionará e também o espaço para nome no arquivo xml.

marcelocolombo
fonte
Esse foi o nosso problema, tivemos uma mistura de android.widget.SearchView na atividade, mas tivemos um uso parcial da v7 em nosso arquivo menu_main.xml. Apenas verifique se o arquivo .xml também está usando o app: actionViewClass = "android.support.v7.widget.SearchView" também.
gmcc051
0

Aqui está um trecho de como lidar com o searchView da biblioteca de suporte 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;
  }

Além disso, se você usar Proguard, adicione isso à sua configuração:

-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v7.widget.SearchView { *; }
-keepattributes *Annotation*
desenvolvedor android
fonte
Para quem quisesse formatar o código, essa é uma formatação muito conhecida chamada "WhiteSmith". Mudar para outro formato não melhora, pois é uma questão de gosto.
desenvolvedor android
@JJD Sim. Corrigir. Não é tão comum quanto os outros, mas eu o uso há muito tempo. Você pode configurá-lo no Eclipse, se desejar.
desenvolvedor android
Obrigado por compartilhar. Eu fico com o padrão, pois evita muitas discussões indesejadas com membros da equipe ou colaboradores.
JJD
@JJD Isso também é verdade. no escritório, trabalhamos com um estilo um pouco diferente do padrão. De qualquer forma, um bom desenvolvedor deve ser capaz de lidar com todos os estilos de formatação comuns e, se isso "magoa os olhos", você sempre pode copiar o código e formatá-lo em várias ferramentas de formatação (online ou no IDE).
desenvolvedor android
0

Eu tive um problema muito parecido com a diferença, pois estava tentando usar uma classe que estendia android.widget.ImageView

Se você estiver usando o ProGuard, precisará especificar para permitir os métodos envolvidos nesta classe.

-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

Isso diz: "Permitir todos os construtores necessários que possam ser chamados a partir do xml e permitir quaisquer configuradores personalizados que ele use também (adicione mais conforme necessário)"

ElliotM
fonte
0

Eu fiz isso pelo manual definido no código 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>

Arquivo de layout:

<?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>

Então no código 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;
    }

insira a descrição da imagem aqui

nobjta_9x_tq
fonte
-1

Remover código: public class DemoActivity estende ActionBarActivity

Substituir por: public class DemoActivity extends Activity

Hieu
fonte
2
Por que ele deveria fazer isso? Por favor, explique sua resposta um pouco mais detalhadamente.
Robin Ellerkmann 23/01
Quando estendo o ActionBarActivity, ele sempre retorna nulo. Mas eu estendo somente a atividade, ele funciona normalmente #
307
Você precisa usar seu próprio espaço para nome ao usar o ActionBarActivity, pois faz parte da biblioteca de suporte. Desde que você está usando o Android: showAsAction em sua xml trabalha com Atividade (que não é da biblioteca de suporte) e não funciona com ActionBarActivity
Dennis K