I want to update my UI based on the ongoing Selected Item from the BottomNavigationView. The problem is that the item ID will be changed after the whole method inside the listener is executed, and method getSelectedItemId() will refer to the item that was selected before when I called it inside updateUI() method.
How I can work around this?
Here is the code:
mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_baby:
case R.id.navigation_you:
case R.id.navigation_partner: {
updateUI();
return true;
}
default:
return false;
}
}
});
Update Method:
The value of mBottomNavigationView.getSelectedItemId() refer to the previous selection
private void updateUI() {
mBagListAdapter.setList(mPregnancyLab.getBagList(mBottomNavigationView.getSelectedItemId()));
mBagListAdapter.notifyDataSetChanged();
}
Try this
mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
mBagListAdapter.setList(mPregnancyLab.getBagList(item.getItemId()));
BagListAdapter.notifyDataSetChanged();
}
});
Hope this helps. Feel free to ask for clarifications...
Related
Currently I am working on an app which has a bottom navbar with three menu items. I had used setOnNavigationItemSelectedListener() for items being clicked. but now iam facing issue that the method has been depreciated.
App Language: Java
Issue: 'setOnNavigationItemSelectedListener(com.google.android.material.bottomnavigation.BottomNavigationView.OnNavigationItemSelectedListener)' is deprecated
Is there any way to resolve it? is is there any better alternative than setOnNavigationItemSelectedListener() method.
Its deprecated according to github sources: BottomNavigationView.setOnNavigationItemSelectedListener
In its comment you can read:
#deprecated Use {#link NavigationBarView#setOnItemSelectedListener(OnItemSelectedListener)}
* instead.
so use NavigationBarView.setOnItemSelectedListener from its base class:
/**
* Set a listener that will be notified when a navigation item is selected. This listener will
* also be notified when the currently selected item is reselected, unless an {#link
* OnItemReselectedListener} has also been set.
*
* #param listener The listener to notify
* #see #setOnItemReselectedListener(OnItemReselectedListener)
*/
public void setOnItemSelectedListener(#Nullable OnItemSelectedListener listener) {
selectedListener = listener;
}
Also see this commit
as it explains confusion about this change:
The listeners were deprecated in favor of
NavigationBarView#OnItemSelectedListener and
NavigationBarView#OnItemReselectedListener, but deprecation
documentation was never added, so it's unclear what developers should
use instead.
you can try setonItemSelectedListener. It is working same as setOnNavigationItemSelectedListener()[tested in android 11]
bnv.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
switch(id){
//check id
}
return true;
}
});
Kotlin:
bnv.setOnItemSelectedListener { item ->
when (item.itemId) {
}
true
}
We can use setOnItemSelectedListener instead of setOnNavigationItemSelectedListener and
setOnItemReselectedListener instead of setOnNavigationItemReselectedListener
navView.setOnItemSelectedListener {
// do something
true
}
// In case the default menu can be the first menu
// Should set the default selected menu BETWEEN setOnItemSelectedListener and setOnItemReselectedListener.
// It will make setOnItemSelectedListener fired when you launch app.
// If you set default menu AFTER setOnItemReselectedListener.
// Then setOnItemReselectedListener will fired when you launch app
navView.selectedItemId = R.id.navigation_home
navView.setOnItemReselectedListener {
// do something
}
you can use in Kotlin
buttmNav.setOnItemSelectedListener { item ->
when (item.itemId) {
}
true
}
viewBindingMainActivity.navView.setOnItemSelectedListener { menuItem ->
if (menuItem.itemId != R.id.navigation_home) {
Add your code
false
} else {
Add your code
true
}
}
kotlin:
use setOnItemSelectedListener
bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
when (item.itemId) {
R.id. ... -> {
Add your code
true
}
else ->
true
}
public class HomeActivity extends AppCompatActivity implements NavigationBarView.OnItemSelectedListener {
BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
bottomNavigationView = findViewById(R.id.bottom_nav);
bottomNavigationView.setOnItemSelectedListener(this);
displayfragment(new FragmentHome());
}
private void displayfragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.content_area, fragment).commit();
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment;
switch (item.getItemId()) {
case R.id.nav_home:
fragment = new FragmentHome();
break;
case R.id.nav_fav:
fragment = new FavouriteFragment();
break;
case R.id.nav_set:
fragment = new FragmentSetting();
break;
default:
fragment = new FragmentHome();
break;
}
displayfragment(fragment);
return true;
}
}
The user presses the hide keyboard button or the back button.
So I need to clear focus on the SearchView when the user is hiding the keyboard.
I tried this but it's not working. focus remains when the user hides the keyboard.
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
searchView.clearFocus();
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
app.requests.getApi().search(newText).enqueue(SearchFragment.this);
return false;
}
});
and this:
searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
app.functions.logWrite("has focus to searchview");
} else {
//code
}
}
});
Okay so try this it needs the use of a library unfortunately but it makes it easier.
In your build.gradle: add this:
dependencies {
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'
}
Register for the keyboard events using KeyboardVisibilityEvent library like this in the fragment/class where SearchView is declared:
KeyboardVisibilityEvent.setEventListener(
getActivity(),
new KeyboardVisibilityEventListener() {
#Override
public void onVisibilityChanged(boolean isOpen) {
if (!isOpen) {
View focusedView = getWindow().getCurrentFocus();
if (focusedView != null && focusedView instanceof SearchView) { // does SearchView have focus?
searchView.clearFocus();
}
}
}
});
searchView.clearFocus(); works on the assumption you have another focusable view in the hierarchy, if not add this to your fragments layout:
android:focusableInTouchMode="true"
Alternatively simply call focus(); on any other view element you want to receive focus.
This is what I use for handling back button clicks for SearchView, by Overriding onBackPressed()
#Override
public void onBackPressed() {
if (!searchView.isIconified()) {
searchView.setIconified(true);
ANY_VIEW_IN_YOUR_LAYOUT.requestFocus();
} else
super.onBackPressed();
}
Hope this helps. Feel free to ask for clarifications...
I have a menu item system implemented into my action bar, I am trying to get a menu item to become invisible once clicked and then to become visible again after clicking a different menu item.
The reason for this is to prevent a menu item getting clicked more than once.
I have done the following attempt below however, I get a null pointer exception error. I believe I may need to implement some if statements here but I'm not sure how to go about it.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.tasklistmenu,menu);
return true;
}//OnCreateOptionsMenu
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.allTasks:
item.setVisible(false);
MenuItem notStartedStatus = findViewById(R.id.notStartedStatus);
notStartedStatus.setVisible(true);
MenuItem inProgressStatus = findViewById(R.id.inProgressStatus);
inProgressStatus.setVisible(true);
MenuItem completeStatus = findViewById(R.id.completeStatus);
completeStatus.setVisible(true);
Log error
Attempt to invoke interface method 'android.view.MenuItem
android.view.MenuItem.setVisible(boolean)' on a null object reference
NEW CODE
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem allTasks = menu.findItem(R.id.allTasks);
MenuItem notStartedStatus = menu.findItem(R.id.notStartedStatus);
MenuItem completeStatus = menu.findItem(R.id.completeStatus);
MenuItem inProgressStatus = menu.findItem(R.id.inProgressStatus);
if(tasksIsVisible) {
allTasks.setVisible(true);
} else {
allTasks.setVisible(false);
}
if(notStartedIsVisible) {
notStartedStatus.setVisible(true);
} else {
notStartedStatus.setVisible(false);
}
if(completeIsVisible) {
completeStatus.setVisible(true);
} else {
completeStatus.setVisible(false);
}
if(inProgressIsVisible) {
inProgressStatus.setVisible(true);
} else {
inProgressStatus.setVisible(false);
}
return true;
}//OnPrepareOptions
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.allTasks:
tasksIsVisible = false;
invalidateOptionsMenu();
Its not enough to change the isVisible variable. You have to call the setVisible() method every time you want to change the visibility. That method does more than just setting a boolean value, so just changing a boolean value will not do.
After changing the isVisible value to false, you need to call invalidateOptionsMenu() which will re-launch the menu by calling onPrepareOptionsMenu() again.
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuItem notStartedStatus = menu.findItem(R.id.notStartedStatus);
if(isVisible)
{
notStartedStatus.setVisible(true);
}
else
{
notStartedStatus.setVisible(false);
}
return true;
}
Try this code for making the menu items unvisible:
...
isVisible = false;
invalidateOptionsMenu();
...
This question already has answers here:
Android: Best way to share code between activities? [closed]
(2 answers)
Closed 4 years ago.
I have multiple activities sharing the same options menu so in my every activity, I am doing
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.settings:
Intent opensettings = new Intent(this, SettingsActivity.class);
startActivity(opensettings);
return true;
case R.id.help:
...others
default:
return super.onOptionsItemSelected(item);
}
}
Is there a way to share the above code amongst different activities?
I have tried adding a class
class MenuHelper{
Context ctx;
public MenuHelper(Context context){
ctx=context
}
public boolean openMenuItems(Menu item){
switch(item.getItemId()) //here .getItemId() doesnt work{
case R.id.settings: //R.id.settings not found
}
}
}
But am stuck in my helper class. How do I proceed so that in my different activities I only have to
MenuHelper menuitems = new MenuHelper(this);
menuitems.openMenuItems(menu)
You can have a BaseActivity where you can put the common implementation across your activities and then have other activities extend the BaseActivity
public class BaseActivity extends AppCompatActivity {
// Any other common methods
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.settings:
Intent opensettings = new Intent(this, SettingsActivity.class);
startActivity(opensettings);
return true;
case R.id.help:
...others
default:
return super.onOptionsItemSelected(item);
}
}
}
You can now create your activities extending BaseActivity:
public class MainActivity extends BaseActivity {
}
Why not just create a super class for your common Activities? If you create a super class, like so:
public class MySharedMenuActivity extends Activity {
#Override
public boolean onOptionsItemSelected(MenuItem item) { ... }
}
Then, if you extend that class for the activities you want, you will be able to access the shared menu.
Inheritance
As the other responses suggest, you could use inheritance to provide this sort of functionality. That does break the "favor composition over inheritance rule", but may be the practical solution for simple applications.
Composition
I think you are on the right path with creating a "menu helper" of sorts. I'd prefer a name such as OptionsMenuHandler and would probably write it like this:
public class OptionsMenuHandler {
private final Activity activity;
public OptionsMenuHandler(Activity activity) {
this.activity = activity;
}
public boolean onCreateOptionsMenu(Menu menu) {
// do menu inflation here.
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.settings:
Intent openSettings = new Intent(activity, SettingsActivity.class);
activity.startActivity(openSettings);
return true;
case R.id.help:
// others
default:
return false;
}
}
public boolean onPrepareOptionsMenu(Menu menu) {
// do menu preparation here.
}
}
and use it like this:
public class TestActivity extends AppCompatActivity {
private final OptionsMenuHandler optionsMenuHandler = new OptionsMenuHandler(this);
#Override
public boolean onCreateOptionsMenu(Menu menu) {
return optionsMenuHandler.onCreateOptionsMenu(menu) ||
super.onCreateOptionsMenu(menu);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
return optionsMenuHandler.onPrepareOptionsMenu(menu) ||
super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return optionsMenuHandler.onOptionsItemSelected(item) ||
super.onOptionsItemSelected(item);
}
}
This does require extra boiler plate in each Activity. It also creates abstraction. The abstraction is justified because it keeps the code DRY. I also like the fact that business logic isn't tucked away and invisible inside a parent class somewhere... the composition makes the location of the business logic a lot more obvious.
Base Activity that Supports Composition
Another option would be to support composition in a base Activity as follows...
Create a well defined abstraction:
public interface OptionsMenuHandler {
boolean onCreateOptionsMenu(Menu menu);
boolean onOptionsItemSelected(MenuItem item);
boolean onPrepareOptionsMenu(Menu menu);
}
Create an implementation for the abstraction:
public class DefaultOptionsMenuHandler implements OptionsMenuHandler {
private final Activity activity;
public DefaultOptionsMenuHandler(Activity activity) {
this.activity = activity;
}
public boolean onCreateOptionsMenu(Menu menu) {
// do menu inflation here.
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.settings:
Intent openSettings = new Intent(activity, SettingsActivity.class);
activity.startActivity(openSettings);
return true;
case R.id.help:
// others
default:
return false;
}
}
public boolean onPrepareOptionsMenu(Menu menu) {
// do menu preparation here.
}
}
Support composition in the base class (ie base class has a setter):
public class BaseActivity extends AppCompatActivity {
#Nullable
private OptionsMenuHandler optionsMenuHandler;
protected void setOptionsMenuHandler(OptionsMenuHandler optionsMenuHandler) {
this.optionsMenuHandler = optionsMenuHandler;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
return optionsMenuHandler != null
? optionsMenuHandler.onCreateOptionsMenu(menu)
: super.onCreateOptionsMenu(menu);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
return optionsMenuHandler != null
? optionsMenuHandler.onPrepareOptionsMenu(menu)
: super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return optionsMenuHandler != null
? optionsMenuHandler.onOptionsItemSelected(item)
: super.onOptionsItemSelected(item);
}
}
Set the implementation in the Activity that needs the functionality.
public class TestActivity extends BaseActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setOptionsMenuHandler(new DefaultOptionsMenuHandler(this));
}
}
The net benefit here is that you write the main boilerplate once and support it through all of your activities. You can also continue to keep your business logic defined in the top level activity - where it goes with the other various logic for that particular activity.
Most non-trivial apps would benefit from something along these lines. I typically do something even more robust that supports zero or more OptionsMenuHandlers being set in any given activity where each handler supports a specific type of functionality. The code for this is fairly long and many considerations are needed, so I won't produce it here.
I have implemented the Android search widget in my navigation drawer based app. I have set it to open the keyboard and focus the editText when clicking on the search icon. I want to set the back button (up button) to hide the keyboard. I have searched the web for the R.id of the up button, and found this android.R.id.home. So I have set it to be:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
...
case android.R.id.home:
hideKeyboard();
break;
...
}
I debugged the code and noticed that clicking on the navigation bar icon fires up the android.R.id.home, but hitting the up button of the search widget doesn't even enter the onOptionsItemSelected(MenuItem item) function.
I have also tried this:
searchView.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard();
}
}
});
But didn't work.
How can I hide the keyboard when pressing the back (up) button?
Setting the search view:
private void setSearchView(Menu menu) {
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
// Assumes current activity is the searchable activity
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false);
SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener() {
public boolean onQueryTextChange(String newText) {
Home.getFilter().filter(newText);
return true;
}
public boolean onQueryTextSubmit(String query) {
return true;
}
};
searchView.setOnQueryTextListener(queryTextListener);
}
The following code should work:
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setOnCloseListener(new OnCloseListener() {
#Override
public bool OnClose() {
searchView.clearFocus();
return true;
});
However this didn't work for me for some reason. :-/
I found the workaround below here:
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
#Override
public void onViewDetachedFromWindow(View v) {
searchView.clearFocus();
}
#Override
public void onViewAttachedToWindow(View v) {
}
});
I don't think that using android.R.id.home will work since I think that onOptionsItemSelected(android.R.id.home) will only be called once the SearchView has been closed.