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...
Related
I have been trying to find the source of ActionMode memory leak for days now without luck. I have an activity with several fragments and when I leave the fragment having ActionMode (while auto cancelling it), LeakCanary detects a memory leak.
I have nulled both ActionMode and ActionMode.Callback on destroy() and even tried doing it on onDestroyActionMode().
Here is my LeakCanary screenshot:
https://i.imgur.com/RUbdqj3.png
I hope someone points me in the right direction.
P.S. I have suspected it has something to do with ActionMode.Callback. Though, I could not find any methods for the CallBack that destroys it. I start the ActionMode using startSupportActionMode(mActionModeCallback). I have tried to find a method to remove the mActionModeCallback from that, too, but no methods.
Here is my full ActionMode code:
private ActionMode mActionMode;
private ActionMode.Callback mActionModeCallback;
public void startCAB()
{
if (mActionMode == null)
mActionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(mActionModeCallback);
}
private void buildActionModeCallBack()
{
mActionModeCallback = new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.menu_cab, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
... Some Code ...
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
mActionModeCallback = null; // Tried with and without this.
}
};
}
public void finishActionMode()
{
mActionMode.finish();
}
#Override
public void onDestroy()
{
super.onDestroy();
mActionMode = null;
mActionModeCallback = null;
}
Parent Activity containing fragments:
#Override
public void onTabUnselected(TabLayout.Tab tab)
{
clearCAB();
}
private void clearCAB()
{
int index = mPagerAdapter.getCurrentFragmentIndex();
FragmentOne fragmentOne = (FragmentOne) mPagerAdapter.instantiateItem(mViewPager, index);
fragmentOne.finishActionMode();
}
According to my experience, if your ActionMode.Callback object use the Anonymous inner class it may cause your fragment memory leak.
Maybe you can create a new class and implements ActionMode.Callback then use it to put in startSupportActionMode() parameter:
public class YourFragment extends skip implements skip, ActionMode.Callback {
private ActionMode mActionMode;
public void startCAB()
{
if (mActionMode == null)
mActionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(new SafeActionModeCallback(this));
}
public void finishActionMode()
{
mActionMode.finish();
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.menu_cab, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
// ... Some Code ...
}
}
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
}
SafeActionModeCallback:
public class SafeActionModeCallback implements ActionMode.Callback {
// you can also use the WeakReference
private ActionMode.Callback callback;
public SafeActionModeCallback(ActionMode.Callback callback) {
this.callback = callback;
}
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return callback.onCreateActionMode(mode, menu);
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return callback.onPrepareActionMode(mode, menu);
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return callback.onActionItemClicked(mode, item);
}
#Override
public void onDestroyActionMode(ActionMode mode) {
callback.onDestroyActionMode(mode);
callback = null;
}
}
It seems the ActionMode in the activity has a reference to the fragment's layout which is causing the memory leak and preventing the fragment from getting GC'ed. I couldn't find a way to remove the reference.
In my use case, I'm using a ListView inside the fragment that was activating the activity's ActionMode (via listener.setMultiChoiceModeListener).
My hacky solution: In the fragment's onDestroyView, remove the listView (or whichever view activated the ActionMode) from the layout and remove all listeners for the list view. I made a kotlin extension method for it:
fun ListView.removeViewAndClearListeners() {
setMultiChoiceModeListener(null)
setOnScrollListener(null)
onItemClickListener = null
(parent as? ViewGroup)?.removeView(this)
}
After doing this, the leak is gone.
I am still wondering why you are relying on ActionMode.Callback. I had an application where I was supposed to create a Custom Menu on long press and I wasted almost 2 months on this issue :
ActionModeCallback does not work
I am not sure If you are aware of this or not, The ActionMode Callback barely works on all devices. After a lot of research, I came to know that devices who are focusing too much on battery consumption and optimization will not let your background services and some callbacks work as expected.
Try testing your code on MI or Oppo/Vivo devices. It will jump directly to onDestroyActionMode instead of calling onActionItemClicked
I'm developing a Javascript based web app and thought that I could use Android's WebView ability to make it like an actual app, so I jumped right into Android Studio and made a simple WebView app without any Java knowledge, but the problem is, between page transitions whenever I want to return back and hit the back button, the app closes itself. I found some solutions but I have no idea how to implement them correctly, so can you help me please? Here are my WebView's FullscreenActivity.java codes.
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
WebView view = (WebView) this.findViewById(R.id.webView);
view.getSettings().setJavaScriptEnabled(true);
view.setWebViewClient(new ZeroBrowser());
view.loadUrl("http://www.google.com");
}
private class ZeroBrowserOverride extends WebViewClient implements ZeroBrowserOverride {
#Override
public boolean ShouldOverrideUrlLoading(WebView view, String Url){
view.loadUrl(Url);
return true;
}
}
Thank you so much!
You can try with this .
WebView view ; //Global
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
view = (WebView) this.findViewById(R.id.webView);
view.getSettings().setJavaScriptEnabled(true);
view.setWebViewClient(new ZeroBrowser());
view.loadUrl("http://www.google.com");
}
Then
#Override
public void onBackPressed() {
if (view.canGoBack()) {
view.goBack();
} else {
super.onBackPressed();
}
}
How to go back to previous page if back button is pressed in WebView?
http://developer.android.com/intl/es/guide/webapps/webview.html
You can check the clients history to go back within webview. Implement something like this:
#Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
super.onBackPressed();
}
}
In your WebView class (or in parent view) override the listener:
#Override
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
// Check if the key event was the Back button and if there's history
if ((keyCode == KeyEvent.KEYCODE_BACK) && this.canGoBack()) {
this.goBack();
return true;
}
// If it wasn't the Back key or there's no web page history, bubble up to the default
// system behavior (probably exit the activity)
return super.onKeyDown(keyCode, event);
}
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.
I want to detect the back button.
However my current implementation does not even detect the back button.
CODE:
#Override
public boolean onTouch(MotionEvent e, int scaledX, int scaledY) {
//... OTHER CODE ...
else if(e.getAction() == MotionEvent.BUTTON_BACK){
System.out.println("BACK BUTTON PRESSED");
setCurrentState(new MenuState());
}
return true;
}
}
You can use onBackPressed() inside your Activity:
#Override
public void onBackPressed() {
//Do something
}
It's written in the documentation:
public static final int BUTTON_BACK
Button constant: Back button pressed (mouse back button). The system may send a KEYCODE_BACK key press to the application when this button is pressed.
You need to override onKeyUp function from the Activity (not from the View):
public boolean onKeyUp(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK)
{
//todo
}
}
Use Intent inside onBackPressed()like this:
#Override
public void onBackPressed()
{
Intent BackIntent = new Intent(getApplicationContext(), NewActivity.class);
startActivityForResult(BackIntent);
finish();
}
I change android soft keyboard to show search icon rather than Enter icon. My question is: how i can detect that user click on that search button?
In the edittext you might have used your input method options to search.
<EditText
android:imeOptions="actionSearch"
android:inputType="text"/>
Now use the Editor listener on the EditText to handle the search event as shown below:
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
// Your piece of code on keyboard search click
return true;
}
return false;
}
});
You can use OnQuerySubmit listener to get keyboard Search button click event.
Here's how,
searchView.setOnQueryTextListener(queryTextListener);
SearchView.OnQueryTextListener queryTextListener
= new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
showLog("searchClickListener onQueryTextSubmit");
getPhotos(searchView.getQuery().toString());
return true;
}
#Override
public boolean onQueryTextChange(String s) {
return false;
}
};