5

I'm trying to integrate ShowcaseView (v5.2.3) into an app, but when I try to target an action bar menu item, the app crashes with the runtime exception message "insertShowcaseViewWithType cannot be used when the theme has no ActionBar". The activity absolutely has an action bar with menu items. This occurs on both Android v5.1.1 and v4.4.2

The stack trace from the crash is as follows...

java.lang.RuntimeException: insertShowcaseViewWithType cannot be used when the theme has no ActionBar
     at com.github.amlcurran.showcaseview.targets.ActionBarReflector.getHomeButton(ActionBarReflector.java:43)
     at com.github.amlcurran.showcaseview.targets.ActionBarReflector.getActionBarView(ActionBarReflector.java:36)
     at com.github.amlcurran.showcaseview.targets.ActionItemTarget.setUp(ActionItemTarget.java:49)
     at com.github.amlcurran.showcaseview.targets.ActionItemTarget.getPoint(ActionItemTarget.java:43)
     at com.github.amlcurran.showcaseview.ShowcaseView$1.run(ShowcaseView.java:176)
     at android.os.Handler.handleCallback(Handler.java:739)
     at android.os.Handler.dispatchMessage(Handler.java:95)
     at android.os.Looper.loop(Looper.java:135)
     at android.app.ActivityThread.main(ActivityThread.java:5254)
     at java.lang.reflect.Method.invoke(Native Method)
     at java.lang.reflect.Method.invoke(Method.java:372)
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

The app module build.gradle is as follows...

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "com.d60402.myappname"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.0'
    compile 'com.github.amlcurran.showcaseview:library:5.2.3'
}

My activity is as follows...

package com.d60402.myappname;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;

import com.github.amlcurran.showcaseview.ShowcaseView;
import com.github.amlcurran.showcaseview.targets.ActionItemTarget;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu
        getMenuInflater().inflate(R.menu.main_menu, menu);

        boolean ret = super.onCreateOptionsMenu(menu);

        ActionItemTarget target = new ActionItemTarget(this, R.id.action_settings);

        new ShowcaseView.Builder(this)
                .setTarget(target)
                .setContentTitle("Settings menu")
                .setContentText("Tap here to view and set the app settings")
                .hideOnTouchOutside()
                .build();

        return ret;
    }
}

The main_menu.xml is as follows...

<?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_settings"
        android:title="@string/action_settings"
        android:orderInCategory="100"
        app:showAsAction="always|withText"/>
</menu>

The AndroidManifest.xml is as follows...

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.d60402.myappname" >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

And the styles.xml is as follows...

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>
d60402
  • 3,190
  • 2
  • 23
  • 28

9 Answers9

10

Use this code. Its working for me. Here "toolbar" is the id of my toolbar.

      Target homeTarget = new Target() {
        @Override
        public Point getPoint() {
            // Get approximate position of home icon's center
            int actionBarSize = toolbar.getHeight();
            int x = actionBarSize / 2;
            int y = actionBarSize / 2;
            return new Point(x, y);
        }
    };
    new ShowcaseView.Builder(this)
            .setContentTitle("Its My Navigation Drawer")
            .setContentText("Click here and you will get options to navigate to other sections.")
            .setTarget(homeTarget)
            .build();
}
RatneZ
  • 1,078
  • 9
  • 9
3

From the ShowcaseView developer (amicurran)...

As you might realise, there is no API in AppCompat for getting a reference to a MenuItem view, and as such this call requires reflection. And basically every time Google change AppCompat, this breaks ShowcaseView.

I don't have any current plans to support the new AppCompat (which is what this crash is coming from). However, if you use an AppCompat Toolbar, then you can quite easily showcase an item on it. See the demo activity, and the new target for more info.

Community
  • 1
  • 1
d60402
  • 3,190
  • 2
  • 23
  • 28
2

Check in your manifest there you will find noactionbar theme applied to current activity. so just try this, hopefully it will solve your problem!

Target target = new ViewTarget(R.id.action_settings, this);

new ShowcaseView.Builder(this)
            .setTarget(target)
            .setContentTitle("Settings menu")
            .setContentText("Tap here to view and set the app settings")
            .hideOnTouchOutside()
            .build();
Madhur
  • 3,303
  • 20
  • 29
  • 1
    The activity in the manifest does not have a theme defined. I also tried using your code of a ViewTarget instead of the ActionItemTarget, but I get the following exception: "java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.getLocationInWindow(int[])' on a null object reference" – d60402 Oct 28 '15 at 14:11
1

This works for me. You can try it

new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
    if(toolbar.getMenu().size()==0){
        return;
    }
    MenuItem item=toolbar.getMenu().getItem(0);
     try {
           // ViewTarget navigationButtonViewTarget = navigationButtonViewTarget(toolbar); use this for back or up button
            new ShowcaseView.Builder(TrainRoute.this)
                    .withMaterialShowcase()
                    .setTarget(new ViewTarget(item.getItemId(),TrainRoute.this))
                    .setContentText("Here's how to highlight items on a toolbar")
                    .setStyle(R.style.CustomShowcaseTheme)
                    .build()
                    .show();
        } catch (Exception e) {
            e.printStackTrace();
        }
        }
    },
1000);
m00am
  • 5,910
  • 11
  • 53
  • 69
DILSHAD AHMAD
  • 215
  • 2
  • 4
0

This code it worked for me, but this code is the "happy path" to my project.

You will need iterate over all items on real time to check how many items are showing or hidden according with your code or toolbar_size to get the right width position.

ShowCaseUtil.showForToolbar(getActivity(),
            ((MainActivity) getActivity()).toolbar, //instance of my toolbar
            R.string.showcase_fragment_message,
            menu.findItem(R.id.action_scan_barcode));


public static void showForToolbar(FragmentActivity activity, final Toolbar toolbar, int message, final MenuItem menuItem) {
    Target homeTarget = new Target() {
        @Override
        public Point getPoint() {
            int actionBarWidth = toolbar.getWidth();
            int y = actionBarWidth - (menuItem.getIcon().getIntrinsicWidth() * menuItem.getOrder());
            return new Point(y, toolbar.getHeight());
        }
    };
    new ShowcaseView.Builder(activity)
            .withMaterialShowcase()
            .setStyle(R.style.CustomShowcaseTheme2)
            .setTarget(homeTarget)
            .setContentText(message)
            .build()
            .show();
}
lucasddaniel
  • 1,779
  • 22
  • 22
0

use it like this

new ShowcaseView.Builder(activity)
           // .withMaterialShowcase()
         //  .setStyle(R.style.CustomShowcaseTheme3)
            .setTarget(Target.NONE)
            .setOnClickListener(this)
            //.withMaterialShowcase()
            .blockAllTouches()
            .useDecorViewAsParent() //this is the difference
            .build();

then target your view in action bar

Reza
  • 321
  • 2
  • 4
  • 14
0

I get a solution..... Code for Appcompat toolber

toolbar = (Toolbar) findViewById(R.id.department_name);
    toolbar.setTitle(R.string.catagory_one);
    toolbar.setSubtitle(getText(R.string.lwebsite));
    toolbar.inflateMenu(R.menu.all_articles);// add this line catching menu items
    setSupportActionBar(toolbar);

Then use your preferred targetview library.I write here this by two different library.One is

 new MaterialTapTargetPrompt.Builder(CatagoryOne_HOME.this)//library link:https://github.com/sjwall/MaterialTapTargetPrompt
            .setTarget(R.id.sync)//this is yours
            .setPrimaryText("Send your first email")
            .setSecondaryText("Tap the envelope to start composing your first email")
            .show();

    ViewTarget target = new ViewTarget(toolbar.findViewById(R.id.sync));//this is yours
    new ShowcaseView.Builder(this)
            .setContentTitle("Its My Navigation Drawer")
            .setContentText("Click here and you will get options to navigate to other sections.")
            .useDecorViewAsParent() 
            .setTarget(target)
            .build();

That's all.Happy coding......

Shahriar Nasim Nafi
  • 1,160
  • 15
  • 19
0
public void startTourGuide() {
    mToolbar.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
        @Override
        public void onDraw() {
            if (!isFinishing()) {
                if (prefs.getInt("guide_upload_sign_in_up", 0) == 0) {

                    final FancyShowCaseView fancyShowCaseViewVideo = new FancyShowCaseView.Builder(MainActivity.this)
                            .title("Upload Video, Sign In, Register...")
                            .focusBorderColor(getResources().getColor(R.color.color_primary))
                            .focusBorderSize(5)
                            .focusOn(findViewById(R.id.main_upload))
                            .build();



                    new FancyShowCaseQueue()
                            .add(fancyShowCaseViewVideo)
                            .show();
                    editor.putInt("guide_upload_sign_in_up", 1);
                    editor.apply();
                }
            }
        }
    });
}
Smeet
  • 4,036
  • 1
  • 36
  • 47
0

For Kotlin I added an inner class as below to get the target pointed at Bottom Navigation Menu items. For me, the BottomNavigation had 5 menu items. navItemPosition ranging from 0 to 4.

class ViewTargetPoint(
    screenWidth: Int,
    screenHeight: Int,
    navItemPosition: Int,
    midPointOfBottomNav: Int
) : Target {

    var x: Int = 0
    var y: Int = 0

    init {
        Log.e("TAG", "screenWidth px = $screenWidth")
        Log.e("TAG", "screenHeight px = $screenHeight")
        Log.e("TAG", "midPointOfBottomNav = $midPointOfBottomNav}")
        x = (screenWidth / 10) + ((screenWidth / 5) * navItemPosition)
        y = screenHeight - midPointOfBottomNav
        Log.e("TAG", "Point X = $x")
        Log.e("TAG", "Point Y = $y")
    }

    override fun getPoint(): Point {
        Log.e("TAG", "Point X = $x")
        Log.e("TAG", "Point Y = $y")
        return Point(x, y)
    }
}

targetPoint = ViewTargetPoint(screenWidth, screenHeight, navItemPosition, midPointOfBottomNav)

ShowcaseView.Builder(this)
        .withMaterialShowcase()
        .setTarget(targetPoint)
        .setContentTitle("ShowcaseView 2")
        .setContentText("This is highlighting the Some button")
        .hideOnTouchOutside()
        .setShowcaseEventListener(
            object : SimpleShowcaseEventListener() {
                override fun onShowcaseViewDidHide(showcaseView: ShowcaseView) {
                    Log.e("TAG", "onShowcaseViewDidHide 2")
                }
            }
        )
        .build()
Jayesh Nair
  • 51
  • 1
  • 4