I have an ActionBar with a SearchView. The hidden/overflow menu items are shown on the right in a drop-down list when the menu button is selected. When the drop-down menu is hidden, the SearchView is focused and the keyboard shows. Is there a way to stop the keyboard from showing (except for the case where the SearchView is touched)?
Regards,
Julius.
Edit added code below:
Here is how I initialise it:
SearchManager searchManager = (SearchManager) mActivity.getSystemService(Context.SEARCH_SERVICE);
((SearchView) mSearchView).setSearchableInfo(searchManager.getSearchableInfo(mActivity.getComponentName()));
mSearchView.setIconifiedByDefault(mIconified);
mSearchView.setOnQueryTextListener(this);
mSearchView.setOnCloseListener(this);
mSearchView.setFocusable(false);
mSearchView.setFocusableInTouchMode(false);
if(null!=mQuery)
mSearchView.setQuery(mQuery, false);
Edit 2:
Here is what I do when the user wants to start the search:
#Override
public boolean onQueryTextSubmit(String query) {
// Hide keyboard
InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
FragmentActivity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);
...
mSearchView.setFocusable(false);
mSearchView.setFocusableInTouchMode(false);
return true;
}
try calling mSearchView.clearFocus() on your initialization of the searchView. That worked for me when I had a similar problem.
try call setFocusable(false) on SearchView when you init it.
I instantiated a search view when creating the options menu in a fragment and none of the above methods worked for me.
However, I was able to solve the problem by holding a reference to the SearchView, NOT setting the SearchView's iconified setting in onCreateOptionsMenu(), but setting it on a post when the fragment resumed like follows:
#Override
public void onResume() {
super.onResume();
getView().post(new Runnable() {
public void run() {
if (mFilterView.isIconfiedByDefault()) {
mFilterView.setIconifiedByDefault(false);
mFilterView.clearFocus();
}
}
});
}
Call requestFocus() for another view, ideally your results view, e.g.
_resultsFragment.getView().requestFocus();
I have just been pulling my hair out over the same issue.
I tried android:focusable="false". I tried the InputMethodManager. I tried creating a dummy view to take the focus but nothing worked.
I finally solved it with the following code inside onCreateOptionsMenu():
final ViewTreeObserver observer = mSearchView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
#Override
public void onGlobalLayout() {
observer.removeOnGlobalLayoutListener(this);
mSearchView.clearFocus();
}
});
The issue was that at every point in the Fragment's lifecycle the menu had not been created yet. The onCreateOptionsMenu() method was being called after onResume(), but only on some devices I tested on, which is really strange.
The above code makes sure that clearing the focus is done after any focus changes during creation of the page.
I hope this helps someone else, it would have saved me a lot of time to know this from the start.
When you set up your SearchView in your XML layout file (if you're using one), just use this:
android:focusable="false"
With this approach, your SearchView won't gain focus until you touch it...no matter where or if you "init" it in your code, or you hide your menu, or any other activity that might not have occurred in your debugging. This also eliminates the possible need to track where you are calling
setFocusable(false) //essentially does the same thing
in your code multiple times as the accepted answer suggests.
Related
I have a Toolbar being used as an ActionBar with two items. I only want to ever display one at a time as they kind of replace each other. The problem is that when i replace a Fragment, it call onCreateOptionsMenu and will inflate the menu again, meaning that the same action button will be shown, even if the other one was previously in the ActionBar. I have to need to change anything in the ActionBar from my Fragments or when a new Fragment is displayed(with FragmentManager.FragmentTransaction.replace()). So my question is how do I not call onCreateOptionsMenu when a new fragment is displayed?
I can't use a boolean because I will still need it to reinflate on orientation change. And any advice on how to handle orentation change for my situation?
I can post code, but it seems more conceptual and I'm not sure that it would help.
I solved the problem by instead of not calling onCreateOptionsMenu, I added the items to my menu manually.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean refreshVisible;
if (refreshItem != null && refreshItem.isVisible()){//is being displayed prior to inflation
refreshVisible = true;
}else if (refreshItem == null){//it's null so the menu has never been created
refreshVisible = true;
}else {//it's not null and invisibe, other icon was being displayed
refreshVisible = false;
}
menu.clear();//clear menu so there are no duplicate or overlapping icons
getMenuInflater().inflate(R.menu.main, menu);//inflate menu
refreshItem = menu.findItem(R.id.refresh);
useDataItem = menu.findItem(R.id.use_data);
refreshItem.setVisible(refreshVisible);//if menu is being created for first time or item was previously visible, then display this item
useDataItem.setVisible(!refreshVisible);//display this item if not displaying other
return true;
}
I would fiddle with the onPrepareOptionsMenu hook. If you can detect that your menu should not be shown you should jest return false from there. Per documentation:
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.
and
You must return true for the menu to be displayed; if you return false it will not be shown.
You can call setHasOptionsMenu(false); inside your fragment.
This will prevent onCreateOptionsMenu() from being called when that fragment added.
I have been searching a lot on invalidateOptionsMenu() and I know what it does. But I cannot think of any real life example where this method could be useful.
I mean, for instance, let's say we want to add a new MenuItem to our ActionBar, we can simply get the Menu from onCreateOptionsMenu(Menu menu) and use it in any button's action.
Now to my real question, is following the only way of using invalidateOptionsMenu()?
bool _OtherMenu;
protected override void OnCreate (Bundle bundle)
{
_OtherMenu = false;
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
Button button = FindViewById<Button> (Resource.Id.myButton);
button.Click += delegate
{
if(_OtherMenu)
_OtherMenu = false;
else
_OtherMenu = true;
InvalidateOptionsMenu ();
};
}
public override bool OnCreateOptionsMenu (IMenu menu)
{
var inflater = this.SupportMenuInflater;
if(_OtherMenu)
inflater.Inflate (Resource.Menu.another_menu, menu);
else
inflater.Inflate (Resource.Menu.main_activity_menu, menu);
return base.OnCreateOptionsMenu (menu);
}
Click the button and a different menu appears. Click the button again and previous menu appears.
P.S. Sorry for the C# syntax.
invalidateOptionsMenu() is used to say Android, that contents of menu have changed, and menu should be redrawn. For example, you click a button which adds another menu item at runtime, or hides menu items group. In this case you should call invalidateOptionsMenu(), so that the system could redraw it on UI. This method is a signal for OS to call onPrepareOptionsMenu(), where you implement necessary menu manipulations.
Furthermore, OnCreateOptionsMenu() is called only once during activity (fragment) creation, thus runtime menu changes cannot be handled by this method.
All can be found in documentation:
After the system calls onCreateOptionsMenu(), it retains an instance
of the Menu you populate and will not call onCreateOptionsMenu() again
unless the menu is invalidated for some reason. However, you should
use onCreateOptionsMenu() only to create the initial menu state and
not to make changes during the activity lifecycle.
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 action bar. When an event
occurs and you want to perform a menu update, you must call
invalidateOptionsMenu() to request that the system call
onPrepareOptionsMenu().
use this to reload new menu during app lifecycle:
new:
getActivity().invalidateOptionsMenu();
old
ActivityCompat.invalidateOptionsMenu(getActivity());
You need to override method onPrepareOptionsMenu(), write your update code of action menu in same method and if you are using fragment then add setHasOptionsMenu(true); in onCreateView().
Hope it helps you
One use I've found is forcing an order of operations between onResume and onCreateOptionsMenu/onPrepareOptionsMenu. The natural order (as of platform 22 at least) seems to flip flop around, especially when re-orientating your device.
Call invalidateOptionsMenu() in onResume() and you'll guarantee that onPrepareOptionsMenu will be called after onResume (it may additionally be called before). For example, this will allow enabling a menu item based on data retrieved in onResume.
/**
* Set a hint for whether this fragment's menu should be visible. This
* is useful if you know that a fragment has been placed in your view
* hierarchy so that the user can not currently seen it, so any menu items
* it has should also not be shown.
*
* #param menuVisible The default is true, meaning the fragment's menu will
* be shown as usual. If false, the user will not see the menu.
*/
public void setMenuVisibility(boolean menuVisible) {
if (mMenuVisible != menuVisible) {
mMenuVisible = menuVisible;
if (mHasMenu && isAdded() && !isHidden()) {
mHost.onSupportInvalidateOptionsMenu();
}
}
}
XML menu sample:
<?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_edit"
android:icon="#drawable/ic_edit"
android:title="Edit Task"
app:showAsAction="always" />
<item
android:id="#+id/action_delete"
android:icon="#drawable/ic_delete"
android:title="Delete Task"
app:showAsAction="always" />
<item
android:id="#+id/action_check"
android:icon="#drawable/ic_check"
android:title="Check Task"
app:showAsAction="always" />
<item
android:id="#+id/action_uncheck"
android:icon="#drawable/ic_undo"
android:title="Uncheck Task"
app:showAsAction="always" />
</menu>
Code inside a sample fragment:
private boolean isMenuItemChecked;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setMenuVisibility(false);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.my_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
try {
menu.findItem(R.id.action_check).setVisible(!isMenuItemChecked);
menu.findItem(R.id.action_uncheck).setVisible(isMenuItemChecked);
}
catch(Exception e) {
Log.e(TAG, "onPrepareOptionsMenu error");
}
}
public void loadUi(boolean isMenuItemChecked) {
this.isMenuItemChecked = isMenuItemChecked;
setMenuVisibility(true);
}
Put the initial state of the menu in onCreateOptionsMenu(...).
Use the invalidateOptionsMenu() to force onCreateOptionsMenu(...) and onPrepareOptionsMenu(...).
In onPrepareOptionsMenu(...), call menu.clear() to remove all items from the menu.
Still in onPrepareOptionsMenu(...) place your dynamic menu changes after the clear.
Edit: Here is a better answer to the question.
A good use for invalidateOptionsMenu() is when we have a ListView and Delete All MenuItem so when the ListView is empty we should use invalidateOptionsMenu() to remove the Delete All MenuItem.
Here is a question related to this answer: Question.
It's old, but hope this helps some one out in the future.
One use I found on real life scenario:
Assume you've a list of items that are stored into database, and you've 2 activities:
DisplayActivity: which displayed these objects after getting them
from database.
EditActivity: used to edit an existing item & save that into database.
You decided to have a couple of options to go from DisplayActivity to EditActivity:
First: To add a brand-new item into database.
Second: To edit an existing item.
In order not to repeat yourself by duplicating code, you decided to use EditActivity for both purposes.
And so, you want to customize Options Menu according to each purpose. For this case you'd build a default options menu using onCreateOptionsMenu(), and leave it as-is when it's time to edit an existing item; and invalidateOptionsMenu() it when it's time to create new items; and in this case onPrepareOptionsMenu() is auto triggered for customizing your menu.
For instance the Options menu can have a delete option for editing an existing item, and this should be hidden when adding a new item.
From fragment call getActivity().invalidateOptionsMenu();.
I have an ActionView with menu item on ActionBar (using ActionBarSherlock), I'm able to display an EditText as a search field in it. It's an input to launch another Activity with a CustomView in ActionBar which it displays the same layout (I don't use anything to force the SoftKeyboard to appear in this second activity, there is no problem here). When I want to make the Soft Keyboard appears/disapears automatically when the view collapse in first activity, I use:
openKeyboard method
mImm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
closeKeyboard method
mImm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
I use the ActionExpandListener to make the SoftKeyboard appears or disappears when the View expands or collapse. With these two methods above, I have the expected result. I found this on several questions on SO (especially on Close/hide the Android Soft Keyboard and Showing the soft keyboard for SearchView on ActionBar or Forcing the Soft Keyboard open).
Just to understand, when I used SHOW_IMPLICIT or SHOW_FORCED alone, it was no effect on lower versions (as 2.+). The EditText was focused but the keyboard didn't show up (so, you guess it was a bad thing). In recent versions (as 4.+ for example), it was a nice effect and no problem. Then, I forced keyboard to show up with the openKeyboard method above.
Now, I got some troubles with this...
On lower versions, I got "empty" space before and after the keyboard created/destroyed, I can live with this. BUT in recent versions, I got "empty" space which it displays when I return to the first Activity. And it's here during less than one second, but sufficient to see that!
To better understand what happens, see the image below:
1. Second Activity: I press the Up Home Button - the keyboard disappears properly.
2. (back to) First Activity: my ListView is covered by a "empty" space (background color in my application). And it disappears (this is the same height of the SoftKeyboard, no possible doubt!)
I guess it's because I forced the keyboard to appear in my first activity although I also forced the keyboard to hide when I go the second, but how can I resolve the "empty" space when I return to the first activity?
Summary
1) A activity => press item in menu > view collapse > show the keyboard > tap text > send it > hide keyboard > launch B activity.
2) B activity => setCustomView in actionbar > show the keyboard only if the edittext is focused/clicked > tap text > send it > hide keyboard > refresh content > press home button > return to A activity
3) A activity => "empty" screen > screen disappears.
Any help will be very appreciate.
Thanks for your time.
EDIT
I add my code of my first class, to see if someone tells me what I'm doing wrong. Maybe it's my code which makes the issue.
Menu (ActionView)
ActionBar actionBar;
MenuItem itemSearchAction;
EditText mSearchEdit;
InputMethodManager mImm;
#Override
public boolean onCreateOptionsMenu(final Menu menu) {
getSupportMenuInflater().inflate(R.menu.main, menu);
itemSearchAction = menu.findItem(R.id.action_search);
View v = (View) itemSearchAction.getActionView();
mSearchEdit = (EditText) v.findViewById(R.id.SearchEdit);
itemSearchAction.setOnActionExpandListener(this);
return true;
}
OnActionExpandListener
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
actionBar.setIcon(R.drawable.ic_app_search); // change icon
mSearchEdit.requestFocus(); // set focus on edittext
openKeyboard(); // the method above
mSearchEdit.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
closeKeyboard(); // same method as above
// new Intent() to second activity
// perform with startActivity();
itemSearchAction.collapseActionView(); // collapse view
return true;
}
return false;
}
});
// add a clicklistener to re-open the keyboard on lower versions
mSearchEdit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
openKeyboard();
}
});
return true;
}
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
actionBar.setIcon(R.drawable.ic_app_logo); // change icon again
if(!(mSearchEdit.getText().toString().equals("")))
mSearchEdit.setText(""); // reinitial the edittext
return true;
}
OnOptionsItemSelected
// I had this verification when I make new Intent() to
// a new activity, just in case (works like a charm)
if(itemSearchAction.isActionViewExpanded())
itemSearchAction.collapseActionView();
ActionView (Item + layout)
<item
android:id="#+id/action_search"
android:icon="#drawable/ic_app_search"
android:title="#string/action_search"
android:showAsAction="ifRoom|collapseActionView"
android:actionLayout="#layout/search_actionview" />
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/SearchEdit"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_gravity="right|bottom" android:gravity="left"
android:layout_marginBottom="6dip"
android:hint="#string/action_search"
android:textColor="#color/white"
android:textColorHint="#color/white"
android:singleLine="true"
android:cursorVisible="true"
android:inputType="text"
android:imeOptions="actionSearch|flagNoExtractUi"
android:imeActionLabel="#string/action_search"
android:focusable="true" android:focusableInTouchMode="true"
android:background="#drawable/bt_edit_searchview_focused" >
<requestFocus />
</EditText>
UPDATE
I see a lot of similar issues, with EditText in ActionBar which not makes the keyboard appear even the focus has set. I tried this again (even if I already tested several time):
/*
* NOT WORKING
* Sources: https://stackoverflow.com/questions/11011091/how-can-i-focus-on-a-collapsible-action-view-edittext-item-in-the-action-bar-wh
* https://stackoverflow.com/a/12903527/2668136
*/
int mode = WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
getWindow().setSoftInputMode(mode);
postDelayed() to show: .showSoftInput(mSearchEdit, InputMethodManager.SHOW_IMPLICIT); - 200ms (Not working on lower versions)
postDelayed() to hide: .hideSoftInputFromWindow(mSearchEdit.getWindowToken(), 0); - 200ms
new Runnable() on edittext => requestFocus() + showSoftInput(SHOW_IMPLICIT/FORCED/HIDE_NOT_ALWAYS/HIDE_IMPLICIT_ONLY)
It seems with me, only SHOW_FORCED|HIDE_IMPLICIT_ONLY can force the keyboard to show automatically when the view collapse. After this, in all versions, I must to make a hideSoftInputFromWindow to 0 for hiding it.
BUT this undisplays the keyboard even if the edittext is pressed, so I added an ClickListener to force the keyboard to show again (this happens only on lower versions).
UPDATE2:
It's clearly weird, when I try to make a little Thread like I saw in many SO answers (with/without ABS), nothing happens in lower versions.
I tried a different way. I created the new thread to have a short time before call the new intent for hide the keyboard. I had the keyboard which forced to close, OK. And then I opened the new activity, OK. But now when I return, it's worth! The "empty" space is also on lower versions when I come back. I did this:
// close the keyboard before intent
closeKeyboard();
// make the intent after 500 ms
Handler handler = new Handler();
Runnable runner = new Runnable() {
public void run() {
// new intent with startActivity()
}
};
handler.postDelayed(runner, 500);
// collapse the View
itemSearchAction.collapseActionView();
It gives me headaches! I don't understand why in my case, the tip above not working whereas on other answers, when they use a new thread to show/hide the keyboard, this works perfectly.
NOTE: my tests were on (emulator:) GalaxyNexus, NexusS, NexusOne and (real devices:) Samsung GalaxySL (2.3.6) and Nexus4 (4.4).
If someone can help me with this ugly situation. Thanks in advance.
Did you tried this one?
setContentView(R.layout.activity_direction_3);
getWindow().getDecorView().setBackgroundColor(
android.R.color.transparent);
Remove Translucent theme:
android:theme="#style/Theme.AppCompat.Translucent"
and use your Activity in manifest.xml as:
<activity
android:name=".SearchActivity"
android:screenOrientation="portrait">
I had the same issue.
Try to add in Manifest, in First Activity:
android:windowSoftInputMode="adjustPan"
I have found the solution here:
Soft Keyboard pushes layout of my activity out of screen
I'm using Actionbar Sherlock. The activity be shown on startup should start in a "search mode" to start searching immediately. For doing so I use the following code:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
//collapse search
MenuItem searchItem = menu.add(Menu.NONE, R.string.inlineSearch, Menu.NONE, getString(R.string.inlineSearch)).setIcon(R.drawable.menu_search);
searchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
searchView = new SearchView(this);
searchItem.setActionView(new SearchView(this));
searchItem.expandActionView();
return true;
}
The SearchView is the View provided by Android / Actionbar Sherlock.
The problem I'm facing with that is that no matter what I do, the item is never expanded on startup.
I tried calling the expandActionView method after startup by using another actionbar item, nothing changed.
I implemented my own View implementing CollapsibleActionView, but the methods onActionViewExpanded() and onActionViewCollapsed() never get called.
But if I click the collapsed button of the SearchView, the view expands as expected.
Does anyone know what I'm doing wrong? Thanks for your help!
After going through the source documentation I finally found the answer by myself.
menuItem.expandActionView() only takes effect if the MenuItem has set the following flag: searchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
Hope that helps somebody out there!
I have a screen layout that is forced to be potrait mode. Because it is very complex I don't have the time right now to invest creating a separate one for landscape mode. It also doesn't make much sense for my type of application.
However, for input fields it's better to provide a landscape mode, because some phones have a hardware keyboard, which automatically aligns the phone in landscape and therefor makes it hard to look at the app that is still in portrait mode.
My solution to this is to put all text input into a dialog and temporarily enable landscape mode (if requested by the user) until the dialog is dismissed again.
This works perfectly. Except of the overlaying search widget (when pressing the search button from my application). I'm looking for two callbacks: one, when the search widget is raised (I cannot listen to the search button, because I sometimes raise it manually via a soft button) and when it is dismissed again (regardless if the search was finally triggered or canceled - it needs to work for both cases).
Any suggestions?
There is an Activity callback for when search is activated. onSearchRequested()
For the dismiss/cancel of the search widget, you can add listeners via the SearchManager:
SearchManager.OnCancelListener
SearchManager.OnDismissListener
Get a reference to your SearchManager with:
context.getSystemService(Context.SEARCH_SERVICE)
see getSystemService()
When using the Search Widget you can use the OnActionExpandListener on the associated action bars menu item. This also works great with the AppCompat Support Library for API versions below 14.
The OnActionExpandListener has two methods:
onMenuItemActionCollapse
onMenuItemActionExpand
See some code example below:
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
MenuItem searchItem = menu.findItem(R.id.action_search);
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener(){
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
KLog.i(TAG, "onMenuItemActionCollapse");
return true;
}
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
KLog.i(TAG, "onMenuItemActionExpand");
return true;
}
});
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
In case you do not use the Support Library use the OnActionExpandListener on the menu item directly.