I'm using the MaterialShowcaseView to give users an overview of my app. set a target for the showcase, it must be a View. Below is an example of a sequence:
String SHOWCASE_ID = getString(R.string.showcase_id);
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(
new MaterialShowcaseView.Builder(this)
.setTarget(findViewById(R.id.navigation_header))
.setDismissText(getString("GOT IT"))
.setTitleText(getString(R.string.onboarding_navigation_header_title))
.setContentText(getString(R.string.onboarding_navigation_header_content))
.withRectangleShape(false)
.build());
...
sequence.start();
In this example, I'm targeting the navigation header of the following NavigationView:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="#menu/main_navigation"
app:headerLayout="#layout/nav_header"/>
How would I get the menu in the snippet above as a View so I can pass that intosetTarget() for my showcase sequence?
I have tried using just the NavigationView, but that targets both the header layout and the menu. I have also tried getting a group of the menu by ID (findViewById(R.id.menu_group_id)), but I don't believe the menu group is technically a View. I can get the menu via NavigationView.getMenu(), but that returns a Menu object. Any ideas?
Edit: The entire menu needs to be targeted. On a side note,this NavigationView exists in a DrawerLayout, if that changes anything.
There is no direct way to get the view reference of menu group in NavigationView. You could remove or hide the header view at runtime when targets NavigationView, and add or show the header view again when targets other.
you could use getheaderview to get the header and call:
your_header.setVisibility(View.GONE) //hide
your_header.setVisibility(View.VISIBLE) //show
You can access the menu items by using them as action views. Set the actionViewClass in your menu.xml like so:
<item
...
app:actionViewClass="android.support.design.widget.NavigationView"
/>
And then in your activity:
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
...
sequence.addSequenceItem(
new MaterialShowcaseView.Builder(this)
.setTarget(findViewById(R.id.navigation_header))
.setDismissText(getString("GOT IT"))
.setTitleText(getString(R.string.onboarding_navigation_header_title))
.setContentText(getString(R.string.onboarding_navigation_header_content))
.withRectangleShape(false)
.build());
...
return true;
}
Related
Kind people! Tell me how to make such an elementary thing as a disabled menu item with the appropriate appearance, as in the example from material design? Programmatically. Thank you in advance
EDIT: The following examples will disable click, but do not change the appearance according to the material design:
Code example:
#Override
public void onPrepareOptionsMenu(#NonNull Menu menu) {
super.onPrepareOptionsMenu(menu);
MenuItem menuItem = menu.findItem(R.id.someItem);
menuItem.setEnable(false);
}
Xml example:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/someItem"
android:enabled="false"
android:title="Some title" />
</menu>
You can do Add, Remove, or Disable menu items and change text color of menu item at Runtime like that::
If you want to modify the options menu based on events that occur during the activity lifecycle, you can do so in the onPrepareOptionsMenu() method. This method passes you the Menu object as it currently exists so you can modify it, such as add, remove, or disable items. (Fragments also provide an onPrepareOptionsMenu() callback.)
On Android 2.3.x and lower, the system calls onPrepareOptionsMenu() each time the user opens the options menu (presses the Menu button).
On Android 3.0 and higher, the options menu is considered to always be open when menu items are presented in the app bar. When an event occurs and you want to perform a menu update, you must call invalidateOptionsMenu() to request that the system call onPrepareOptionsMenu().
You can see the Documentation
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/someItem"
android:enabled="false"
android:title="Some title"
app:actionViewClass="android.support.v7.widget.TextView"
// if using androidX
//app:actionViewClass="androidx.appcompat.widget.AppCompatTextView"/>
</menu>
And in java code
//sample code
#Override
public boolean onPrepareOptionsMenu (Menu menu) {
//disable menu item and change text color
MenuItem menu1 = menu.findItem(R.id.yourmenuid);
menu1.setEnabled(false);
TextView textView = (TextView) menu1.getActionView();
textView.setTextColor(Color.BLACK);
return true;
}
you must call invalidateOptionsMenu() to request that the system call onPrepareOptionsMenu().
I found a workaround on how to change the text color in the menu item. It is extremely sad that such an elementary thing is not provided out of the box. Perhaps someone has a better solution, I'll be glad to see!
MenuItem menuItem = menu.findItem(R.id.some_item);
menuItem.setEnabled(false);
SpannableString s = new SpannableString(menuItem.getTitle());
s.setSpan(new ForegroundColorSpan(Color.GRAY), 0, s.length(), 0);
menuItem.setTitle(s);
Result:
Background
I have a CoordinatorLayout based view, which has an AppbarLayout and RecyclerView as direct children. The AppBarLayout houses a search bar that collapses when you scroll the RecyclerView up. The XML for this view is:
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/search_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/search_app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
app:elevation="0dp">
<!-- Search bar -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/search_constraint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusableInTouchMode="true"
android:focusable="true"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed|snap">
<!-- Abbreviated -->
<EditText .../>
<!-- Abbreviated -->
<ImageView .../>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
<!-- List -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/scroll_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
When you tap on the EditText view in the search bar, that enables what I call "Search Mode". All that search mode does is disable the AppBarLayout from collapsing when scrolling the RecyclerView. The user can then type into the search bar - which filters the items in the RecyclerView - and then they can scroll the list without the search bar collapsing. I hook into the EditText onFocus events to perform this:
searchField.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
// When user taps the search bar, enable search mode
enableSearchMode()
}
}
And the enableSearchMode code is:
private fun enableSearchMode() {
...
itemRecyclerView.isNestedScrollingEnabled = false
...
}
Problem
This setup seems to work perfectly... most of the time. Randomly - maybe 1% of the time - when you touch the EditText to enable search mode, something goes wrong and I'm not able to effectively scroll the RecyclerView anymore. It is like it is stuck at the top of the list. If you try to scroll towards the bottom of the list, the scrolling jerks around, and generally jumps back up to the top of the list. As soon as the search mode is disabled, the problem goes away.
// Disable search mode
itemRecyclerView.isNestedScrollingEnabled = true
Despite an enormous amount of testing, I have not been able to consistently reproduce the issue or determine what progression of actions leads to it. It just randomly happens, as if there is some sort of race condition going on within the CoordinatorLayout.
I have stripped away so much code in my app to isolate the issue that I am confident the issue occurs precisely when I set isNestedScrollingEnabled to false. That said, I have also tried an alternative to disabling the AppBarLayout from moving when the RecyclerView is scrolled, which is to override the behavior of the AppBarLayout as described here. Oddly enough, this leads to the same problem. If I don't disable the AppBarLayout either through this means or via setting isNestedScrollingEnabled false, the issue never appears.
What is happening here?!?
What is happening here?!?
Setting isNestedScrollingEnabled to false, in fact, breaks the communication between your itemRecyclerView as scrolling child and AppBarLayout as it's parent. The normal behaviour is itemRecyclerView notifies it's parent AppBarLayout it's scrolling progress for which the parent is supposed to react to it by calculating it's collapsed height given any scrolling progress and all other stuffs.
I found somewhere that setting isNestedScrollingEnabled to false would cause the RecyclerView to not recycle its views. I can't say exactly if it's true but if it is then, I think it's cause for that glitch.
The solution that I would like to propose is to change the scroll_flags programmatically to NO_SCROLL so that AppBarLayout wouldn't react/scroll in scrolling of its child scrolling view.
Although, it's in java but following code snippet should help you.
// get a reference for your constraint layout
ConstraintLayout constraintLayout = findViewById(R.id.search_constraint);
// get the layout params object to change the scroll flags programmatically
final AppBarLayout.LayoutParams layoutParams = (AppBarLayout.LayoutParams) constraintLayout.getLayoutParams();
// flags variable to switch between
final int noScrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL;
final int defaultScrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
| AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
| AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP;
// now we will set appropriate scroll flags according to the focus
searchField.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
layoutParams.setScrollFlags(hasFocus ? noScrollFlags : defaultScrollFlags);
}
});
I'm trying to hide an Actionbar menu item if a shared preference is false.
I'm getting the shared preference as I want, but the menu item wont hide.
// Inflate a menu to be displayed in the toolbar
toolbar.inflateMenu(R.menu.activity_main_actionbar);
// Enable disable set start page item
if(!sharedPref.getBoolean("enable_custom_startpage", false)) {
toolbar.getMenu().findItem(R.id.setasstartpage).setVisible(false);
}
What am i doing wrong??
The right place for doing this is onPrepareOptionsMenu. From the docs,
Prepare the Screen's standard options menu to be displayed. This is
called right before the menu is shown, every time it is shown. You can
use this method to efficiently enable/disable items or otherwise
dynamically modify the contents.
So, I would recommend you to override onPrepareOptionsMenu and then check for the Shared Prefs inside it and show menu accordingly. Something like,
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// Enable disable set start page item
if(!sharedPref.getBoolean("enable_custom_startpage", false)) {
toolbar.getMenu().findItem(R.id.setasstartpage).setVisible(false);
}
return true;
}
try this
mToolbar.getMenu().findItem(id).setEnabled(false);
I hope it will work for you.
try this
invalidateOptionsMenu();
I hope it is helpful for you.
Get a MenuItem pointing to such item, call setVisible on it to adjust its visibility and then call invalidateOptionsMenu() on your activity so the ActionBar menu is adjusted accordingly.
Update: A MenuItem is not a regular view that's part of your layout. Its something special, completely different. Your code returns null for item and that's causing the crash. What you need instead is to do:
MenuItem item = menu.findItem(R.id.addAction);
Start by saving the menu globally in your activity-
Menu menuu;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
menuu=menu;
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
Have different menu.xml for you . one is having noitem and other is
containing the items you want.
menu_main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.afixi.prasenjeetpati.notification_service.MainActivity">
<item
android:id="#+id/action_settings"
android:orderInCategory="100"
android:title="#string/action_settings"
app:showAsAction="never" />
</menu>
menu_blank.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.afixi.prasenjeetpati.notification_service.MainActivity">
</menu>
Now to remove the option menu at any time do this-
menuu.clear();
getMenuInflater().inflate(R.menu.menu_blank, menuu);
and to get back the normal menu-
menuu.clear();
getMenuInflater().inflate(R.menu.menu_main, menuu);
I'm working on the navigation drawer where i want to show details of the user in the navigation drawer like profile,contact us,logout etc if he is not logged in then i want to show only one item which is have be login on it nothing else must present in it i will be using the JSON for Authenticate the login Please refer the pics friends
Your question is broad. However to achieve what you want, you will probably want to use the MaterialDrawer library. Just check their README it's very simple to use.
The "login" functionality you are looking for will be in the AccountHeader of your drawer, for instance:
// Create the AccountHeader
AccountHeader headerResult = new AccountHeaderBuilder()
.withActivity(this)
.withHeaderBackground(R.drawable.header)
.addProfiles(
new ProfileDrawerItem().withName("Mike Penz").withEmail("mikepenz#gmail.com").withIcon(getResources().getDrawable(R.drawable.profile))
)
.withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {
#Override
public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) {
return false;
}
})
.build();
//Now create your drawer and pass the AccountHeader.Result
new DrawerBuilder()
.withAccountHeader(headerResult)
//additional Drawer setup as shown above
...
.build();
(Snippet taken from the official README, again: every thing is there).
Your question is broad but if you are using navigation view you can change header view and drawer items using
navigationView.inflateHeaderView(layoutResId);
navigationView.inflateMenu(menuResId);
Navigation view is provided by android. So it is not a third party library and it is simple to integrate.
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- your content layout -->
<android.support.design.widget.NavigationView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="#layout/drawer_header"
app:menu="#menu/drawer"/>
</android.support.v4.widget.DrawerLayout>
You can find complete information here - http://android-developers.blogspot.in/2015/05/android-design-support-library.html
http://developer.android.com/reference/android/support/design/widget/NavigationView.html
I am making an android app and I am trying to apply some java code to the xml menu items but they are not getting inflated. I have some menu items defined within my menu xml file (stored in res/menu/main.xml) defined as follows:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".HomeActivity"
app:showAsAction="always">
<item android:id="#+id/action_close"
android:title="Close"
android:icon="#drawable/exitediting"
android:enabled="false"
app:showAsAction="always"/>
<item android:id="#+id/action_edit"
android:title="Edit"
android:icon="#drawable/editbutton"
android:enabled="false"
app:showAsAction="always"/>
<item android:id="#+id/action_mainedit"
android:title="MainEdit"
android:icon="#drawable/exitediting"
android:enabled="true"
app:showAsAction="always"/>
</menu>
Within my main activity java code I then override the menu inflator as follows:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
createAnimatorSets();
return true;
}
Then within my createAnimatorSets function I try and access the buttons like this
final ActionMenuItemView editButton = (ActionMenuItemView) toolbar.getChildAt(1).findViewById(R.id.action_edit);
final ActionMenuItemView closeButton = (ActionMenuItemView) toolbar.getChildAt(1).findViewById(R.id.action_close);
final ActionMenuItemView mainButton = (ActionMenuItemView) toolbar.getChildAt(1).findViewById(R.id.action_mainedit);
This simply returns null. I tried to put a break point at this point in the code and watch a few variables. R.id.action_edit is returning the id as expected but both findViewById(R.id.action_edit) and toolbar.getChildAt(1).findViewById(R.id.action_edit) are returning null. The toolbar itself (variable name toolbar) is there and the buttons are stored inside but they are for some reason not inflated in order to be utilised. I have also tried to do it in the onStart method rather than onCreate but this does not work either. Does anyone know how to fix this.
Thanks
You can get a reference to your MenuItems in the onCreateOptionsMenu(Menu menu) method by using
MenuItem edit = menu.findItem(R.id.action_edit);
View actionViewEdit = edit.getActionView();
once you have inflated your menu.
So after a lot of testing it seems that the menu only gets inflated in the onResume. This led to a very hacky solution to my problem but in the future if people struggle with this you need to do your stuff in onresume and just set the buttons to hidden initially within the xml if needed as I have done