Query in a SearchView is lost after closing and opening it again - android

I'm working on a Fragment in which I have a SearchView. If I close the SearchView and then reopen it, the query I typed is gone.
The problem is I don't want this behavior. I want the behavior like the SearchView in the Google Play store, where the query is still there if I close and open it.
May it have something to do with the fact that I don't have a searchable.xml?

You need to create an onCloseListener according to the documentation:
Returns true if the listener wants to override the default behavior of
clearing the text field and dismissing it, false otherwise.
See
http://developer.android.com/reference/android/widget/SearchView.OnCloseListener.html#onClose()
I'm uncertain if this works properly after looking at the source code for SearchView which seems to only call the onCloseListener if the query TextView is empty.
Check out SearchView.onCloseClicked() to see what is happening:
private void onCloseClicked() {
CharSequence text = mQueryTextView.getText();
if (TextUtils.isEmpty(text)) {
if (mIconifiedByDefault) {
// If the app doesn't override the close behavior
if (mOnCloseListener == null || !mOnCloseListener.onClose()) {
// hide the keyboard and remove focus
clearFocus();
// collapse the search field
updateViewsVisibility(true);
}
}
} else {
mQueryTextView.setText("");
mQueryTextView.requestFocus();
setImeVisibility(true);
}
}
So if overriding the onCloseListener doesn't work that's why.
Since SearchView is public you can extend the class and fix the logic of onCloseClicked() or override onActionViewExpanded / onActionViewCollapsed to restore and save the query string.
You could also try saving the query in your OnQueryTextListener and then calling setQuery(savedQuery, false) in onOptionsItemSelected.

Related

Android/Kotlin - Fragment not attached to Activity after loosing focus on textfield by pressing navigation menu button

i've a problem in my Android app.
I have a fragment that is generated as a list of editable textview. Every textview has a setOnFocusChangeListener that calls an API on server.
FocusChangedListener works correctly on last textview, and after that my textview remain focused as you can see in the figure below.
Problem
Now i have to change menu (fragment) by clicking on the right menu button.
When button is clicked and the new menu is about to be loaded, my textview loses focus and calls the API, but i don't want my app do that. If I click menu button is because i've to change it and i expect that my old fragment disappear or basically don't execute the FocusChangedListener.
Any ideas?
I made a little trick to solve the problem.
I just added this code inside OnCreate of my DrawerMenu and then i set a Global Variable "drawerOpened" to check every time if my Menu is open or not.
// Initialize the action bar drawer toggle instance
val drawerToggle:ActionBarDrawerToggle = object : ActionBarDrawerToggle(
this,
drawer_layout,
toolbar,
R.string.navigation_drawer_open,
R.string.navigation_drawer_close
){
override fun onDrawerClosed(view:View){
super.onDrawerClosed(view)
GlobalVar.drawerOpened = false
}
override fun onDrawerOpened(drawerView: View){
super.onDrawerOpened(drawerView)
GlobalVar.drawerOpened = true
currentFocus?.clearFocus() //clear focus - any view having focus
}
}
Then in my HomeFragment i did this:
// if GlobalVar.drawerOpened is false then setOnFocus
if (field.validatefield || field.nextpage != 0)
{
textView.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus && !GlobalVar.drawerOpened)
{
if ((field.mandatory && !textView.text.toString().isNullOrEmpty()) || (!field.mandatory)) {
if (field.validatefield) {
validateField(field.fieldcode, textView.text.toString(), field.nextpage)
} else {
_goToNextPageNo = field.nextpage
goToNextPageIfExist()
}
}
}
}
}
This is not a complete solution, but it works fine.
I assume that after a click, the application switches to touch mode and your EditText loses focus.
It happens at the system level.
A focus can have only one view at a time.
https://android-developers.googleblog.com/2008/12/touch-mode.html

Search view shows no record found after entering 3 characters not before

When I search something it updates recycler view accordingly and onQueryTextChange method I am showing layout No items found if it doesn't matches, it is showing No items found when I enter 3rd character and if first two character doesn't match then it shows blank screen.
I want it to show No items found even if first two characters doesn't match.
#Override
public boolean onQueryTextChange(String query) {
newsListAdapter.getFilter().filter(query);
if (newsListAdapter.getItemCount() < 1) {
listRecyclerView.setVisibility(View.GONE);
noRecord.setVisibility(View.VISIBLE);
} else {
listRecyclerView.setVisibility(View.VISIBLE);
noRecord.setVisibility(View.GONE);
}
return false;
}
if your are using search view check this link Android search list while typing,
if you use autocompletetextview you can set the throushouldvalue
For AutoCompleteTextView use this:
AutoCompleteTextView searchAutoCompleteTextView = (AutoCompleteTextView) mSearchView.findViewById(getResources().getIdentifier("search_src_text", "id", getPackageName()));
searchAutoCompleteTextView.setThreshold(1);
The threshold value defines, how much characters you need to type in, until the first proposal can be shown (listeners get called).

How to check if the current activity has a dialog in front?

I am using a third-party library and sometimes it pops up a dialog. Before I finish the current activity, I want to check whether there is a dialog popped up in the current context.
Is there any API for this?
You can check it running over the active fragments of that activity and checking if one of them is DialogFragment, meaning that there's a active dialog on the screen:
public static boolean hasOpenedDialogs(FragmentActivity activity) {
List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment instanceof DialogFragment) {
return true;
}
}
}
return false;
}
I faced a similar problem, and did not want to modify all locations where dialogs were being created and shown. My solution was to look at whether the view I was showing had window focus via the hasWindowFocus() method. This will not work in all situations, but worked in my particular case (this was for an internal recording app used under fairly restricted circumstances).
This solution was not thoroughly tested for robustness but I figured I would post in in case it helped somebody.
This uses reflection and hidden APIs to get the currently active view roots. If an alert dialog shows this will return an additional view root. But careful as even a toast popup will return an additional view root.
I've confirmed compatibility from Android 4.1 to Android 6.0 but of course this may not work in earlier or later Android versions.
I've not checked the behavior for multi-window modes.
#SuppressWarnings("unchecked")
public static List<ViewParent> getViewRoots() {
List<ViewParent> viewRoots = new ArrayList<>();
try {
Object windowManager;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
windowManager = Class.forName("android.view.WindowManagerGlobal")
.getMethod("getInstance").invoke(null);
} else {
Field f = Class.forName("android.view.WindowManagerImpl")
.getDeclaredField("sWindowManager");
f.setAccessible(true);
windowManager = f.get(null);
}
Field rootsField = windowManager.getClass().getDeclaredField("mRoots");
rootsField.setAccessible(true);
Field stoppedField = Class.forName("android.view.ViewRootImpl")
.getDeclaredField("mStopped");
stoppedField.setAccessible(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
List<ViewParent> viewParents = (List<ViewParent>) rootsField.get(windowManager);
// Filter out inactive view roots
for (ViewParent viewParent : viewParents) {
boolean stopped = (boolean) stoppedField.get(viewParent);
if (!stopped) {
viewRoots.add(viewParent);
}
}
} else {
ViewParent[] viewParents = (ViewParent[]) rootsField.get(windowManager);
// Filter out inactive view roots
for (ViewParent viewParent : viewParents) {
boolean stopped = (boolean) stoppedField.get(viewParent);
if (!stopped) {
viewRoots.add(viewParent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return viewRoots;
}
AFAIK - there is no public API for this.
Recommended way is to have a reference to the dialog, and check for isShowing() and call dismiss() if necessary, but since you're using a third party library, this may not be an options for you.
Your best bet is to check the documentation for the library you use. If that doesn't help, you're out of luck.
Hint: Activity switches to 'paused' state if a dialog pops up. You may be able to 'abuse' this behavior ;)
You can override activity method onWindowFocusChanged(boolean hasFocus) and track the state of your activity.
Normally, if some alert dialog is shown above your activity, the activity does not get onPause() and onResume() events. But it loses focus on alert dialog shown and gains it when it dismisses.
For anyone reading this and wondering how to detect a Dialog above fragment or activity, my problem was that inside my base fragment I wanted to detect if I'm displaying a Dialog on top of my fragment. The dialog itself was displayed from my activity and I didn't want to reach it there, so the solution I came up with (Thanks to all answers related to this kind of question) was to get the view (or you can get the view.rootView) of my fragment and check whether any of its children have the focus or not. If none of its children have no focus it means that there is something (hopefully a Dialog) being displayed above my fragment.
// Code inside my base fragment:
val dialogIsDisplayed = (view as ViewGroup).children.any { it.hasWindowFocus() }
Solution in kotlin
Inside Fragment
val hasWindowFocus = activity?.hasWindowFocus()
In Activity
val hasWindowFocus = hasWindowFocus()
If true, there is no Dialog in the foreground
if FALSE , there is a view/dialog in the foreground and has focus.
I am assuming, you are dealing with third party library and you don't have access to dialog object.
You can get the root view from the activity,
Then you can use tree traversal algorithm to see if you can reach any of the child view. You should not reach any of your child view if alert box is displayed.
When alert view is displayed ( check with Ui Automator ), the only element present in UI tree are from DialogBox / DialogActivity. You can use this trick to see if dialog is displayed on the screen. Though it sounds expensive, it could be optimized.
If you are using Kotlin just:
supportFragmentManager.fragments.any { it is DialogFragment }

Android Honeycomb - how to show suggestions for a SearchView when query text is empty?

I've added a SearchView to ActionBar of my application, and setup a SuggestionsProvider using SearchableInfo. Suggestions actually work well, except that no suggestions are shown when search query text is empty. I've found that my suggestion provider is not queried for suggestions even when searchSuggestThreshold is set to 0.
The closest I've come to a solution is to use setOnQueryTextFocusChangeListener and in onFocusChange swap the cursor of SearchView suggestion adapter. This way I see a suggestion drop down of correct height, however no suggestion text is shown.
Here's how I query cursor to show suggestions for empty text.
Cursor emptySuggestions = getContentResolver().query(
Uri.parse("content://my.authority/search_suggest_query/test"),
null, null, null, null);
Is there some way to make suggestions appear correctly?
Of course there's always a possibility to show my own ListPopupWindow when search query is empty, but I'd like to avoid that, unless there's an easy way to reuse layout used in SearchView.
I figured out how to trigger search view to query my suggestion provider for empty search text:
_searchView.setOnQueryTextFocusChangeListener(new OnFocusChangeListener()
{
#Override
public void onFocusChange(View v, boolean hasFocus)
{
if (!hasFocus)
return;
String query = _searchView.getQuery().toString();
_searchView.setQuery(query, false);
}
});
In addition to manually trigger onQueryTextChange() method above, SearchView.SearchAutoComplete threshold need to be set to zero
SearchView.SearchAutoComplete sac = (SearchView.SearchAutoComplete) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
if (sac != null) {
sac.setThreshold(0);
}

Adding features to Clipboard ActionBar

In my app, I want to be able to start my own function when user copy a text to the clipboard.
I'm overriding function onActionModeFinished and as I want to be sure user tapped on Copy I'm getting selected navigation index. Unfortunately if always return -1. Is there something I'm doing wrong here ?
#Override
public void onActionModeFinished(ActionMode mode) {
super.onActionModeFinished(mode);
if (_actionBar != null) {
int index = _actionBar.getSelectedNavigationIndex();
// here index is always -1
}
}
ActionBar can contain tabs and list selector on the left side. So, getSelectedNavigationIndex is used to get the position in this navigation items not in the menu items located in the right side.

Categories

Resources