Recently while developing an app, I faced an issue. I have searched a lot on google but couldn't find any solution. In the end I came across this Android issue tracker
To explain my issue, I have made a sample App.
Basic Working of my Sample App
I have a screen, which has an EditText, a Button and a RelativeLayout.
Width and Height of RelativeLayout is 0px. It is just a view to move focus away from EditText.
When App is launched focus is on RelativeLayout, not on EditText(so that there is not blinking cursor in it.)
When a user clicks on Button I just move focus to RelativeLayout using requestFocus() call on RelativeLayout.
When user taps on EditText, keyboard comes up. I can enter text in that.
What I want to achieve
If I change orientation of phone when keyboard is visible then after orienation changes, keyboard should stay.
If keyboard is visible and some other activity comes on top of it for e.g. alarm, facebook chat heads, opening something from notification area, locking unlocking device, etc.. then on returning back to sample app keyboard should be visible.
How I am achieving this
In onSaveInstanceState(), I check if focus is on EditText then put a boolean variable in Bundle.
In onStop(), I am setting a one boolean flag wasEditing = true.
In onRestoreInstanceState(), I checked if Bundle has flag value set in onSaveInstanceState(). If yes then I am make wasEditing = true.
In onResume(), I check this wasEditing and if it is true, I request focus for EditText.
After that I call imm.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT,resultRec)
Where I am getting problem
Sometimes after executing this call, Keyboard is not visible in few cases, like during orientation change.
When I put logs I have found this function is returning false
But if I make this showSoftInput() call with some delay of 100ms using mEditText.postDelayed() in onResume() everything works fine.
Question
In what cases this function returns false and why delay is working?
Note
Although I have solved my problem using delay, but I still want to know why it is behaving like that.
This is a problem I ran into today as well. Of my 8 android devices only 1 has the problem and it's running Android 4.0.4.
The problem was fixed by adding
mEditText.requestFocus();
mEditText.requestFocusFromTouch();
before calling
mEditText.showSoftInput(...)
You'll see the resultcode from showSoftInput is now true. I noticed that after the mEditText.requestFocus() the isFocused() was still false. Probably a bug in Android 4.0 and perhaps 4.1.
Please call showKeyboard after your mEditText is attached to window.
Please make sure the time you make the call.
PROBLEM:
I faced with this keyboard not showing up problem. I found the following solution inspired by this answer but not their solution! In short the reason for this mess is that the request focus and the IMM provided service can only run on a view that is created and active. When you do all these on the creation phase onCreate(Bundle savedInstance).. or onCreateView(LayoutInflater inflater... and the view is still in initializing state, you won't get an active view to act on! I have seen many solutions using delays and checks to wait for that view to get active then do the show keyboard but here is my solution based on the android frame work design:
SOLUTION:
in your activity or fragment override the following make sure your view has the access (define it in the top of the activity/fragment):
#Override
public void onStart() {
yourView.requestFocus();
showSoftKeyboard(yourView);
super.onStart();
}
public void showSoftKeyboard(View view) {
if(view.requestFocus()){
InputMethodManager imm = (InputMethodManager)
mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}
Related
I have 2 fragments in a ViewPager and I want the window to adjust differently to the soft keyboard on the 2nd fragment. Here's what I'm trying:
#Override
public void onPageSelected(int position) {
if(position == 1){ // desired for 2nd fragment
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
} else { // desired for 1st fragment
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED);
}
}
Observed behavior:
Enter 1st fragment and default softInputMode is working, as expected.
Swipe to 2nd fragment and breakpoint shows that the softInputMode should be set to ADJUST_NOTHING, but everything still behaves like the default.
Swipe back to 1st fragment and it behaves with ADJUST_NOTHING.
Swiping back and forth now reveals both fragments to behave like ADJUST_NOTHING, even though breakpoints show these calls are being made.
To top it off, I can switch fragments all I want and the input mode will behave as default until I pull up the soft keyboard. Then it starts its migration toward ADJUST_NOTHING. I'm quite baffled.
I don't have any relevant flags in the manifest, although in my Activity onCreate() I do set the input mode to SOFT_INPUT_STATE_ALWAYS_HIDDEN.
The solution I found works well enough, although I hope there's a smoother way out there somewhere. I did two things.
First, I stopped using SOFT_INPUT_ADJUST_UNSPECIFIEDas my "default" input state because the WindowManager seems to treat changes to and from this state a bit differently. 'SOFT_INPUT_ADJUST_RESIZE' gives the behavior I desire, so I changed my method to
#Override
public void onPageSelected(int position) {
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
);
hide_keyboard(activity);
if(position == 1){ // desired for 2nd fragment
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
} else { // desired for 1st fragment
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
}
This fixed the "getting stuck in a certain input mode" issue. Still, I wasn't getting the input mode I wanted on my 2nd fragment, so I added a callto set the input mode right before launching the DialogFragments with text input fields from my 2nd fragment.
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING); // fragment behind doesn't get readjusted from keyboard.
editCatFragment.show(getActivity().getSupportFragmentManager(), "editCatFrag");
This actually doesn't guarantee the mode I want the first time the fragment is launched, but makes it work the 2nd time which, due to how my app is designed, actually works out okay.
Hopefully this helps someone and hopefully there's a better way of solving this problem. Thanks!
I am trying to work out how to show the "up" arrow in Xamarin.Forms without a pushing a page onto the stack. I.E. I just want to perform an action when the back button is pressed. I am completely stuck on this so any help would be appreciated.
I have tried creating a custom renderer which handles a view property called DisplayHomeAsBack. Which in the renderer calls the following:
FormsAppCompatActivity context = ((FormsAppCompatActivity)Forms.Context);
Android.Support.V7.App.ActionBar actionBar = context.SupportActionBar;
if (actionBar != null)
{
actionBar.SetDisplayHomeAsUpEnabled(element.DisplayHomeAsBack);
}
Unfortunately it seems this does absolutely nothing, even though all online tutorials and stackoverflow question for android suggest this method.
The plan is that I can then use the "OnBackButtonPressed" override in MasterDetailPage, which should allow me to perform this action. Unfortunately displaying the back button has been the larger hurdle so far!
Any idea of a better way to do this or how I can get the current mechanism to work?
EDIT
I have created a project and uploaded it to this question on the Xamarin support forums, if it helps.
http://forums.xamarin.com/discussion/comment/186330#Comment_186330
Sorry to keep you waiting so long!
Warning that I did not actually run this code and changed it from my own so I would be surprised if it worked perfectly without some changes.
So below should add a back button where there was not one before (so like when there is not really a page to go back to) and then we will add a custom action to perform when it gets pressed.
I would suggest you push a new page onto the stack without using animation so it is transparent to the user and also makes all of this much simpler, but if you absolutely do not want to do that, the below method should work.
MainActivity:
//Use this to subscribe to the event which will create the back button
public override bool OnCreateOptionsMenu(IMenu menu) {
if(menu != null && App.AppMasterPage != null) { //You will need this to make sure you are on your MasterDetailPage, just store a global reference to it in the App class or where ever
Xamarin.Forms.MessagingCenter.Unsubscribe<string>(this, "CreateBackButton");
Xamarin.Forms.MessagingCenter.Subscribe<string>(this, "CreateBackButton", stringWeWillNotUse => { //Use this to subscribe to the event that creates the back button, then when you want the back button to show you just run Xamarin.Forms.MessagingCenter.Send<string>(this, "CreateBackButton")
ActionBar.DisplayOptions = ActionBarDisplayOptions.ShowTitle | ActionBarDisplayOptions.ShowHome | ActionBarDisplayOptions.UseLogo | ActionBarDisplayOptions.HomeAsUp; //You may need to play with these options to get it working but the important one is 'HomeAsUp' which should add the back button
});
} else {
Xamarin.Forms.MessagingCenter.Unsubscribe<string>(this, "CreateBackButton");
}
return base.OnCreateOptionsMenu(menu);
}
Now the next step is do do a custom action when it is pressed. I think you can either override OnBackPressed() or OnOptionsItemSelected() in MainActivity or maybe you can override the MasterDetailPage method. I am not sure.
Which ever one works for you, inside of that override, I would simply check to see if you are on your App.AppMasterPage like we did above, and if so, send a MessagingCenter message which your App.AppMasterPage has already subscribed to in order for it to handle the custom action.
If you get stuck let me know!
I know it sounds like a bit of a hack, but the best "solution" I have found so far is to add a page behind the current page (behind the root) so it is not visible. Then when the user presses the back button, handle it by removing that page.
I have a Layout,
that has a form .. about five items down in the form is a spinner, and everytime my activity loads Talkback seems to read this spinners content description even though the focus and flashing cursor are on the first edittext view.
I can't seem to stop it.. any ideas?
Accessibility Focus and Input Focus are tracked separately in Android. I would need more details about your specific scenario to provide a 100% answer. However, my guess is that your EditText field is requesting input focus when the view loads, so the cursor is being moved there, and potentially the keyboard is popped up (depending on Android version). However, due to tab ordering, or some other mechanism, the spinner is the first item on the page to get Accessibility Focus.
There are two solutions to this, depending on the behavior that you want.
Solution 1 (recommended): Don't have your EditText box request input focus. Allow the user to force input focus into the box on interaction. For capable users, this is just a simple tap into the EditText box.
Solution 2 (less accessibile, but perhaps more usable): Shift accessibility focus to the EditText in your onResume method (or onCreate, or wherever you deem most appropriate). A method like this would be best:
public void resetFocus() {
editTextControl.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
editTextControl.requestFocus();
}
I would recommend calling the above method in onResume, so that every time this particular view loads input and accessibility focus will be in a predictable place. Otherwise, you've created additional accessibility issues.
I solved it simply like this:
/*
This custom Spinner has the purpose of making sure that there are no accessibility events fired after
a selection is made
*/
public class SilentSpinner<T> extends SimpleSpinner<T> {
//region constructors
...
//endregion
//region public methods
#Override
public void sendAccessibilityEvent(int eventType) {
if (eventType != AccessibilityEvent.TYPE_VIEW_SELECTED) {
super.sendAccessibilityEvent(eventType);
}
}
//endregion
}
I have a simple activity that shows an animation with ObjectAnimator. The animation is created and started in onCreate method of the activity, it is a very simple animation:
cloudAnim = ObjectAnimator.ofFloat(cloud1ImageView, "x", sw);
cloudAnim.setDuration(35000);
cloudAnim.setRepeatCount(ValueAnimator.INFINITE);
cloudAnim.setRepeatMode(ValueAnimator.RESTART);
cloudAnim.setInterpolator(null);
cloudAnim.start();
it simply displays a cloud on the left of the screen and moves from the left to the right.
The problem is that in my nexus 5 (android 4.4 lastet version) the cloud is doing a frame jump when the activity starts.
This jump is only visible in my nexus 5, because i'm testing the app also in a huawei ascend y300 devide with android 4.1 and the jump is not visible, the movement is very smooth.
What is wrong with ObjectAnimator and Android 4.4?
Thanks
Starting animations in onCreate is not a good idea. When user will finally be able to see this animation (after activity being inflated and displayed on the screen with animation etc.) the animation is not in it's beginning but a bit after it, so user will miss the very beginning of the animation or perhaps will see some frame drops then as well. The final result really depends on the device, android version, standard window animations styles etc.
If you want to launch an animation right after creating an activity make use of onWindowFocusChanged method:
http://developer.android.com/reference/android/app/Activity.html#onWindowFocusChanged(boolean)
Called when the current Window of the activity gains or loses focus.
This is the best indicator of whether this activity is visible to the
user.
In addition you need to do some checks:
1. Window has focus (hasFocus==true) - it's visible to the user
2. Create boolean variable indicating that animation was already started, so it will be launched only once
private boolean cloudAnimStarted;
#Override
public void onWindowFocusChanged (boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus && !cloudAnimStarted) {
cloudAnimStarted = true;
cloudAnim.start();
}
}
So creating a cloudAnim object is fine in onCreate, but launching it should be done in onWindowFocusChanged method instead.
Hi I wrapped edittext control onto a control that is being displayed on the screen at users request. It overlays the whole screen until user presses 'done' button on the keyboard.
I am not able to explicitly show the control on the screen. only when user taps into control only then its shown. Am I missing something?
I even try this and it does not brin it up when I launch the overlay that Edit Text exists on:
customCOntrol.showKeyboard();
public void showKeyboard()
{
InputMethodManager imm = (InputMethodManager)_context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(this._textView.getWindowToken(), InputMethodManager.SHOW_IMPLICIT);
}
here is the settig I have on the screen itself in the config file android:windowSoftInputMode="stateHidden|adjustPan"
Thank you in advance
In your showKeyboard function you are calling:
imm.hideSoftInputFromWindow(this._textView.getWindowToken(), InputMethodManager.SHOW_IMPLICIT);
This will hide the softInput keyboard from the window!
Do you want to show the keyboard? If yes then would you use:
imm.showSoftInput(view, flags, resultReceiver);
EDIT: I think you can also toggle the keyboard from the InputMethodManager, try:
imm.toggleSoftInput(0, 0);
#dropsOfJupiter
You can do: editText.requestFocus() as you launch the Activity or Fragment containing your EditText reference. This will give the focus to the EditText and will bring uo the SoftKeyboard.
I hope this helps.
PROBLEM:
I faced with this keyboard not showing up problem. I wrote the following solution inspired by this answer but not their solution! It works fine. In short the reason for this mess is that the request focus and the IMM provided service can only run on a view that is created and active. When you do all these on the creation phase onCreate(Bundle savedInstance).. or onCreateView(LayoutInflater inflater... and the view is still in initializing state, you won't get an active view to act on! I have seen many solutions using delays and checks to wait for that view to get active then do the show keyboard but here is my solution based on the android frame work design:
SOLUTION:
in your activity or fragment override the following make sure your view has the access (define it in the top of the activity/fragment):
#Override
public void onStart() {
yourView.requestFocus();
showSoftKeyboard(yourView);
super.onStart();
}
public void showSoftKeyboard(View view) {
if(view.requestFocus()){
InputMethodManager imm = (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(view,InputMethodManager.SHOW_IMPLICIT);
}
}