获取 TapTargetView 的 MenuItem 视图参考

Get MenuItem's View reference for TapTargetView

我正在尝试将 TapTargetView 用于菜单项,但我看不到它。

我的代码:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater menuInflater = getMenuInflater();
    menuInflater.inflate(R.menu.menu, menu);

    new TapTargetSequence(this)
            .targets(
                    TapTarget.forView(menu.findItem(R.id.add).getActionView(), "Gonna"))

            .listener(new TapTargetSequence.Listener() {
                // This listener will tell us when interesting(tm) events happen in regards
                // to the sequence
                @Override
                public void onSequenceFinish() {
                    // Yay
                }

                @Override
                public void onSequenceStep(TapTarget lastTarget, boolean targetClicked) {

                }


                @Override
                public void onSequenceCanceled(TapTarget lastTarget) {
                    // Boo
                }
            });


    return true;
}

错误:

java.lang.IllegalArgumentException: Given null view to target

我该如何解决这个问题? 我试过将 android:actionViewClass 添加到 xml 文件,但没有成功。

您可以利用View#findViewsWithText() API来获取MenuItem的观点的参考。

菜单如下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_settings"
      android:title="@string/action_settings"
      android:orderInCategory="100"
      app:showAsAction="ifRoom"/>
</menu>

并假设正在显示 MenuItem,则:

@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  final View decorView = getWindow().getDecorView();

  decorView.post(() -> {
    ArrayList<View> list = new ArrayList<>();
    decorView.findViewsWithText(list, getString(R.string.action_settings), View.FIND_VIEWS_WITH_TEXT);
    // `itemView` is the actual view you should use to create your `TapTargetView`
    View itemView = list.get(0);
  });
}

使用[=h11=]代替TapTarget.forView

像这样更改代码...

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.menu, menu);

new TapTargetSequence(this)
        .targets(
                TapTarget.forToolbarMenuItem(toolbar,R.id.add, "Gonna"))

        .listener(new TapTargetSequence.Listener() {
            // This listener will tell us when interesting(tm) events happen in regards
            // to the sequence
            @Override
            public void onSequenceFinish() {
                // Yay
            }

            @Override
            public void onSequenceStep(TapTarget lastTarget, boolean targetClicked) {

            }


            @Override
            public void onSequenceCanceled(TapTarget lastTarget) {
                // Boo
            }
        });


return true;
}

另一种方法是在菜单项中使用 "app:actionLayout="@layout/some_layout",some_layout 可以将该项目作为其中的视图。然后,在您的 activity 中,您可以使用:

MenuItem menuItem = menu.findItem(R.id.menu_item); // get the menu item
ImageView menuView = menuItem.getActionView().findViewById(R.id.some_icon);

您可以使用此 menuView 来设置点击目标

TapTargetView.showFor(activity, getTapTarget(menuView, title, message),
    new TapTargetView.Listener()
    {
        @Override
        public void onTargetClick(TapTargetView view)
        {
            super.onTargetClick(view);
            view.dismiss(true);
        }

        @Override
        public void onOuterCircleClick(TapTargetView view)
        {
            super.onOuterCircleClick(view);
            view.dismiss(true);
        }
});

经过反复的搜索和测试,终于找到了可行的解决方案!

只需在 onCreateOptionsMenu() 中获取对您的菜单项的引用。启动一个处理程序,以便在您获取 id 引用之前正确地膨胀视图。否则你将得到 空视图错误

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

    new Handler().post(new Runnable() {
        @Override
        public void run() {
            final View view = findViewById(R.id.askHelp);

            TapTargetView.showFor(BasicInformation.this,                 // `this` is an Activity
                    TapTarget.forView(view, "You can tap here to get Chat Support")
                            // All options below are optional
                            .outerCircleColor(R.color.colorAccent)      // Specify a color for the outer circle
                            .outerCircleAlpha(0.96f)            // Specify the alpha amount for the outer circle
                            .targetCircleColor(R.color.white)   // Specify a color for the target circle
                            .titleTextSize(30)                  // Specify the size (in sp) of the title text
                            .titleTextColor(R.color.white)      // Specify the color of the title text
                            .textColor(R.color.white)            // Specify a color for both the title and description text
                            .textTypeface(Typeface.SANS_SERIF)  // Specify a typeface for the text
                            .dimColor(R.color.black)            // If set, will dim behind the view with 30% opacity of the given color
                            .drawShadow(true)                   // Whether to draw a drop shadow or not
                            .cancelable(true)                  // Whether tapping outside the outer circle dismisses the view
                            .tintTarget(true)                   // Whether to tint the target view's color
                            .transparentTarget(false)           // Specify whether the target is transparent (displays the content underneath)
                            .targetRadius(60),                  // Specify the target radius (in dp)
                    new TapTargetView.Listener() {          // The listener can listen for regular clicks, long clicks or cancels
                        @Override
                        public void onTargetClick(TapTargetView view) {
                            super.onTargetClick(view);      // This call is optional
                            //doSomething();
                        }
                    });

        }
    });


    return true;
}