MenuItemCompat.getActionView zawsze zwraca wartość null


142

Właśnie zaimplementowałem v7 AppCompatbibliotekę wsparcia, ale MenuItemCompat.getActionViewzawsze zwraca wartość null w każdej testowanej wersji Androida (4.2.2, 2.3.4 ....)

SearchViewJest wyświetlany w pasku akcji, ale nie reaguje na dotyk działania i nie rozwinie się, pokazując jej EditTexti jest niczym prostym ikony.

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

Odpowiedzi:


296

Wreszcie znalazłem rozwiązanie.

  1. Zmiana przestrzeni nazw od actionViewClassod android:actionViewClassdoapp:actionViewClass

  2. Implementacja android.support.v7.widget.SearchView.OnQueryTextListenerinterfejsu do bieżącej działalności.

  3. Użyj bezpośrednio setOnQueryTextListenerzamiastSearchViewCompat.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
Jeśli to rozwiązało problem, prawdopodobnie powinieneś zaakceptować swoją odpowiedź. Chciałbym również skierować wszystkie inne osoby z podobnymi problemami do jeszcze jednego wątku omawiającego podobne problemy: stackoverflow.com/q/18407171/1108032 . Jeśli bieżący wątek nie rozwiązuje twoich problemów, rozważ przyjrzenie się rozwiązaniom tam.
Boris Strandjev

4
Świetna odpowiedź! Warto też wyjaśnić, że „app” w app: actionViewClass również wymaga dodatkowej deklaracji xmlns dla przestrzeni nazw „app”.
jklp

3
Muszę powiedzieć, że android.support.v7.widget.SearchViewklasy nie należy mylić z klasą „android.support.v4.widget.SearchViewCompat” (która jest znana jako częsty błąd podczas korzystania z biblioteki ActionBarCompat)
Alex Semeniuk

1
@jklp Próbuję dodać deklarację xmlns, <menu xmlns:app="http://schemas.android.com/apk/res/android" >ale pojawia się błąd Attribute is missing the Android namespace prefix. Czy to rozumiesz i jak to naprawić?
anticafe

5
Należy również pamiętać, że android: showAsAction również musi zostać zmieniony na app: showAsAction. Upewnij się również, że motyw dla działania (nie tylko aplikacja) odwołuje się do motywu aplikacji. Ostatnia rzecz, deklaracja aplikacji to „ schemas.android.com/apk/res-auto
Kalel Wade

85

W moim przypadku był to plik ProGuard. Musisz dodać tę linię:

-keep class android.support.v7.widget.SearchView { *; }

2
Wow, dlaczego to jest rzecz? To była jedyna rzecz, która działała dla mnie.
Claud

2
+1. Całkiem oczywiste, ale nadal wspomnę - w przypadku rozszerzenia SearchView do innej klasy, zachowaj ścieżkę do tej klasy w programie proguard!
user2520215

42

U mnie menu.xmlten problem spowodował nieprawidłowy import przestrzeni nazw.

Mój oryginał 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>

Wygląda na xmlns:app="http://schemas.android.com/tools"to, że MenuItemCompat.getActionView()powracał null. Zmiana tego importu w celu xmlns:app="http://schemas.android.com/apk/res-auto"rozwiązania problemu.

Nowa praca 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

Myślę, że problem polega na tym, że używasz SearchView z pakietu Support V7 i być może Twój poziom API jest ustawiony na ..... 22 ??.

Zmień kod na następujący w celu rozwiązania problemu:

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
Czy możesz wyjaśnić, dlaczego podany przykład kodu rozwiązuje problem? (Zakładam, że tak.)
Edgar N,

4

Wystąpił ten sam błąd, moja metoda getActionView()zawsze zwracała wartość null. Zrobiłem więc następujące rzeczy:

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

Widziałem w niektórych postach, że ludzie używają app: lub yourapp, ale ja używałem normalnie android:ActionVewClass.

Na mojej onCreateOptionsMenumetodzie:

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

I nie zapomnij wprowadzić onCreatemetody:

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

Działa to bardzo dobrze w przypadku mojej działalności „rozszerzającej” dla FragmentActivityi ActionBarActivity.


2

Powyższa odpowiedź Mohsena Afshina była moim punktem wyjścia i wprowadziłem kilka poprawek, aby działała z moją konfiguracją:

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

1

Miałem ten sam kod, ale zamiast korzystać z importu, którego android.support.v7.widget.SearchView;użyłem import android.widget.SearchView;. To rozwiązało mój problem z nullwartością. Po prostu zmień ten kod w swojej działalności wyszukiwania, a zadziała, a także zmieni przestrzeń nazw w pliku xml.


To był nasz problem, mieliśmy mieszankę android.widget.SearchView w działaniu, ale częściowo używaliśmy wersji 7 w naszym pliku menu_main.xml. Upewnij się tylko, że plik .xml również używa app: actionViewClass = "android.support.v7.widget.SearchView".
gmcc051

0

Oto krótki opis sposobu obsługi searchView z biblioteki pomocy technicznej w wersji 7:

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

Ponadto, jeśli używasz Proguard, dodaj to do jego konfiguracji:

-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v7.widget.SearchView { *; }
-keepattributes *Annotation*

Dla każdego, kto chciał sformatować kod, jest to bardzo znane formatowanie zwane „WhiteSmith”. Zmiana na inny format nie czyni go lepszym, ponieważ jest to kwestia gustu.
programista Androida

@JJD Yes. Poprawny. Nie jest tak powszechny jak inne, ale używam go przez bardzo długi czas. Jeśli chcesz, możesz ustawić go na Eclipse.
programista Androida

Dzięki za udostępnienie. Pozostaję przy ustawieniu domyślnym, ponieważ pozwala to uniknąć wielu niechcianych dyskusji z członkami zespołu lub współtwórcami.
JJD

@JJD To też prawda. w biurze pracujemy w nieco innym stylu niż domyślny. W każdym razie dobry programista powinien być w stanie obsłużyć wszystkie popularne style formatowania, a jeśli to „boli”, zawsze możesz skopiować kod i sformatować go za pomocą różnych narzędzi formatujących (online lub w środowisku IDE).
programista Androida

0

Miałem bardzo podobny problem z tą różnicą, że próbowałem użyć rozszerzonej klasy android.widget.ImageView

Jeśli używasz ProGuard, musisz określić, czy zezwalają na metody związane z tą klasą.

-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

To mówi: „Zezwalaj na wszystkie potrzebne konstruktory, które mogą być wywoływane z xml i zezwalaj na dowolne niestandardowe ustawiacze, których używa (dodaj więcej w razie potrzeby)”


0

Zrobiłem to ręcznie ustawioną w kodzie 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>

Plik układu:

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

Następnie w kodzie 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;
    }

wprowadź opis obrazu tutaj


-1

Usuń kod: klasa publiczna DemoActivity rozszerza ActionBarActivity

Zastąp przez: publiczna klasa DemoActivity rozszerza Activity


2
Dlaczego miałby to zrobić? Prosimy o bardziej szczegółowe wyjaśnienie odpowiedzi.
Robin Ellerkmann

Kiedy rozszerzam ActionBarActivity, zawsze zwraca null. Ale przedłużam tylko Aktywność, działa normalnie
Hieu

Musisz użyć własnej przestrzeni nazw podczas korzystania z ActionBarActivity, ponieważ jest to część biblioteki wsparcia. Ponieważ używasz androida: showAsAction w swoim xml, działa on z Activity (który nie pochodzi z biblioteki wsparcia) i nie działa z ActionBarActivity
Dennis K
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.