Using Custom XAML Controls in the .Android and .iOS Projects with a Keyboard Service App

adamjhiltonadamjhilton Member ✭✭
edited August 12 in Xamarin.Forms

I have a custom keyboard app I'm trying to write. I'd like to define all the keys as XAML controls in my shared code and then incorporate them into native code in the .Android and .iOS projects. Being primarily a desktop WPF developer, I'm comfortable with this approach. To be honest, I'm not sure this is even possible.

I have versions of my controls as both a ContentPage and a StackLayout. I couldn't quite figure out how to shoehorn either into my native project.

From the Android side...
When your MainActivity inherits from FormsAppCompatActivity, this is pretty straight forward:

namespace MobileTestApp2.Droid
{
    [Activity(Label = "MobileTestApp2", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());
        }
    }
}

However, in order for the app to register as an Android keyboard service, MainActivity has to inherit from InputMethodService:

namespace TestKeyboard.Droid
{
 [Service(Permission = "android.permission.BIND_INPUT_METHOD", Label = "Test Keyboard")]
 [MetaData("android.view.im", Resource = "@xml/method")]
 [IntentFilter(new string[] { "android.view.InputMethod" })]
 public class MainActivity : InputMethodService, KeyboardView.IOnKeyboardActionListener
 {
  private KeyboardView keyboardView;
  private Keyboard keyboard;
  private bool isCaps = false;

  public override View OnCreateInputView()
  {
   keyboardView = (KeyboardView)LayoutInflater.Inflate(Resource.Layout.Keyboard, null);
   keyboard = new Keyboard(this, Resource.Xml.Qwerty);
   keyboardView.Keyboard = keyboard;
   keyboardView.OnKeyboardActionListener = this;

   return keyboardView;
  }
}

I'm not sure how to convert my XAML controls into Android controls. From research, I found that this is possible using the CreateSupportFragment() method. However, this method is only available for FormsAppCompatActivity objects. I tried instantiating a local object inheriting from FormsAppCompatActivity and replacing my qwerty.xml layout with my converted XAML control before I instantiated a new Keyboard object; but it didn't work.

namespace MurrayRegisterKeys.Droid
{
 public class InnerActivity : FormsAppCompatActivity
 {
  public void Test()
  {
   Fragment mainPage = new MainPage().CreateSupportFragment(MainActivity.mainActivity);
   SupportFragmentManager.BeginTransaction().Replace(Resource.Id.qwerty, mainPage).Commit();
  }
 }
}

Any ideas? Thanks!

From it iOS side...
TBD...

Answers

  • LuisDavidDelaCruzLuisDavidDelaCruz Member ✭✭✭

    Hi @adamjhilton, have you tried implement a Switch?

    switch(Device.RuntimePlatform)
            {
                case Device.iOS:
                    //Show iOS controls
                    break;
                case Device.Android:
                    //Show Android controls
                    break;
        case Device.WinPhone:
                    //Show Windows controls
                    break;
            }
    
  • adamjhiltonadamjhilton Member ✭✭

    I don't think so. Something like this:

     public class MainActivity : InputMethodService, KeyboardView.IOnKeyboardActionListener
     {
      private KeyboardView keyboardView;
      private Keyboard keyboard;
      private bool isCaps = false;
    
      public override View OnCreateInputView()
      {
       keyboardView = (KeyboardView)LayoutInflater.Inflate(Resource.Layout.Keyboard, null);
       KeyboardControl keyboardControl = new KeyboardControl(); // Instantiate Xamarin.Forms control.
       keyboard = new Keyboard(this, keyboardControl); // Instantiate Keyboard with Xamarin.Forms control as visualization.
       keyboardView.Keyboard = keyboard;
       keyboardView.OnKeyboardActionListener = this;
    
       return keyboardView;
      }
    

    KeyboardControl is my XAML Xamarin Forms control. I want to use this as my visualization for the Android Keyboard control. Or something like that.

  • LandLuLandLu Member, Xamarin Team Xamurai

    @adamjhilton If you want to load pages from PCL on the native project you can try Native Forms:
    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/platform/native-forms
    Use CreateViewController() on iOS
    and CreateSupportFragment() on Android.

  • adamjhiltonadamjhilton Member ✭✭

    I was trying something similar in my third code block of my original post. Although, I see I was missing the Forms.Init(this, bundle) call. Because of the bundle argument, I still need to call from this inside of the OnCreate() method. I'm not even sure this is getting called as the entry point for the Android project is not derived from AppCompatActivity; it's derived from InputMethodService. Also, the SupportFragmentManager member is not available in that context either.

    Here is my latest attempt: I'm not sure just instantiating an instance of InnerActivity will call the OnCreate() method.

     public class MainActivity : InputMethodService, KeyboardView.IOnKeyboardActionListener
     {
      public override View OnCreateInputView()
      {
       // This is the entry point.
       new InnerActivity();
    
       KeyboardView keyboardView = (KeyboardView)LayoutInflater.Inflate(Resource.Layout.Keyboard, null);
       keyboardView.OnKeyboardActionListener = this;
    
       return keyboardView;
      }
     }
    
     class InnerActivity : AppCompatActivity
     {
      protected override void OnCreate(Bundle bundle)
      {
       base.OnCreate(bundle);
    
       Forms.Init(this, bundle);
    
       SetContentView(Resource.Layout.Keyboard);
    
       Android.Support.V4.App.Fragment keyboardPage = new KeyboardPage().CreateSupportFragment(this);
       SupportFragmentManager
           .BeginTransaction()
           .Replace(Resource.Id.keyboard, keyboardPage)
           .Commit();
      }
     }
    
  • LandLuLandLu Member, Xamarin Team Xamurai

    Have you made it work?
    I found this blog talking about how to change the keyboard on Xamarin.Android you could refer to it for the detailed code:
    https://medium.com/swlh/how-to-create-a-custom-keyboard-with-xamarin-forms-android-4fa3b83dad1d

  • adamjhiltonadamjhilton Member ✭✭

    I've read that post before. The author is creating his keyboard completely in Android XML; not in Xamarin.Forms. Also, his design is for a normal application; not a keyboard service. In other words, his MainActivity inherits from FormsAppCompatActivity, not InputMethodService.

    I want to be able to replace the default keyboard with my custom keyboard so that I can use it in a 3rd party VNC application to remotely control an industrial control unit. I want to avoid having to write an entire VNC app just to have a keyboard.

Sign In or Register to comment.