为什么 MenuItemCompat.getActionProvider 返回 null?

25

我尝试在我的应用程序中的操作栏上使用android.support.v7.widget.ShareActionProvider。所以我按照Android文档中的示例进行了操作,但是遇到了一些问题。
这是我的菜单xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:myapp="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/action_share"
        android:orderInCategory="100"
        android:icon="@drawable/ic_action_share"
        android:title="@string/action_share"
        myapp:showAsAction="ifRoom"
        myapp:actionProviderClass="android.support.v7.widget.ShareActionProvider" />

</menu>

这是我用来创建分享按钮的代码:

@Override
public void onCreateOptionsMenu (Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.share, menu);
    MenuItem shareItem = menu.findItem(R.id.action_share);
    ShareActionProvider mShareActionProvider = (ShareActionProvider)MenuItemCompat.getActionProvider(shareItem);
    mShareActionProvider.setShareIntent(getDefaultIntent());
    super.onCreateOptionsMenu(menu, inflater);
}

我的问题是:

  1. 对于我来说,MenuItemCompat.getActionProvider(shareItem)总是返回null,为什么会这样?
  2. 当我注释掉那些代码行时,分享按钮出现在菜单栏上但是点击无效,如何解决(如果问题1无法解决)?

顺便说一下,我查看了MenuItemCompat.getActionProvider的代码,它看起来像是检查菜单项是否实现了SupportMenuItem接口并在未实现时返回失败。我该怎么处理?


4
我能够重现MenuItemCompat总是返回null的情况。我的问题在于我使用了android:命名空间,而不是定义一个例如app:或myapp:的命名空间。在两种情况下,我都验证了menu.findItem返回的是SupportMenuItem的实例。 - josh527
我找到了我犯两个错误的原因,应用程序主题应该是theme.appcompat.*,而我使用了标准的holo主题,另外似乎在片段中执行onCreateOptionsMenu的顺序在活动之前。 - hago
我已经使用了一个appcompat主题,但我无法理解你提到的第二个原因的相关性。这个问题对我来说仍然没有解决。请参见http://stackoverflow.com/questions/24219842/getactionprovider-item-does-not-implement-supportmenuitem。 - faizal
12个回答

20

在我的情况下,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/menu_item_share"
        app:actionProviderClass="android.support.v7.widget.ShareActionProvider"/>

请注意 app:actionProviderClass="android.support.v7.widget.ShareActionProvider":它应该具有:

  • 正确的android.widget android.support.v7.widget
  • 正确的命名空间android app)。

不幸的是,编译器会在没有错误的情况下编译它,只有Android Studio会用下划线进行通知。


18

如果有人想要保持progaurd并仍然使用该代码:

ShareActionProvider mShareActionProvider = (ShareActionProvider)MenuItemCompat.getActionProvider(shareItem);

只需要加入 proguard

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

这是对我有效的解决方案。由于Android支持库现在已被AndroidX取代,我不得不将其更改为-keep class androidx.appcompat.widget.ShareActionProvider { *; }。但是,有什么想法为什么会这样?由于类明确地被实例化,为什么Proguard会将其删除?我不理解。 - Abhijit

17

以下这段代码是唯一可行的解决方法,可以避免ShareActionProvider为空的情况...我使用setActionProvider代替了它...请看下面的代码:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.messages_activity_menu, menu);
    MenuItem menuItem = menu.findItem(R.id.menu_item_share);
    shareActionProvider = new ShareActionProvider(this);
    MenuItemCompat.setActionProvider(menuItem, shareActionProvider);

    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    if(item.getItemId() == R.id.menu_item_share){
        onShareAction();
    }

    return super.onOptionsItemSelected(item);
}

private void onShareAction(){
    // Create the share Intent
    String playStoreLink = "https://play.google.com/store/apps/details?id=" + getPackageName();
    String yourShareText = getResources().getString(R.string.share_text) + playStoreLink;
    Intent shareIntent = ShareCompat.IntentBuilder.from(this).setType("text/plain").setText(yourShareText).getIntent();
    // Set the share Intent
    if (shareActionProvider != null) {
        shareActionProvider.setShareIntent(shareIntent);
    }
}

并且...

 <?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android" >
     <item
    android:id="@+id/menu_item_share"
    android:icon="@drawable/ic_action_share"
    android:showAsAction="ifRoom|withText"
    android:title="@string/menu_item_share" />
 </menu>

还有其他可能需要检查的事项:

该活动必须继承ActionBarActivity:

MyActivity extends ActionBarActivity

请检查并使用这些导入:

import android.support.v4.app.ShareCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.OnNavigationListener;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.ShareActionProvider;
在AndroidManifest.xml中,在您的activity标签属性中添加此行代码:
android:theme="@style/Theme.AppCompat.Light"

如果您不知道如何导入v7和v4兼容库,请参阅:http://developer.android.com/tools/support-library/setup.html


3
这不是唯一可行的解决方案,可以看到我的回答。 - yshahak
你可以简单地不使用extends ActionBarActivity,我将以下代码: Intent shareIntent = ShareCompat.IntentBuilder.from(this).setType("text/plain").setText(yourShareText).getIntent(); 更改为: Intent shareIntent = new Intent(Intent.ACTION_SEND).putExtra(Intent.EXTRA_TEXT, yourShareText).setType("text/plain"); 这样就可以正常工作了。 - JFouad

12

请确保您的类扩展自AppCompatActivity而不仅是Activity。

注意:已编辑以反映更新的应用兼容库。


ActionBarActivity现已被弃用。 - ban-geoengineering
如上所述,AppCompatActivity 是更好的选择。 - EnduroDave

11

通过一些阅读和可能包括你的一些回复,最终解决了这个问题:

  1. Share_Menu.xml. 确保你有一个自定义命名空间,actionProvider类也来自于该自定义命名空间,并且正确的值为: android.support.v7.widget.ShareActionProvider

    <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myapp="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_share" android:title="@string/action_detail_share" myapp:showAsAction="always" myapp:actionProviderClass="android.support.v7.widget.ShareActionProvider"></item> </menu>

  2. Detail_Activity.java
    2.1. 继承自 ActionBarActivity 而不是 Activity
    2.2. 添加 正确的导入

    import android.support.v4.app.Fragment; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.ShareActionProvider;

  3. AndroidManifest.xml 添加 android:theme="@style/Theme.AppCompat.Light"

    <activity android:name=".detail_activity" android:label="@string/title_activity_detail_activity" android:theme="@style/Theme.AppCompat.Light" android:parentActivityName=".main_activity" >

  4. Build.gradle
    4.1. 在我的情况下,为了更安全,我在Debug模式下关闭ProGuard

    debug { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' }

4.2. 确保你的依赖项中有以下编译部分

`compile 'com.android.support:appcompat-v7:20.0.+'` 

1

我遇到了同样的错误。MenuItemCompat.getActionProvider返回了null。

我的问题在于ProGuard。关闭ProGuard解决了我的问题。


4
无需完全关闭proguard,只需要保留搜索类 - https://code.google.com/p/android/issues/detail?id=58508 - Nir Hartmann
2
只需添加 -keep class android.support.v7.widget.ShareActionProvider { *; }-keep class android.support.v7.widget.SearchView { *; },一切都应该正常工作。 - jeff_bordon

1

我在我的应用程序中遇到了相同的空指针问题。正如@josh527所说,我忘记为我的应用程序定义自定义xml命名空间。我知道这不是你的问题,但有些人可能会像我一样看到你的帖子并没有意识到这一点,所以我只是想指出它;)


0
变量: Android.Support.V7.Widget.ShareActionProvider shareActionProvider;
this.MenuInflater.Inflate(Resource.Menu.share_action_provider, menu);
var shareItem = menu.FindItem(Resource.Id.menu_item_share_action_provider_action_bar);
MenuItemCompat.SetShowAsAction (shareItem,  MenuItemCompat.ShowAsActionIfRoom);
var actionprov = new Android.Support.V7.Widget.ShareActionProvider (this);
MenuItemCompat.SetActionProvider (shareItem, actionprov);
var test =  MenuItemCompat.GetActionProvider (shareItem);
shareActionProvider = test.JavaCast<Android.Support.V7.Widget.ShareActionProvider>();
var intent = new Intent(Intent.ActionSend);
intent.SetType("text/plain");
intent.PutExtra(Intent.ExtraText, "ActionBarCompat is Awesome! Support Lib v7 #Xamarin");
shareActionProvider.SetShareIntent (intent);
return base.OnCreateOptionsMenu(menu); 

这对我来说起了作用...我只是创建了自己的ShareActionProvider!然后我自己设置它,再获取它...也许有些代码甚至是不需要的...但是有很多强制类型转换,并且确保你始终使用正确的类型,如果你只是输入"ShareActionProvider",实际上你使用的是V4而不是V7。


0

你应该更改onCreateOptionsMenu函数的声明和定义。你已经改变了基类的函数声明,这意味着该方法没有被覆盖。

尝试这样做:

@Override
public boolean onCreateOptionsMenu (Menu menu) {

    inflater.inflate(R.menu.share, menu);// declare a local layout inflater
    MenuItem shareItem = menu.findItem(R.id.action_share);
    ShareActionProvider mShareActionProvider = (ShareActionProvider)MenuItemCompat.getActionProvider(shareItem);
    mShareActionProvider.setShareIntent(getDefaultIntent());
//    super.onCreateOptionsMenu(menu, inflater);   <--- Remove this line or put in the first line because base class constructor should be called in the first line of the method.

return super.onCreateOptionsMenu(menu);//<-- Add this line to set the menu after adding menu items
}

0

对于Android Pie,操作提供程序类再次发生了变化:

app:actionProviderClass="androidx.appcompat.widget.ShareActionProvider"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接