[assembly: ExportRenderer(typeof(EntryWithCustomKeyboard), typeof(EntryWithCustomKeyboardRenderer))] namespace CustomKeyboard.Droid.Renderers { public class EntryWithCustomKeyboardRenderer : EntryRenderer, IOnKeyboardActionListener { private Context context; private EntryWithCustomKeyboard entryWithCustomKeyboard; private Android.InputMethodServices.KeyboardView mKeyboardView; private Android.InputMethodServices.Keyboard mKeyboard; private InputTypes inputTypeToUse; private bool keyPressed; public EntryWithCustomKeyboardRenderer(Context context) : base(context) { this.context = context; } protected override void OnElementChanged(ElementChangedEventArgs<Entry> e) { base.OnElementChanged(e); var newCustomEntryKeyboard = e.NewElement as EntryWithCustomKeyboard; var oldCustomEntryKeyboard = e.OldElement as EntryWithCustomKeyboard; if (newCustomEntryKeyboard == null && oldCustomEntryKeyboard == null) return; if (e.NewElement != null) { this.entryWithCustomKeyboard = newCustomEntryKeyboard; this.CreateCustomKeyboard(); this.inputTypeToUse = this.entryWithCustomKeyboard.Keyboard.ToInputType() | InputTypes.TextFlagNoSuggestions; // Here we set the EditText event handlers this.EditText.FocusChange += Control_FocusChange; this.EditText.TextChanged += EditText_TextChanged; this.EditText.Click += EditText_Click; this.EditText.Touch += EditText_Touch; } // Dispose control if (e.OldElement != null) { this.EditText.FocusChange -= Control_FocusChange; this.EditText.TextChanged -= EditText_TextChanged; this.EditText.Click -= EditText_Click; this.EditText.Touch -= EditText_Touch; } } protected override void OnFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e) { e.Result = true; if (e.Focus) this.Control.RequestFocus(); else this.Control.ClearFocus(); } // Event handlers private void Control_FocusChange(object sender, FocusChangeEventArgs e) { // Workaround to avoid null reference exceptions in runtime if (this.EditText.Text == null) this.EditText.Text = string.Empty; if (e.HasFocus) { this.mKeyboardView.OnKeyboardActionListener = this; if (this.Element.Keyboard == Keyboard.Text) this.CreateCustomKeyboard(); this.ShowKeyboardWithAnimation(); } else { // When the control looses focus, we set an empty listener to avoid crashes this.mKeyboardView.OnKeyboardActionListener = new NullListener(); this.HideKeyboardView(); } } private void EditText_TextChanged(object sender, Android.Text.TextChangedEventArgs e) { // Ensure no key is pressed to clear focus if (this.EditText.Text.Length != 0 && !this.keyPressed) { this.EditText.ClearFocus(); return; } } private void EditText_Click(object sender, System.EventArgs e) { ShowKeyboardWithAnimation(); } private void EditText_Touch(object sender, TouchEventArgs e) { this.EditText.InputType = InputTypes.Null; this.EditText.OnTouchEvent(e.Event); this.EditText.InputType = this.inputTypeToUse; e.Handled = true; } // Keyboard related section // Method to create our custom keyboard view private void CreateCustomKeyboard() { var activity = (Activity)this.context; var rootView = activity.Window.DecorView.FindViewById(Android.Resource.Id.Content); var activityRootView = (ViewGroup)((ViewGroup)rootView).GetChildAt(0); this.mKeyboardView = activityRootView.FindViewById<Android.InputMethodServices.KeyboardView>(Resource.Id.customKeyboard); // If the previous line fails, it means the keyboard needs to be created and added if (this.mKeyboardView == null) { this.mKeyboardView = (Android.InputMethodServices.KeyboardView)activity.LayoutInflater.Inflate(Resource.Layout.CustomKeyboard, null); this.mKeyboardView.Id = Resource.Id.customKeyboard; this.mKeyboardView.Focusable = true; this.mKeyboardView.FocusableInTouchMode = true; this.mKeyboardView.Release += (sender, e) => { }; var layoutParams = new Android.Widget.RelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.WrapContent); layoutParams.AddRule(LayoutRules.AlignParentBottom); activityRootView.AddView(this.mKeyboardView, layoutParams); } this.HideKeyboardView(); this.mKeyboard = new Android.InputMethodServices.Keyboard(this.context, Resource.Xml.special_keyboard); this.SetCurrentKeyboard(); } private void SetCurrentKeyboard() { this.mKeyboardView.Keyboard = this.mKeyboard; } // Method to show our custom keyboard private void ShowKeyboardWithAnimation() { // First we must ensure that keyboard is hidden to // prevent showing it multiple times if (this.mKeyboardView.Visibility == ViewStates.Gone) { // Ensure native keyboard is hidden var imm = (InputMethodManager)this.context.GetSystemService(Context.InputMethodService); imm.HideSoftInputFromWindow(this.EditText.WindowToken, 0); this.EditText.InputType = InputTypes.Null; var animation = AnimationUtils.LoadAnimation(this.context, Resource.Animation.slide_in_bottom); this.mKeyboardView.Animation = animation; this.mKeyboardView.Enabled = true; // Show custom keyboard with animation this.mKeyboardView.Visibility = ViewStates.Visible; } } // Method to hide our custom keyboard private void HideKeyboardView() { this.mKeyboardView.Visibility = ViewStates.Gone; this.mKeyboardView.Enabled = false; this.EditText.InputType = InputTypes.Null; } // Implementing IOnKeyboardActionListener interface public void OnKey([GeneratedEnum] Keycode primaryCode, [GeneratedEnum] Keycode[] keyCodes) { if (!this.EditText.IsFocused) return; // Ensure key is pressed to avoid removing focus this.keyPressed = true; // Create event for key press long eventTime = JavaSystem.CurrentTimeMillis(); var ev = new KeyEvent(eventTime, eventTime, KeyEventActions.Down, primaryCode, 0, 0, 0, 0, KeyEventFlags.SoftKeyboard | KeyEventFlags.KeepTouchMode); // Ensure native keyboard is hidden var imm = (InputMethodManager)this.context.GetSystemService(Context.InputMethodService); imm.HideSoftInputFromWindow(this.EditText.WindowToken, HideSoftInputFlags.None); this.EditText.InputType = this.inputTypeToUse; switch(ev.KeyCode) { case Keycode.Enter: // Sometimes EditText takes long to update the HasFocus status if (this.EditText.HasFocus) { // Close the keyboard, remove focus and launch command asociated action this.HideKeyboardView(); this.ClearFocus(); this.entryWithCustomKeyboard.EnterCommand?.Execute(null); } break; } // Set the cursor at the end of the text this.EditText.SetSelection(this.EditText.Text.Length); if (this.EditText.HasFocus) { this.DispatchKeyEvent(ev); this.keyPressed = false; } } public void OnPress([GeneratedEnum] Keycode primaryCode) { } public void OnRelease([GeneratedEnum] Keycode primaryCode) { } public void OnText(ICharSequence text) { } public void SwipeDown() { } public void SwipeLeft() { } public void SwipeRight() { } public void SwipeUp() { } private class NullListener : Java.Lang.Object, IOnKeyboardActionListener { public void OnKey([GeneratedEnum] Keycode primaryCode, [GeneratedEnum] Keycode[] keyCodes) { } public void OnPress([GeneratedEnum] Keycode primaryCode) { } public void OnRelease([GeneratedEnum] Keycode primaryCode) { } public void OnText(ICharSequence text) { } public void SwipeDown() { } public void SwipeLeft() { } public void SwipeRight() { } public void SwipeUp() { } } } }