title: How to make a curved onscreen keyboard for Android
body: <p>I would like to make a curved on-screen keyboard for Android.  I have examined the softkeyboard, and various other on screen keyboards on google code.  None that I have found involve a keyboard shape that is anything other than a rectangle.  Ideally, I would like to create a keyboard that consists of keys distributed across two semi-circles on opposite sides of the screen (i.e., imagine holding a tablet by the sides and being able to hit the keys with your thumbs).</p>
  
  <p>Of the code I have examined, onscreen keyboards are created as views (usually extending KeyboardView) and appear as a continuous bar across the bottom of the screen.  As an approximation to my goal, I have tried to alter code I found on google code (dotdash-keyboard-android) to only draw its keys in the lower left-hand corner and leave the lower-right hand corner transparent.  I have been able to override onMeasure to affect the dimensions of the view (see below), but this only seems to alter the positions of the keys and not the positions of the container. In other words, there is still a black bar filling the bottom of the screen.</p>
  
  <pre><code>//Located within KeyboardView
  @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
      this.setMeasuredDimension(200, 200);
  }
  </code></pre>
  
  <p>Is what I want to do even possible? Is there a more correct term for this?  Are there projects I can use as examples?</p>
  
  <p>I have also tried to set the view's dimensions using this.setLayoutParams -- but, these calls seem to have no effect.  I have also tried to use this.getParent to access the parent view (if one even exists) and change it's dimensions but this approach does not work (or, I'm just doing it wrong).  Any help would be much appreciated.</p>
  
  <p>Thank you</p>
  
  <p>UPDATE: 12/21/2012 - I think I need to override the parent class's onDraw method.  Looking <a href="http://www.java2s.com/Open-Source/Android/android-core/platform-frameworks-base/android/inputmethodservice/KeyboardView.java.htm" rel="noreferrer">here</a> , it looks like KeyboardView's onDraw method draws to a canvas that is equal to the size of the screen using the following code:</p>
  
  <pre><code>final int width = Math.max(1, getWidth());
  final int height = Math.max(1, getHeight());
  mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  mCanvas = new Canvas(mBuffer);
  </code></pre>
  
  <p>I think I can override onDraw and draw whatever I want on the canvas.</p>
  
  <p>UPDATE: 12/21/2012 - I've overridden onDraw and it's now clear that the keyboardView is the dimensions I'm setting it to (200x200).  Using Hierarchyview, I can see that the keyboardview is inside a framelayout with the id InputArea.  So, the horizontal bar that fills the entire width is this framelayout.  But, I'm not creating it -- where does it come from and how can I alter its dimensions?</p>
  
  <p>UPDATE: 12/22/2012 - After more testing, it seems like the behavior (dimensions) of a keyboardview are in-part determined by the activity that calls it.  In the browser, I get the behavior I've been describing: the height of the browser window shrinks to accommodate a bar across the bottom of the screen that holds the keyboard, even if the width of the keyboard is less than the width of the screen.  In the calendar app, the keyboard size appears as I have set it (as a square in the lower-left hand corner)  with the calendar appearing unchanged beneath it.  So, it seems impossible to reach my goal with most apps using this approach.  An alternative approach might be to have the IME service create a popupwindow or dialog.  One problem is that popupwindows need a parent view or anchor to attach to and I don't think it's possible to find the top-most view from the IME service.  Perhaps I can create a transparent view over the current activity and place the popup on top of that?</p>
  
  <p>UPDATE: 12/23/2012 - Progress.  I've figured out how to display a popup from the keyboard IME.  The next step is to figure out how to make the popups a little round/organic.  Here's a screenshot of what I accomplished followed by source.
  <img src="https://i.stack.imgur.com/PvCKY.png" alt="Emulator screenshot of keyboard"></p>
  
  <p>Source.  The following method is in the service IME class and called by the child (of the service) view's onMeasure method so that the popups open at the same time the keyboard is drawn.  I've set the dimensions of the keyboard to 1x1 so it isn't visible.  The log statements are there to help me figure out how to position the popups.</p>
  
  <pre><code>public void initiatePopupWindow()
  {
      try {
          WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
          Display display = wm.getDefaultDisplay();
          DisplayMetrics dm = new DisplayMetrics();
          display.getMetrics(dm);
          //display.getSize(p);
  
  
          Log.i("dotdashkeyboard","initiatePopupWindow (from IME service)");
          LayoutInflater inflater = (LayoutInflater) this.getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          View layoutLeft = inflater.inflate(R.layout.popup_layout_left,null);
          View layoutRight = inflater.inflate(R.layout.popup_layout_right, null);
          // create a 300px width and 470px height PopupWindow
          int popupHeight = 300;
          int popupWidth = 200;
          if (popUpLeft == null) popUpLeft = new PopupWindow(layoutLeft, popupWidth, popupHeight, false);
          if (popUpRight == null) popUpRight = new PopupWindow(layoutRight, popupWidth, popupHeight, false);
  
          int ypos = 0;
          int xposRight = 0;
  
  
  
          if (display.getRotation() == Surface.ROTATION_0) {
              ypos = -(dm.heightPixels / 2 + popupHeight/2);
              xposRight = (dm.widthPixels - popupWidth);
              Log.i("dotdashkeyboard","test rotation=normal");
          } else if (display.getRotation() == Surface.ROTATION_90) {
              ypos = -(dm.heightPixels / 2 + popupHeight/2)/2;
              xposRight = (dm.widthPixels - popupWidth)*2-popupWidth;
              Log.i("dotdashkeyboard","test rotation=90-degrees");
          } else {
              Log.i("dotdashkeyboard","test rotation=unknown=" + display.getRotation());
          }
  
          popUpLeft.showAtLocation(inputView, Gravity.NO_GRAVITY, 0, ypos);
          popUpRight.showAtLocation(inputView, Gravity.NO_GRAVITY, xposRight, ypos);
  
          Log.i("dotdashkeyboard","test created popup at ypos="+ypos  + " xposRight=" + xposRight);
          Log.i("dotdashkeyboard","test screenWidth=" + dm.widthPixels + " screenHeight=" + dm.heightPixels);
  
          Button cancelButton = (Button) layoutLeft.findViewById(R.id.popup_cancel_button);
          //cancelButton.setOnClickListener(inputView.cancel_button_click_listener);
          cancelButton.setOnClickListener(cancel_button_click_listener);
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
  </code></pre>