Prevent EditText from focussing after rotation - android

I've implemented a simple EditText that provides a Done button to hide the keyboard and upon rotating to landscape it does not present a full screen dialog for the EditText. But there's a problem. When I tap Done to dismiss the keyboard then I rotate the device to landscape, the keyboard appears. If I dismiss it again then rotate to portrait the keyboard appears yet again.
How can I preserve the keyboard visibility state upon rotation - if it was hidden before rotation then don't present it after rotation, but if it was visible before rotation then present it after rotation?
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/editText1"
android:inputType="text"
android:imeOptions="flagNoFullscreen|actionDone" />
I tried setting android:descendantFocusability="beforeDescendants" and android:focusableInTouchMode="true" in the parent container (RelativeLayout), but that didn't affect this behavior.

There are two options..
In your onCreate() method try these options
Option 1.
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
Option 2
edittext.clearFocus();
This option sets the focus back to the first focusable view in the activity.
EDIT:
Option 2 will not work if your edittext it self is the first focusable view in your activity as clearFocus sets the focus back to first focusable view in your activity.
Usage of Option 1:
public class MyActivity extends Activity {
EditText editText1;
boolean isKeyBoardOpen=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
editText1 = (EditText) findViewById(R.id.editText1);
if(savedInstanceState!=null &&(savedInstanceState.getBoolean("isKeyBoardOpen",false)))
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
else this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
final View activityRootView = findViewById(R.id.root_layout);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
isKeyBoardOpen=true;
}else isKeyBoardOpen=false;
}
});
}
protected void onSaveInstanceState(final Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putBoolean("isKeyBoardOpen",isKeyBoardOpen);
}
}

there are many ways to do but i give you 2
1 overide onConfigurationChange method in there at the time of configuration change see if keyboard is open or not and you can act according by saving the value of it an boolean and after onConfigurationChange is changes use that boolean value to show or hide keyboard.
2 Another way if you have not implemented onConfigurationChange method is saving status in onSaveInstanceState and retrieving it in onRestoreInstanceState like below.
#Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
//get the status of keyboard
boolean status = isKeyboardVisible(){this is your method or your logic};
outState.putBoolean(key, status)
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onRestoreInstanceState(savedInstanceState);
boolean status = savedInstanceState.getBoolean(key);
//based on status show or hide keyboard.
}

Related

How to validate virtual keyboard visibility?

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)

Unable to maintain the state of a custom dialog button when screen orientation changes

I have a customized dialog which i have created using DialogFragment. it uses a view which got one button and an edittext.
in my edittext, i have implemented a textwatcher where if there are values in the edittext, the button becomes visible else it remains invisible.
public class ShowDialog extends DialogFragment {
private Button btnShowBalance;
private EditText balanceInquiryPinInput;
public ShowDialog(){
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View myView = inflater.inflate(R.layout.fragment_balance_inquiry,null);
btnShowBalance= (Button) myView.findViewById(R.id.btnShowBalance);
balanceInquiryPinInput = (EditText) myView.findViewById(R.id.et_balance_inquiry_pin);
balanceInquiryPinInput.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(s.length() > 0){
btnShowBalance.setVisibility(View.VISIBLE);
}else{
btnShowBalance.setVisibility(View.INVISIBLE);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
btnShowBalance.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
showBalance(balanceInquiryPinInput,nameview,ShowDialog.this);
}
});
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(myView);
// Create the AlertDialog object and return it
return builder.create();
}
}
Problem
When i change my screen orientation to (let say) landscape, if i had typed anything to my edittext before changing my orientation, whatever i had typed is still visible in landscape, my button is visible But it becomes unclickable. i cannot click it. i have to remove the dialog by pressing somewhere outside the dialog window or back button and create it again.
How can i make my button remain clickable even when one change the screen orientation?
Note: it is clickable before changing orientation.
EDIT
My dialog is activated by a button which is in a fragment and not activity.
this question is not a duplicate of This one because the latter is an implentation on an activity and it's implementation is unreliable to my view and it's state in question(button)
EDIT xml for my custom dialog layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#044848"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:background="#drawable/check_balance"
>
<EditText
android:layout_width="200dp"
android:layout_height="wrap_content"
android:id="#+id/et_balance_inquiry_pin"
android:layout_marginTop="5dp"
android:gravity="center"
android:background="#drawable/enter_pin_textview"
android:inputType="numberPassword"/>
<Button
android:layout_width="150dp"
android:layout_height="wrap_content"
android:text="OK"
android:id="#+id/btnShowBalance"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:textColor="#android:color/white"
android:textSize="20sp"
android:background="#drawable/show_balance_ok_button"
android:visibility="invisible" />
</LinearLayout>
The problem of the button being disabled may be due to the dialog no longer being functional due to the calling view being destroyed in the orientation change. So it may still be visible, but is really an artifact of the previous view before it was destroyed.
You need to use methods that saves the state of your dialog fragment instance when it is created. for example you can call
setRetainInstanceState(true);
in your onCreateDialog method to retain the state of your DialogFragment instance when screen orientation changes.
You may need to overide onDestroyView() to prevent dialog from been destroyed when screen orientation changes. like this
#Override
public void onDestroyView() {
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
Information about this and other fragment methods can been found here.
If you are adding click listeners in Dialog to your buttons, you need to add onclick for your new buttons objects create after onConfigurationChanged.
setting on click listener in xml is good practice for removing problem like this:
so set on click in xml like this :
<Button
android:id="#+id/addnote"
android:onClick="closeDialog"
/>
now in activity call onclick where you want
public void closeDialog(View view) {
dialogObject.dismiss();
}
after using on click in xml no need to call setOnClickListener

PopupWindow shown at wrong place in onGlobalLayoutListener

I have a View say anchor and a PopupWindow pop. pop is shown with showAsDropDown when I click a button on anchor.
What I want is to show pop automatically at the first time anchor comes to screen. So I override onAttachedToWindow and add a onGlobalLayoutListener and show pop in it. see below:
private boolean mFirstRun = true;
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if(mFirstRun) {
mFirstRun = false;
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
showPopup();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}
}
The result is, only the first time (see above) pop will stay right above anchor. I have confirmed the width and height of pop is correct. when the button click triggers, pop is shown at the proper place.
After spending some time I found the problem is in onGlobalLayoutListener anchor.getLocationOnScreen has a larger value than the final value so there's no enough space for pop below it.
How can I fix this? and why the screen location is incorrect in that listener?

CheckBoxes in ListView disappear when the screen rotates

What my application first does is it loads ListView whose items have invisible CheckBoxes by setting its visibility View.Gone. When the user tabs a menu button then it will turn on and off the CheckBox visibility and some other layouts. Below is the code, I removed some unnecessary parts:
private void editmodeSwitch(boolean flag){
// get topbar, bottombar, and bottombar2
LinearLayout topbar = (LinearLayout) findViewById(R.id.task_topbar_linearLayout);
LinearLayout bottombar = (LinearLayout) findViewById(R.id.task_bottombar1_linearlayout);
LinearLayout bottombar2 = (LinearLayout) findViewById(R.id.task_bottombar2_linearlayout);
if(flag){
isEditmodeOn = true;
// make topbar and bottombar2 visilble, but bottombar gone
topbar.setVisibility(View.VISIBLE);
bottombar.setVisibility(View.GONE);
bottombar2.setVisibility(View.VISIBLE);
// make checkboxes visible in listview visible as well
for(int i=0; i<listView.getChildCount(); i++){
LinearLayout ll = (LinearLayout) listView.getChildAt(i);
CheckBox cb = (CheckBox) ll.findViewById(R.id.task_row_checkBox1);
cb.setVisibility(View.VISIBLE);
}
}
else{
isEditmodeOn = false;
topbar.setVisibility(View.GONE);
bottombar.setVisibility(View.VISIBLE);
bottombar2.setVisibility(View.GONE);
// set each checkbox false and its visibility gone
for(int i=0; i<listView.getChildCount(); i++){
LinearLayout ll = (LinearLayout) listView.getChildAt(i);
CheckBox cb = (CheckBox) ll.findViewById(R.id.task_row_checkBox1);
cb.setVisibility(View.GONE);
cb.setChecked(false);
}
}
}
It works fine but the problem is the application doesn't work when the screen rotates(changes the screen orientation). Everything worked fine as it displayed some layouts but only CheckBoxes in list items. Below is the code inonCreate()`:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.task_layout);
initialize();
loadDB();
updateListAdapter(list_title, list_date);
// in case of screen rotation
if(savedInstanceState != null){
isEditmodeOn = savedInstanceState.getBoolean(EDITMODE_CHECK);
isItemChecked = savedInstanceState.getBoolean(ITEM_CHECK);
if(isEditmodeOn){
if(!isItemChecked){
Log.i(tag, "item NOT checked");
editmodeSwitch(true);
} else{
//this is something different so please don't mind
deditmodeSwitch(savedInstanceState.getBooleanArray(LIST_CB_CHECK));
}
}
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// save values for rotation
outState.putBoolean(EDITMODE_CHECK, isEditmodeOn);
outState.putBoolean(ITEM_CHECK, isItemChecked);
outState.putBooleanArray(LIST_CB_CHECK, list_cb_check);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.i(tag, "you're in onRestoreInstanceState()");
// in case of screen rotation
if(savedInstanceState != null){
isEditmodeOn = savedInstanceState.getBoolean(EDITMODE_CHECK);
isItemChecked = savedInstanceState.getBoolean(ITEM_CHECK);
if(isEditmodeOn){
if(!isItemChecked){
Log.i(tag, "item NOT checked");
editmodeSwitch(true);
} else{
// this is for something else so please ignore this part
editmodeSwitch(savedInstanceState.getBooleanArray(LIST_CB_CHECK));
}
}
}
What I guessed is the ListView is being loaded at the end. Therefore, even if the code in onCreate() makes CheckBoxes visible, the CheckBoxes will become invisible again as its initialization in xml will do so. However, I'm stuck here and need your advice to solve this problem. Can anyone help me?
Just in case, below is the checkbox code of layout xml file for getview.
<CheckBox android:id="#+id/task_row_checkBox1" android:gravity="right"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"
/>
Override onSaveInstanceState for saving value on screen rotation and onRestoreInstanceState as:
protected void onCreate(Bundle savedInstanceState) {
if(null != savedInstanceState)
{
Boolean IntTest = savedInstanceState.getBoolean("ITEM_CHECK");
Boolean StrTest = savedInstanceState.getBoolean("ITEM_CHECK");
Log.e(TAG, "onCreate get the savedInstanceState+IntTest="+IntTest+"+StrTest="+StrTest);
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save away the CheckBoxes states, so we still have it if the activity
// needs to be killed while paused.
savedInstanceState.putBoolean(EDITMODE_CHECK, 0);
savedInstanceState.putBoolean(ITEM_CHECK, 0);
super.onSaveInstanceState(savedInstanceState);
Log.e(TAG, "onSaveInstanceState");
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Boolean IntTest = savedInstanceState.getBoolean(EDITMODE_CHECK);
Boolean StrTest = savedInstanceState.getBoolean(ITEM_CHECK);
Log.e(TAG, "onRestoreInstanceState+IntTest="+IntTest+"+StrTest="+StrTest);
}
Similar to how you override onCreate, you can override onConfigurationChanged(...) which you can setup to run when the screen changes orientation.
In order for OnConfigurationChanged(...) to be trigger when the screen rotates, you need to to edit your manifest and put that relationship/rule in.
It's easy to do but takes a bit of explaining and it was answered before in this question:
Activity restart on rotation Android
Edit: Here is the dev guide on how to handle configuration changes
http://developer.android.com/guide/topics/resources/runtime-changes.html
Edit #2: First, let me suggest using Imran's solution. It follows the Developer Guide better and the end results will be the same.
Now, for the onConfigurationChanged solution.
Look at what you are doing with your onCreate:
1) Set the view. (Checkboxes are hidden at this point. Right?)
2) Call your DB and determine if you should display checkboxes (edit mode)
3) Make all the checkboxes visible.
Now, onConfigurationChanged also calls setContentView, at which point all your checkboxes are hidden again. So you need to repeat the process of making your checkboxes visible (#3 above). You probably don't need to repeat step #2 because the value should be retained, but I'm not sure how the logic of your app works, so you may need to re-do step #2.
Does that make sense?
Based on my experience, getview seems to be triggered at the end and it was why 'onRestoreInstanceState()' and 'onConfigurationChanged()' could not make it as getview will reset my checkboxes invisible as initialization in the layout xml file.
Therefore, the only solution I could find out was I must control them in getview for the answer.

Android: why is my OnKeyListener() not called?

I defined an EditText-field and I want to be informed when the user edits that fields.
So I thought: simple - I add an OnKeyListener and so I did. But even though the text field gets edited (and even displays the entered/modified text) I don't get any callback, i.e. the LOG-output doesn't show up.
TextView text = new TextView(this);
text.setText(...);
...
text.setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event) {
TextView tv = (TextView)v;
CharSequence val = tv.getText();
Log.v(TAG, "value: " + val);
// ... rest omitted for brevity
}
});
Any idea, why that callback is never called?
Michael
PS.: Sigh! Android is really full of oddities! It seems that almost nothing I touched so far worked immediatly as one would expect. And - believe it or not - I have LOTS of experience with GUIs, esp. in Java (AWT, Swing, SWT, you name it...) But Android is a really tough beast!
Are you using the soft keyboard (ime) to type in the edit text? I believe that the onKeyListener only gets invoked with events from the hardware keyboard. You are better off using the TextWatcher if you can. onKeyListener not working with soft keyboard (Android)
I had the exact same problem, but on only 1 of my Android apps and I never did figure out what the difference was.
My solution though was to do what Mayra suggested and add a TextWatcher to handle the TextChanged events. So it works no matter how the text entry occurs.
editName.addTextChangedListener(new TextWatcher () {
public void afterTextChanged(Editable s) {
Button btnSave = (Button)findViewById(R.id.btnSaveToon);
if(s.length() > 0)
btnSave.setEnabled(true);
else
btnSave.setEnabled(false);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
});
Works like a charm in the emulator and on my HTC Inspire
You say that you're dealing with an EditText, but your code refers to a TextView. My guess is that you have an EditText in your layout XML files, but you're referring to this newly created TextView in your code, which is in fact not even in the app's UI at all.
If there is already an EditText in your layout XML file, then you need to get a pointer to it in your Java code, probably using the findViewById() method. Then add your OnKeyListener to that EditText.
Defining your layout in XML actually makes a lot more sense (at least in many, if not most, cases) than defining it one component at a time and then adding each those components to the UI, like you do in Swing. But it takes some getting used to, no question.
I had the same problem. The goal of the EditText was to input an amount of a currency, so I only needed the KeyEvents because I wanted the amount to be written from back to front as in apps like PayPal. I ended up just generating my own soft keyboard at the bottom of a RelativeLayout using a Fragment that is toggled with its visibility. If anyone wants to use my code, here you go:
The Keyboard Fragment Class:
public class KeyboardFragment extends Fragment {
private LinearLayout keyboard1, keyboard2, keyboard3, keyboard4, keyboard5, keyboard6,
keyboard7, keyboard8, keyboard9, keyboard0, keyboardReturn, keyboardApply;
private KeyboardListener keyboardListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_keyboard, container, false);
keyboard0 = view.findViewById(R.id.keyboard0);
keyboard1 = view.findViewById(R.id.keyboard1);
//and so on...
keyboard0.setOnClickListener((View v) -> {
keyboardListener.keyPressed(0);
});
keyboard1.setOnClickListener((View v) -> {
keyboardListener.keyPressed(1);
});
//and so on...
return view;
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
keyboardListener = (KeyboardListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement KeyboardListener");
}
}
public interface KeyboardListener {
public void keyPressed(int key);
}
}
The overlaying activity needs to implement the KeyboardListener and override the keyPressed function.
The XML of the Fragment looks like this:
Drawables:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/ui_keyboard_dark" />
<corners android:radius="5dp" />
</shape>
Then I inflated a container in the overlaying activity with the fragment and set its height to a moderate fraction of the screen's height:
KeyboardFragment fragment = new KeyboardFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.keyboardContainer, fragment);
fragmentTransaction.commit();
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,(int) ((double)displayMetrics.heightPixels / 2.8));
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
keyboardContainer.setLayoutParams(layoutParams);
Set a boolean that tracks the state of the keyboard in the activity. Then set the visibility according to actions like onBackPressed() or the click of the apply or ok button of the keyboard.
The amount of the currency is tracked using an int that would represent 10,95€ like this: amount = 1095
Then you just need to multiply the amount by 10 and add the pressed number. When pressing backspace just divide by 10.
I hope someone faces the same problem as me and finds this useful :).

Categories

Resources