I have an activity with bunch of controls (EditText, Spinner etc.), with one of the EditText's having a custom keyboard. Here is how my XML looks like
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/masterRelativeLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ScrollView
android:id="@+id/mainScrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true" >
<LinearLayout
android:id="@+id/childLinearLayout"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<!-- Many EditText's, Spinners here -->
</LinearLayout>
</ScrollView>
<android.inputmethodservice.KeyboardView
android:id="@+id/myKeyboardView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:keyPreviewLayout ="@layout/kbdpreview"
android:layout_alignParentBottom="true"
android:visibility="gone" />
</RelativeLayout>
Notice that the keyboard view is bottom-aligned, so that it shows up at the bottom of the screen.
The custom keyboard gets hidden and shown on the specific EditText as follows:
These methods are called when a specific EditText in the activity is touched.
private OnTouchListener m_onTouchListenerNotationText = new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
if (v == m_notationText)
{
// Hide the default keyboard
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
m_notationText.onTouchEvent(event);
m_customKeyboard.showCustomKeyboard(v);
AdjustScrollView();
return true; // Done with the event
}
return false;
}
};
Whenever the custom keyboard appears on the screen, it hides some of the controls in my view. However, Android framework does not know that this keyboard is being shown so the scrollview does not adjust. The controls remain hidden behind the custom keyboard. Hence, I have implemented a callback in the custom keyboard which gets called when the keyboard is visible.
// This listener is in showCustomKeyboard(...) function
// Set a static variable m_height and tell the activity to adjust the scrollview after keyboard becomes VISIBLE
mKeyboardView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout()
{
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN)
{
mKeyboardView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else
{
mKeyboardView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
m_height = mKeyboardView.getHeight();
Log.d(TAG, "Custom Keyboard now visible, height = " + m_height);
mHostActivity.AdjustScrollView();
}
});
// This listener is in hideCustomKeyboard(...) function
// Set a static variable m_height and tell the activity to adjust the scrollview after keyboard becomes HIDDEN
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout()
{
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN)
{
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else
{
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
m_height = 0;
Log.d(TAG, "Custom Keyboard now hidden");
mHostActivity.AdjustScrollView();
}
});
Finally, the scrollview adjustment happens as follows:
public synchronized void AdjustScrollView()
{
RelativeLayout rl = (RelativeLayout) findViewById(R.id.masterRelativeLayout);
int masterHeight = rl.getHeight();
Log.d(TAG, "Scrollview master relative layout height = " + masterHeight);
LinearLayout ll = (LinearLayout) findViewById(R.id.childLinearLayout);
int childHeight = ll.getHeight();
Log.d(TAG, "Scrollview child linear layout height = " + childHeight);
ScrollView scrollView = (ScrollView) findViewById(R.id.mainScrollview);
if (masterHeight - childHeight - m_customKeyboard.m_height <= 0)
{
// Need to adjust scrollview's height
scrollView.getLayoutParams().height = masterHeight - m_customKeyboard.m_height;
Log.d(TAG, "Setting scrollview.layoutparams.height = " + (masterHeight - m_customKeyboard.m_height));
}
else if (m_customKeyboard.m_height == 0)
{
// Need to adjust scrollview's height to full screen
scrollView.getLayoutParams().height = childHeight;
Log.d(TAG, "Setting scrollview.layoutparams.height = " + childHeight);
}
}
AndroidManifest.xml has the following line for this activity:
android:windowSoftInputMode="stateAlwaysHidden|stateUnchanged|adjustResize"
This mostly works, with 2 problems:
Problem 1: When I press long press on the EditText which shows custom keyboard, sometimes there is a race condition whereby the default keyboard AND custom keyboard both show up momentarily, then the default keyboard goes away (because I am hiding it in m_onTouchListenerNotationText). But in this time, the calculation of AdjustScrollView gets messed up, because now, the master relative layout height is very small (total height - custom keyboard height - default keyboard height). So the scrollview height calculation above is wrong. The scrollview now gets confined to a very tiny area at the top of the screen, followed by white space, followed by my custom keyboard at the bottom. I worked around this problem by adding call to AdjustScrollView in touch listener for the EditText (so whenever this happens, the user can click on that tiny view and the touch listener will adjust the scrollview). This work-around is undesirable, because, then it makes the long-press useless (I want the user to be able to long-press to show the default system menu of cut/copy/paste etc.). The work-around of requiring an additional touch makes this system menu go away.
Problem 2: If the custom keyboard is hidden, The scrollview does not take the entire screen size again. The bottom part of the screen, which was occupied by the custom keyboard, remains blank and the scrollview is only in the top part of the screen.