I'm created WindowPopup, with edittext. And when i focused on it, soft keyboard show and displace this popup above the top bounds, so i can't see what i'm typing. I want to show keyboard without any displace of views, just above them.
I read that i can change softInputMode for it, so i'm created class which extends from EditText, and tried to change inputMode in onFocusListener, but it didn't help.
setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View veiw, boolean has_focus) {
if (has_focus) {
//Try to change input mode to prevent displacing
((Activity) getContext()).getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
} else {
//.. return back previous input mode
}
});
I did so, because i need such behavior only in this popup, but i even try to change action attribute in my manifets file
android:windowSoftInputMode="adjustNothing"
or
android:windowSoftInputMode="stateHidden"
Can i show keyboard without any displacing of view?
P.S. i'm using Android API 15
When PopupWindow creates popup view, it sets to it new WindowManager.LayoutParams with softInputMode, which is overwrites behavior of Window.softInputMode. Here piece of code from PopupWindow
private WindowManager.LayoutParams createPopupLayout(IBinder token) {
WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.gravity = Gravity.LEFT | Gravity.TOP;
p.width = mLastWidth = mWidth;
p.height = mLastHeight = mHeight;
if (mBackground != null) {
p.format = mBackground.getOpacity();
} else {
p.format = PixelFormat.TRANSLUCENT;
}
p.flags = computeFlags(p.flags);
p.type = mWindowLayoutType;
p.token = token;
/*mSoftInputMode is the private field which is by default equals
to WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
*/
p.softInputMode = mSoftInputMode;
}
So to change softInputMode you need just to call public method of PopupWindow
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
And there is no need to remember previous soft input method, because this behavior will be only for this PopupWindow
Related
Working on a lockscreen app I see a very strange behaviour: showing a ListPopupWindow forces the soft keyboard to close. This happens only on a "lockscreen" activity (activity window flags are set to FLAG_SHOW_WHEN_LOCKED|FLAG_DISMISS_KEYGUARD) and only if keyguard pin/password is set.
Here is a code example:
editText.setOnFocusChangeListener((v, hasFocus) -> {
ListPopupWindow popupWindow = new ListPopupWindow(getContext());
popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
popupWindow.setAdapter(new ArrayAdapter(
getContext(), android.R.layout.simple_dropdown_item_1line, Arrays.asList("aaa", "bbb", "ccc")));
popupWindow.setAnchorView(editText);
popupWindow.show();
});
When popup list appears the keyboards immediately disappears.
Note that at the same time PopupWindow works ok:
editText.setOnFocusChangeListener((v, hasFocus) -> {
PopupWindow popupWindow = new PopupWindow(getContext());
popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
popupWindow.setContentView(new TextClock(getContext()));
popupWindow.showAsDropDown(editText);
});
Both the popup window and the keyboard are visible.
Any idea of how to avoid this keyboard disappearance? Finally I need a SearchView (it displays suggestions in a ListPopupWindow) to work properly on a lockscreen.
I found that the keyboard is not closed if the popup window input method mode is
INPUT_METHOD_FROM_FOCUSABLE.
SearchView contains AutoCompleteTextView that changes its suggestions popup input method mode to INPUT_METHOD_NEEDED or INPUT_METHOD_NOT_NEEDED. So the workaround is to keep PopupWindow.mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE.
I solved this by creating a custom PopupWindow with setInputMethodMode() overridden:
public class SearchSuggestionPopupWindow extends PopupWindow {
SearchSuggestionPopupWindow(final Context context, final PopupWindow original) {
super(context);
setElevation(original.getElevation());
setAnimationStyle(original.getAnimationStyle());
setBackgroundDrawable(original.getBackground());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setOverlapAnchor(original.getOverlapAnchor());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setEnterTransition(original.getEnterTransition());
setExitTransition(original.getExitTransition());
}
}
#Override
public void setInputMethodMode(final int mode) {
super.setInputMethodMode(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE);
}
}
Then it's necessary to get AutoCompleteTextView from SearchView:
final AutoCompleteTextView textView = searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
and to substitute the custom popup window:
final Field listPopupField = AutoCompleteTextView.class.getDeclaredField("mPopup");
listPopupField.setAccessible(true);
final ListPopupWindow listPopup = (ListPopupWindow) listPopupField.get(textView);
final Field popupField = ListPopupWindow.class.getDeclaredField("mPopup");
popupField.setAccessible(true);
final PopupWindow popup = (PopupWindow) popupField.get(listPopup);
final PopupWindow customPopup = new SearchSuggestionPopupWindow(textView.getContext(), popup);
popupField.set(listPopup, customPopup);
I want to show a button when the virtual keyboard is open and hide this button if the virtual keyboard visibility is off.But I could not find any listeners to perform this activity.
Anybody knows how to do this?
As found here, you'll need to instantiate the SoftkeyBoard and add a listener.
/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
InputMethodManager im = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);
/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged()
{
#Override
public void onSoftKeyboardHide()
{
// Code here
}
#Override
public void onSoftKeyboardShow()
{
// Code here
}
});
/*
Open or close the soft keyboard programatically
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();
/*
SoftKeyboard can catch keyboard events when an EditText gains focus and keyboard appears
*/
/* Prevent memory leaks:
*/
#Override
public void onDestroy()
{
super.onDestroy();
softKeyboard.unRegisterSoftKeyboardCallback();
}
In his post, you will also find more information about bug fixes and possible problems.
add onGlobalLayoutListener to your parent view of activity/fragment and make your button visibility accordingly
final View parentView= findViewById(R.id.myrootview);
parentView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = root.getRootView().getHeight() - root.getHeight();
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int contentViewTop=
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
if(heightDiff <= contentViewTop){
//Soft KeyBoard Hidden---button visible
}else{
//Soft KeyBoard Shown---button hide
}
}
});
There is no direct event for keyboard open and close. but you can create observer on your full layout and then display buttons or whatever you want to do.
For Observer code check this - Hide part of activity_main.xml if keyboard is open (Android)
I got a problem when I tried to close the notification bar from my application.
here is where i try to close it
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
Log.i("FocusChanged", "Focus changed");
if(!hasFocus)
{
Log.e("Focus debug", "Lost focus !");
// Close every kind of system dialog
Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
getApplicationContext().sendBroadcast(closeDialog);
Log.i("FocusChanged", "Focus changed - iam supposed to close this dialogs");
}
super.onWindowFocusChanged(hasFocus);
}
But it don't work.
Unless if I add a breakpoint to the function.
Everytime that i open the system notification i got the log that say "Focus changed" so my app run the code, but hasFocus not set to true, or as I said only if i ran the debugger and set breakpoint in the function.
We could not prevent the status appearing in full screen mode in kitkat devices, so made a hack which still suits the requirement ie block the status bar from expanding.
For that to work, the app was not made full screen. We put a overlay over status bar and consumed all input events. It prevented the status from expanding.
note:
customViewGroup is custom class which extends any layout(frame,relative layout etc) and consumes touch event.
to consume touch event override the onInterceptTouchEvent method of the view group and return true
You also need to set the android permission
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
You need an extended ViewGroup class to handle Touch Event
public class customViewGroup extends ViewGroup {
public customViewGroup(Context context) {
super(context);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.v("customViewGroup", "**********Intercepted");
return true;
}
}
in the activity where you want to disable the notification bar you need to add this function (don't forget to add field variable CustomViewGroup blockingView)
private void disableNotificationBar(){
WindowManager manager = ((WindowManager) getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.height = (int) (50 * getResources()
.getDisplayMetrics().scaledDensity);
localLayoutParams.format = PixelFormat.TRANSPARENT;
blockingView = new CustomViewGroup(this);
manager.addView(blockingView, localLayoutParams);
}
and then in you onCreate you need to call
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
....
disableNotificationBar();
}
You need to remove it on the onDestroy() to be able to close your application
#Override
protected void onDestroy() {
super.onDestroy();
if (blockingView!=null) {
WindowManager manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
manager.removeView(blockingView);
}
}
You also need to adjust the overlay size, according to previous answer 40 is enought, but you don't want to prevent user from doing action on your own app, if overlay is to height it will intercept event on your Activity too.
Also in android version > to 5.1 you need an extra permission from the user, add this on your onCreate function insted of the function call
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "Please give my app this permission!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
} else {
disableNotificationBar();
}
}
else {
disableNotificationBar();
}
I want to show a PopupWindow above virtual keyboard like Google Keep did when creating a reminder:
I believe, what you are looking for is combination of:
popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
It's basically how native SearchView works:
https://android.googlesource.com/platform/frameworks/support/+/android-6.0.1_r31/v7/appcompat/src/android/support/v7/widget/SearchView.java#1695
which basically calls AutocompleteTextView.ensureImeVisible()
public void ensureImeVisible(boolean visible) {
mPopup.setInputMethodMode(visible
? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
if (mPopup.isDropDownAlwaysVisible() || (mFilter != null && enoughToFilter())) {
showDropDown();
}
}
This would resize Popup window to be shown exactly between Anchor and Keyboard.
use the following code. Change the location of the popup window in the method "showAtLocation".
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
PopupWindow pw = new PopupWindow(inflater.inflate(R.layout.popup_layout, null, false), ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT, true);
findViewById(R.id.activity_layout).post(new Runnable() {
#Override
public void run() {
pw.showAtLocation(findViewById(R.id.activity_layout), Gravity.CENTER, 0, 0); //set location here
}
});
To make a PopupWindow display above (on top of, or in front of) the on-screen keyboard, and make it dismiss when clicked outside, use this:
popupWindow = new PopupWindow(context);
popupWindow.setFocusable(true);
popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
Sample code: https://github.com/lorensiuswlt/NewQuickAction3D
To make a ListPopupWindow display in front of the keyboard, and make it automatically close when clicked outside, use this:
listPopupWindow = new ListPopupWindow(context);
listPopupWindow.setModal(true);
listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
Sample code: Custom PopupMenu (layout)
Good day.
I have an android application and I have a custom listView. Upon clicking the customListView I change the layout of the selected row (I change the last 2 column to editTexts), and I show a dialogFragment (which has an editText). Here is my code:
The onItemClickListener:
resultListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
//first I move the selected row to the top of the listView
resultListView.smoothScrollToPositionFromTop(pos, 0);
//then I disable selection of the listView to limit user action
resultListView.setScrollContainer(false);
resultListView.setClickable(false);
resultListView.setEnabled(false);
//I try to make the row still enabled and clickable
view.setClickable(true);
view.setEnabled(true);
//TODO - set clickstate = 0 is base/default state
for(SearchResultListViewItem row : results){
row.setClickState(1); //state 1 = dim
}
SearchResultListViewItem rowItem = results.get(pos);
rowItem.setClickState(2); //state 2 - clicked state
//in my CustomAdapter, I handle the clickState in the getView method
//basically, the change happens when click state is 2, the last 2 columns become editText
adapter.updateResults(results);
//I removed a lot of unrelated code in here
ItemDialog itemDialog = new ItemDialog();
//I set various variables here
itemDialog.show(getFragmentManager(), "");
};
});
This is the onCreateDialog Method. As you can see below, I set various properties such as setCancelable, setCancelOnTouchOutside, and window Flags for Modal.
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog dialog = new Dialog(getActivity());
//set width height here
int width = getResources().getDisplayMetrics().widthPixels * 4/5;
int height = getResources().getDisplayMetrics().heightPixels;
dialog.getWindow().setLayout(width, height);
//set other features here
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
//make sure that the dialog persists through certain events
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
LayoutInflater layoutInflater = (LayoutInflater) getActivity()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout2 = layoutInflater.inflate(R.layout.fragment_item_dialog, null);
dialog.setContentView(layout2);
setStyle(DialogFragment.STYLE_NORMAL, R.style.MyDialog);
//set window properties such as modal, dim, and position
Window window = dialog.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.setGravity(Gravity.TOP|Gravity.LEFT);
WindowManager.LayoutParams params = window.getAttributes();
params.x = 50;
params.y = y;
params.copyFrom(window.getAttributes());
window.setAttributes(params);
//edited out a lot of extra code
dialog.show();
return dialog;
}
And if necessary, here is the code in the getView method in my Custom BaseAdapter that handles when I change the clickState to 2:
//the if condition checks if the clickstate is < 2, if < 2, do the default and dim state
//click state of 2 means that I use the other row layout which has 2 editTexts
else{
convertView = layoutInflater.inflate(R.layout.search_result_inflate, null);
holder = new ViewHolder();
holder.itemView = (TextView) convertView.findViewById(R.id.dialogItemName);
holder.itemView.setText(listData.get(position).getItemName());
holder.itemView.setBackgroundColor(Color.parseColor("#66B2FF"));
holder.priceView = (TextView) convertView.findViewById(R.id.price);
holder.priceView.setText(listData.get(position).getPrice());
holder.priceView.setBackgroundColor(Color.parseColor("#66B2FF"));
holder.discountView = (TextView) convertView.findViewById(R.id.discount);
holder.discountView.setText(listData.get(position).getDiscount());
holder.discountView.setBackgroundColor(Color.parseColor("#66B2FF"));
holder.qtyIn = (EditText) convertView.findViewById(R.id.qtyInput);
holder.qtyIn.setText(listData.get(position).getQtyInput());
holder.qtyIn.setFocusable(true);
holder.qtyIn.setClickable(true);
holder.discIn = (EditText) convertView.findViewById(R.id.discInput);
holder.discIn.setText(listData.get(position).getDiscInput());
holder.discIn.setFocusable(true);
holder.discIn.setClickable(true);
return convertView;
}
As you can see, I tried to enable the necessary rows as much as possible. However, it seems that the DialogFragment is blocking it.
I also tried to comment out the
resultListView.setScrollContainer(false);
resultListView.setClickable(false);
resultListView.setEnabled(false);
block of code in my OnItemClickListener but to no avail.
The funny/interesting thing about this is that I am able to select the editTexts in the new row, I can see the focus shift, however, the soft keyboard does not appear at all. The soft keyboard only appears when I click the editText inside the DialogFragment. Even switching focus from the EditText in the dialog Fragment to the one in the list view doesn't enable me to put input inside the EditText in the rows.
If it's conflict between the EditText in the background/base activity (the one in the rows) and the EditText in the DialogFragment, then I hope there's a way to properly switch focus as I cannot resort to removing either of them.
Does anyone have an idea on how to work around this? Any help is very much appreciated, thank you.
Edit: I removed the EditText in the DialogFragment and it still doesn't work.