7

I'm using MaterialShowcaseView to show the user a quick tutorial, when they first start the app.

The problem I'm having is that I'm only able to get a view reference to the items in my action bar items when the user selects that item in onOptionsItemSelected.

i.e.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.my_location:
            // User chose the "Favorite" action, mark the current item
            // as a favorite...
            ShowcaseConfig config = new ShowcaseConfig();
            config.setDelay(500); // half second between each showcase view


            MaterialShowcaseSequence sequence = new MaterialShowcaseSequence(this, SHOWCASE_ID);

            sequence.setConfig(config);

            sequence.addSequenceItem(findViewById(R.id.action_my_location),
                    "This is button one", "GOT IT");
            sequence.start();
            Toast.makeText(MapsActivity.this, "My location action press", Toast.LENGTH_SHORT).show();

            return true;

        default:
            // If we got here, the user's action was not recognized.
            // Invoke the superclass to handle it.
            return super.onOptionsItemSelected(item);
    }
}

The above code works.

Is it possible to get a view reference in onCreateOptionsMenu? Everything else I've tried gives me a null object reference for the view.

I've tried this answer too, to no success.

I should mention that for the actionBar I used android's action bar tutorial.

Thanks guys.

EDIT:

This is what I've tried to do now:

@Override
    public void invalidateOptionsMenu() {
        super.invalidateOptionsMenu();

    }
    MenuItem mi;
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        mi = menu.findItem(R.id.action_my_location);
        new MaterialShowcaseView.Builder(this)
                .setTarget(mi.getActionView())
                .setDismissText("GOT IT")
                .setContentText("This is some amazing feature you should know about")
                .setDelay(300) // optional but starting animations immediately in onCreate can make them choppy
                .singleUse("101010110") // provide a unique ID used to ensure it is only shown once
                .show();
        return super.onPrepareOptionsMenu(menu);
}

Error:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.getLocationInWindow(int[])' on a null object reference

Community
  • 1
  • 1
Adz
  • 2,809
  • 10
  • 43
  • 61
  • Please check that whether your Menu item is still null inside `onPrepareOptionsMenu` ? Also paste the line number where the exception occurs – Qandeel Abbassi Feb 29 '16 at 21:05
  • Exception occurs at .setTarget(mi.getActionView()) . and Menu is not null. – Adz Feb 29 '16 at 21:11
  • Check my edit in my answer. I have given a code on how the library suggest to use it on actiobar items. – Qandeel Abbassi Feb 29 '16 at 21:12
  • It is passing a view object of menu item, You can keep the code inside the `onPrepareOptionsMenu` method but change the code for using the library – Qandeel Abbassi Feb 29 '16 at 21:12
  • This is what I've already got. Surely the user would want the tutorial before they start pressing the action bar items themselves? I want to show them what each action bar item does without them selecting the menu item manually. Is that possible? Sorry if I'm being picky here. – Adz Feb 29 '16 at 21:16
  • Can you try this `View view = findViewById(R.id.action_my_location);` inside `onPrepareOptionsMenu` and then pass it in setTarget like this `.setTarget(view)` , I know `getActionView` does the same but just try it – Qandeel Abbassi Feb 29 '16 at 21:28
  • Still getting a null pointer exception on setTarget() :( . Sorry about this man! I'm thinking maybe the View is only initialised once the user has pressed the action item. – Adz Feb 29 '16 at 21:36
  • try this https://stackoverflow.com/a/62533513/4685284 – Shahbaz Hashmi Jun 23 '20 at 11:22

2 Answers2

5

You can do menu.findItem(R.id.youritemid) to get the menu item, and you can get reference to menu object in your onCreateOptionsMenu(Menu menu) method then you can initialize a global variable with that menu object to use it anywhere.

Here is some code:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    _menu = menu;
    return true;
}

I am initialize my global variable Menu _menu; with menu object and then i can do like this MenuItem mi = _menu.findItem(R.id.itemid); any where i want.

Edit: Please take care that you are not calling anything on menuitem before the menu gets created, you can schedule a thread to wait for 3 to 5 secs or you can do it some other way, all you should worry about is whether the menu has been initialized or not.

Qandeel Abbassi
  • 999
  • 7
  • 31
  • Hi there, can you show me how you'd do this in code? Thanks – Adz Feb 26 '16 at 19:11
  • I'm getting a null object reference on _menu.findItem(R.id.my_item); – Adz Feb 26 '16 at 19:43
  • I think the problem is that the view of the action item is not initialised unless the user presses the item! – Adz Feb 26 '16 at 19:51
  • 1
    Another workaround can be that you can call `invalidateOptionsMenu()` yourself in the code which would call `onPrepareOptionMenu(Menu menu)` and your can do what you want with the menu i.e: find menu item. To get more info about `onPrepareOptionMenu` read the **Changing menu items at runtime** section of this [link](http://developer.android.com/guide/topics/ui/menus.html) – Qandeel Abbassi Feb 26 '16 at 20:29
  • did you try the solution in above comment? – Qandeel Abbassi Feb 29 '16 at 19:01
  • See my edit please. I've called invalidateOptionsMenu from another method. I still get nullpointer exception. – Adz Feb 29 '16 at 20:43
  • and it still doesn't work? are you calling `invalidateOptionsMenu()` somewhere in the code? – Qandeel Abbassi Feb 29 '16 at 20:47
  • Yeah I have called it on my oncreate() – Adz Feb 29 '16 at 20:51
  • Have you tried to put a log statement inside `onPrepareOptionsMenu` , just to check is it even called? Moreover you dont need to override the `invalidateOptionsMenu` method. and you must know it only works for api>=11 – Qandeel Abbassi Feb 29 '16 at 21:01
  • My api is at 18 and yeah the logger inside onPrepareOptionsMenu gets called :( – Adz Feb 29 '16 at 21:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/104922/discussion-between-qandeel-abbasi-and-adz). – Qandeel Abbassi Feb 29 '16 at 21:36
0

You need this class:

public class ToolbarActionItemTarget implements Target {

private final Toolbar toolbar;
private final int menuItemId;

public ToolbarActionItemTarget(Toolbar toolbar, @IdRes int itemId) {
    this.toolbar = toolbar;
    this.menuItemId = itemId;
}

@Override
public Point getPoint() {
    return new ViewTarget(toolbar.findViewById(menuItemId)).getPoint();
}

So:

showcaseView = new ShowcaseView.Builder(getActivity())
            .setTarget(Target.NONE)
            .setTarget(new ToolbarActionItemTarget(toolbar, R.id.action_view))

Referenced by: https://github.com/amlcurran/ShowcaseView/releases/tag/5.3.0

`

João Paulo Paiva
  • 2,207
  • 1
  • 10
  • 5