Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"java.lang.IllegalArgumentException: Given null view to target" when calling TapTarget.forToolbarMenuItem #168

Closed
2 tasks
henrichg opened this issue May 8, 2017 · 18 comments

Comments

@henrichg
Copy link

henrichg commented May 8, 2017

  • I have verified the issue exists on the latest version
  • I am able to reproduce it

Version used:
Latest: 1.9.1

Stack trace:
Fatal Exception: java.lang.IllegalArgumentException: Given null view to target
at com.getkeepsafe.taptargetview.ViewTapTarget.(ViewTapTarget.java:31)
at com.getkeepsafe.taptargetview.ToolbarTapTarget.(ToolbarTapTarget.java:36)
at com.getkeepsafe.taptargetview.TapTarget.forToolbarMenuItem(TapTarget.java:151)
at sk.henrichg.phoneprofilesplus.EditorProfilesActivity.showTargetHelps(EditorProfilesActivity.java:1956)
at sk.henrichg.phoneprofilesplus.EditorProfilesActivity.access$500(EditorProfilesActivity.java:67)
at sk.henrichg.phoneprofilesplus.EditorProfilesActivity$6.run(EditorProfilesActivity.java:513)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5103)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java)
at dalvik.system.NativeStart.main(NativeStart.java)

Android version:
Reported FC - API level <= 19

My application uses android.support.v7.widget.Toolbar, support lib version: 25.3.1, toolbar is act as the ActionBar via setSupportActionBar().

@tijee
Copy link

tijee commented May 29, 2017

+1

@babramovitch
Copy link

babramovitch commented May 29, 2017

I think I figured this one out.

The sample app is inflating the toolbar with the menu.

https://github.com/KeepSafe/TapTargetView/blob/master/app/src/main/java/com/getkeepsafe/taptargetviewsample/MainActivity.java

final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.inflateMenu(R.menu.menu_main);

I was using setSupportActionBar(toolbar) and then inflating the menu in onCreateOptionsMenu

getMenuInflater().inflate(R.menu.menu_main, menu);

So when you're calling

TapTarget.forToolbarMenuItem(toolbar, R.id.item_id, "test")

The toolbar doesn't actually have any menu items and throws the error.

I'm not sure if inflating the menu in the toolbar, and then calling setSupportActionBar(toolbar) will cause unknown problems though?

This should really be clarified in the documentation if there's no problem inflating the toolbar in addition to the setSupportActionBar(toolbar) OR make it work with the support method.

@tijee
Copy link

tijee commented May 29, 2017

Thanks for your help @babramovitch, unfortunately toolbar.inflateMenu() can't seem to work well with setSupportActionBar, onPrepareOptionsMenu is not called. I'll try to work around with the item bounds.

@babramovitch
Copy link

@tijee Weird, it does call onPrepareOptionsMenu when I do it, and the actions all appear to work. I just haven't had enough time yet to look for other side effects. It might be worth taking another look at that before moving to the bounds?

This was on a LG G4 running 6.0.1 and a Moto G running 4.4

@tijee
Copy link

tijee commented May 29, 2017

Yeah I'm not sure maybe I need to look further, for now what I'm doing is creating my menu as usual in onCreateOptionsMenu() with getMenuInflater(), plus inflate the menu in the toolbar itself. I'm not sure what it does under the hood but it works :)

@fgagneten
Copy link

Any solution about this issue? I get:

Caused by: java.lang.IllegalArgumentException: Given null view to target

@babramovitch
Copy link

babramovitch commented Jun 12, 2017

@fgagneten The method I described above is still working for me and I haven't encountered any problems so far, although to be cautious, I only load the menu into the toolbar if I'm going to show a tap target.

Specifically - set the toolbar xml in addition to initializing the support action bar with the toolbar

I'd give it a shot. I think that's the best you're going to get pending the library being updated to work with the support actionbar initialized with a toolbar.

@EliuTimana
Copy link

@fgagneten check this out #168 (comment)

@fgagneten
Copy link

Change:

getMenuInflater().inflate(R.menu.main, menu);

With the following line:

toolbar.inflateMenu(R.menu.main);

@henrichg
Copy link
Author

@fgagneten working good. No more reported FC in Crashlitics. Thank you.

@xiphirx
Copy link
Collaborator

xiphirx commented Jul 16, 2017

This is interesting. Is there a sample project that reproduces this issue?

@babramovitch
Copy link

@xiphirx I can put one together quickly later. It's just taking the sample app and removing line 31, and also configuring the the toolbar via setSupportActionBar

@xiphirx
Copy link
Collaborator

xiphirx commented Jul 17, 2017

So the issue here is that by setting up the Toolbar to act as an ActionBar, you're also making the menu inflation not immediately deterministic.

In my testing, onCreateOptionsMenu or onPrepareOptionsMenu are called after onResume and after onPostResume, so there's no lifecycle method to rely on to be sure that the menu has been inflated and attached to the view hierarchy.

What you can do, though, is listen for the next immediate layout of the Toolbar after inflating the menu in onCreateOptionsMenu or onPrepareOptionsMenu. Here's some sample code to do just that:

static void onNextLayout(final View view, final Runnable runnable) {
    final ViewTreeObserver observer = view.getViewTreeObserver();
    observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        final ViewTreeObserver trueObserver;

        if (observer.isAlive()) {
          trueObserver = observer;
        } else {
          trueObserver = view.getViewTreeObserver();
        }

        removeOnGlobalLayoutListener(trueObserver, this);

        runnable.run();
      }
    });
  }

  static void removeOnGlobalLayoutListener(ViewTreeObserver observer,
                                           ViewTreeObserver.OnGlobalLayoutListener listener) {
    if (Build.VERSION.SDK_INT >= 16) {
      observer.removeOnGlobalLayoutListener(listener);
    } else {
      observer.removeGlobalOnLayoutListener(listener);
    }
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    onNextLayout(toolbar, new Runnable() {
      @Override
      public void run() {
        showTapTargetView();
      }
    });

    return true;
  }

This isn't something that the library can feasibly do well for you, so I'd advise moving away from "setSupportActionBar" entirely and just using the Toolbar directly.

@filipebezerra
Copy link

This is my solution:

Firtly we must follow the activity lifecycle, so to configure the tap targets I used the onPrepareOptionsMenu() callback, example:

@Override public boolean onPrepareOptionsMenu(Menu menu) {
    menu.findItem(R.id.action_map).setVisible(presenter.canOpenMap());
    initializeDiscovery();
    return super.onPrepareOptionsMenu(menu);
}

Then we must to wait the ActionMenuView to laid out, first let's find this view:

final Toolbar toolbar = findViewById(R.id.toolbar);

View toolbarChild;
ActionMenuView actionMenuView = null;
for (int i = 0; i < toolbar.getChildCount(); i++) {
    toolbarChild = toolbar.getChildAt(i);

    if (toolbarChild instanceof ActionMenuView) {
        actionMenuView = (ActionMenuView) toolbarChild;
        break;
    }
}

And now we must to addOnGlobalLayoutListener:

if (actionMenuView != null) {
    ViewUtils.onLaidOut(actionMenuView, () -> {
        List<TapTarget> targets = new ArrayList<>();
        targets.add(forView(findViewById(R.id.fab), "Fab", "Nice"));

        if (presenter.canOpenMap()) {
            targets.add(forToolbarMenuItem(toolbar, R.id.action_map, "Map", "Great"));
        }

        targets.add(forToolbarMenuItem(toolbar, R.id.action_share, "Share", "Cool"));

        displayDiscovery(targets);
    });
}

My ViewUtils class

@eskalera
Copy link

eskalera commented Nov 9, 2017

I am trying to do the same but for a menu item inside the overflow menu.

<item
        android:id="@+id/sign_out_menu"
        android:title="@string/sign_out"
        app:showAsAction="never"/>

Trying to target this menu item always returns the

Fatal Exception: java.lang.IllegalArgumentException: Given null view to target

I am starting to think this has something to do with the fact that showAsAction="never" is set and therefore actionview is always null.

Has the library consider this usecase?

@toukea
Copy link

toukea commented Nov 30, 2018

Any solution about this issue? I get:

Caused by: java.lang.IllegalArgumentException: Given null view to target

I have just use a mHandler.postDelay(runnable, 500) to display tap target. at this time all views was created and ItemMenu inflatted.

@coskungun
Copy link

Change:

getMenuInflater().inflate(R.menu.main, menu);

With the following line:

toolbar.inflateMenu(R.menu.main);

this code with my problem solved. Thanks

@iman2420
Copy link

i use this :

class MyActivity : AppCompatActivity(), OnMenuVisibilityListener {

    override fun onMenuVisibilityChanged(isVisible: Boolean) {
        if (isVisible) {
            // Menu is opened
            // Perform actions when the menu is opened
        } else {
            // Menu is closed
            // Perform actions when the menu is closed
        }
    }

    // Rest of your activity code

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests