简体   繁体   English

如何在 Android 中以编程方式隐藏菜单选项?

[英]How to hide menu option programmatically in Android?

I want to show/hide a menu option on the top bar programmatically from a fragment.我想以编程方式从片段中显示/隐藏顶部栏上的菜单选项。

There are two possible situations:有两种可能的情况:

  1. In the preferences the user indicates that he does not want to use that option.在首选项中,用户表示他不想使用该选项。 In that case, the option will not appear in the top bar menu.在这种情况下,该选项将不会出现在顶部栏菜单中。

  2. In the preferences the user indicates that he wants to use that option.在首选项中,用户表示他想使用该选项。 In that case, the option will appear in the top bar menu.在这种情况下,该选项将出现在顶部栏菜单中。

Regarding (1) there is no problem, the option is invisible by default.关于(1)没问题,默认不可见选项。

My problem is regarding (2).我的问题是关于 (2)。

When the fragment is opened I check the status of that option in the preferences.打开片段时,我会在首选项中检查该选项的状态。 When the user has indicated that he wants to use that option, I show it in the top bar.当用户表示他想使用该选项时,我会在顶部栏中显示它。 This is a text-to-speech reading option.这是一个文本到语音的阅读选项。 And also I want to hide the option in the top bar menu when there is a reading in progress.而且我还想在阅读过程中隐藏顶部栏菜单中的选项。

Everything works fine, but only the first time .一切正常,但只是第一次 That is, the first time I enter the fragment, if the option in the preferences is activated, I can see the button in the menu and I can use it without problems and I can also hide it when a voice reading is happening and show it again when the reading stops.也就是说,我第一次进入片段时,如果首选项中的选项被激活,我可以在菜单中看到按钮,我可以毫无问题地使用它,我也可以在语音朗读时隐藏它并显示它当阅读停止时再次。

This is a screenshot of my top bar the first time I load the fragment:这是我第一次加载片段时顶部栏的屏幕截图:

在此处输入图像描述

But if I choose the Calendar option (another fragment) and from the Calendar I return to the previous fragment (Tercia), it doesn't work the same for me.但是,如果我选择“日历”选项(另一个片段)并从“日历”返回到上一个片段(Tercia),它对我来说就不一样了。 Initially, the two options appear in the top bar but if I click on the Calendar option, the Play option is hidden and it no longer navigates to the Calendar .最初,这两个选项出现在顶部栏中,但如果我单击 Calendar 选项,则 Play 选项被隐藏并且不再导航到 Calendar What should happen in this case is that it navigates to the Calendar snippet.在这种情况下应该发生的是它导航到日历片段。

This is a screenshot of my top bar when I go back to the Tercia fragment from the Calendar and click on the Calendar button in the menu (the Play option disappears):这是我 go 从日历返回 Tercia 片段并单击菜单中的日历按钮(播放选项消失)时顶部栏的屏幕截图:

在此处输入图像描述

Another even more serious problem occurs.另一个更严重的问题出现了。 If I navigate to the Settings option (menu of three vertical dots) and then reopen the Tercia fragment (from which I navigated to Settings) and try to select the Calendar option, the App exits with an NPE:如果我导航到设置选项(三个垂直点的菜单),然后重新打开 Tercia 片段(我从中导航到设置)并尝试 select 日历选项,应用程序退出并出现 NPE:

java.lang.NullPointerException: Attempt to invoke interface method 'android.view.MenuItem android.view.Menu.findItem(int)' on a null object reference
    at org.my.app.ui.fragments.BreviarioDataFragment.setPlayerButton(BreviarioDataFragment.java:342)
    at org.myapp.app.ui.fragments.BreviarioDataFragment.lambda$observeIntermedia$3$org-myapp-app-ui-fragments-BreviarioDataFragment(BreviarioDataFragment.java:195)
    at org.myapp.app.ui.fragments.BreviarioDataFragment$$ExternalSyntheticLambda0.onChanged(Unknown Source:4)

How could I solve this problem?我该如何解决这个问题?

This is the relevant code from the Tercia fragment:这是 Tercia 片段中的相关代码:

public class BreviarioDataFragment extends Fragment implements TextToSpeechCallback {

    private Menu mainMenu;
    private boolean isReading = false;
    private boolean isVoiceOn;
    private StringBuilder sbReader;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        setHasOptionsMenu(true);
        binding = FragmentBreviarioDataBinding.inflate(inflater, container, false);
        View root = binding.getRoot();
        setConfiguration();
        observeHour();
        return root;
    }

    private void setConfiguration() {

        mViewModel =
                new ViewModelProvider(this).get(BreviarioViewModel.class);
        mTextView = binding.include.tvZoomable;

        progressBar = binding.progressBar;
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
        float fontSize = Float.parseFloat(prefs.getString("font_size", "18"));
        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
        hasInvitatorio=prefs.getBoolean("invitatorio",false);
        isVoiceOn = prefs.getBoolean("voice", true);
        if (isVoiceOn) {
            sbReader = new StringBuilder(VOICE_INI);
        }
        pickOutDate();
    }

    private void observeIntermedia(int hourId, String endPoint) {
        mTextView.setText(PACIENCIA);
        mViewModel.getIntermedia(mDate, hourId, endPoint).observe(getViewLifecycleOwner(), data -> {
            progressBar.setVisibility(View.GONE);
            if (data.status == DataWrapper.Status.SUCCESS) {
                mTextView.setText(data.getData().getForView());
                if (isVoiceOn) {
                    sbReader.append(data.getData().getForRead());
                    setPlayerButton();
                }
            } else {
                mTextView.setText(Utils.fromHtml(data.getError()));
            }
        });
    }

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        mainMenu = menu;
        inflater.inflate(R.menu.toolbar_menu, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.item_voz) {
            if (mActionMode == null) {
                mActionMode =
                        requireActivity().startActionMode(mActionModeCallback);
            }
            readText();
            isReading = true;
            requireActivity().invalidateOptionsMenu();
            return true;
        }

        NavController navController = NavHostFragment.findNavController(this);
        return NavigationUI.onNavDestinationSelected(item, navController)
                || super.onOptionsItemSelected(item);
    }

    @Override
    public void onPrepareOptionsMenu(@NonNull Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item = menu.findItem(R.id.item_voz);
        if (isReading) {
            item.setVisible(false);
        }
    }

    private void setPlayerButton() {
        mainMenu.findItem(R.id.item_voz).setVisible(isVoiceOn);
    }

    //...

}

This is the.xml of the menu:这是菜单的.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/item_voz"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:icon="@drawable/ic_play"
    android:visible="false"
    android:title="@string/leer"
    app:showAsAction="ifRoom" />
<item
    android:id="@+id/nav_calendario"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:icon="@drawable/ic_calendar_toolbar"
    android:title="@string/all_calendar"
    app:showAsAction="ifRoom" />


    <item
        android:id="@+id/empty"
        android:icon="@drawable/ic_more_vert_black_32dp"
        android:title="@string/title_fragment_settings"
        app:showAsAction="always">

        <menu>

            <item
                android:id="@+id/nav_settings"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:icon="@drawable/ic_settings_black_32dp"
                android:title="@string/title_fragment_settings"
                app:showAsAction="ifRoom" />

        </menu>
    </item>

</menu>

I'm using Navigation Component too:我也在使用导航组件:

<fragment
    android:id="@+id/nav_calendario"
    android:name="org.myapp.app.ui.fragments.CalendarioFragment"
    android:label="@string/title_fragment_calendario"
    tools:layout="@layout/fragment_calendario" />


<fragment
    android:id="@+id/nav_settings"
    android:name="org.myapp.app.ui.fragments.SettingsFragment"
    android:label="@string/title_fragment_settings"
     />

For the Play option I don't navigate anywhere, but display a progress bar using ActionMode.对于“播放”选项,我没有导航到任何地方,而是使用 ActionMode 显示进度条。 There is no problem in that sense.从这个意义上说没有问题。

Follow this code:请遵循以下代码:

 @Override
 public void onPrepareOptionsMenu(@NonNull Menu menu) {
        
        MenuItem item = menu.findItem(R.id.item_voz);
        if (isReading) {
            item.setVisible(false);
        }
       super.onPrepareOptionsMenu(menu);
  }

This solved the problem on updating the OptionsMenu without invoking the invalidateOptionsMenu()这解决了在不调用invalidateOptionsMenu()的情况下更新 OptionsMenu 的问题

You can set visibility of the menu from an activity instead of a fragment.您可以从活动而不是片段设置菜单的可见性。

Here is a code of activity.这是一个活动代码。

public class TestActivity extends AppCompatActivity {

private Menu mainMenu;
private MenuItem menuVoz;
private boolean isReading;

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);
}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_toolbar, menu);
    mainMenu = menu;
    menuVoz = mainMenu.findItem(R.id.item_voz);
    if (isReading) {
        menuVoz.setVisible(false);
    } else {
        menuVoz.setVisible(true);
    }
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    switch (item.getItemId()) {

        case R.id.item_voz:
            // TODO Add your code
            readText();
            isReading = true;
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

public void updateMenu() {
    isReading = true; // Read vale from your resource
    if (isReading) {
        menuVoz.setVisible(false);
    } else {
        menuVoz.setVisible(true);
    }
}}

updateMenu() is a public method. updateMenu()是一个公共方法。 You can call this method from Fragment of this activity by casting activity context whenever youo need to update it.每当您需要更新它时,您都可以通过转换活动上下文从该活动的片段中调用此方法。 For example,例如,

((TestActivity) getActivity()).updateMenu();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM