4

I'm making a custom view that can be annotated with typical paint tools like draw, shape dropping, or writing text. I've implemented all of the tools that would fall under the "Drawing" category, but text entry is giving me a lot of trouble. I would very much prefer to avoid creating EditTexts on top of this view as an approach. There seem to be a lot of conflicting answers to questions about this topic out there.

What I want to do:

  • Tap anywhere in the custom view and open keyboard / indicate that text can be entered
  • Get keyboard input - preferably any kind of keyboard. How to listen to keypress in the soft keyboard seems to have some contention about what works for hard or soft keyboards.
  • Take the keyboard input, and draw it on my canvas after the user enters any character.
  • Allow the user to edit that text
  • Allow the user to move the text

I've tried overriding onKeyUp() and intercepting each keypress. The android docs suggest that editable text should be entered using DynamicLayout, so I've been attempting to use dynamicLayout.draw(canvas). I can open the soft keyboard, but even after setting my view to focusable and requesting focus, the onKeyUp() does not fire. I've seen several answers to questions that warn against using anything but an EditText for text entry due to complexity and issues with custom keyboards. Surely someone knows how to do this?

What would be a huge help to me, is a basic overview of what goes into displaying user entered text on a canvas as it is entered. A high level step by step of what to do would be fine. Even just details on where to look to figure out the first three steps above would be greatly appreciated.

neekolawz
  • 99
  • 8
  • Have you considered using a single, "floating" `EditText` that is repositioned, and shown/hidden as needed? Upon tapping, move the initially-hidden `EditText` to the appropriate spot, set any necessary text/font attributes on it, then show it. After text entry is complete, hide the `EditText`, grab the text from it, and draw it in the same location on your custom `View`. – Mike M. Aug 17 '18 at 01:04
  • Possible duplicate of [How to capture soft keyboard input in a View?](https://stackoverflow.com/questions/5419766/how-to-capture-soft-keyboard-input-in-a-view) – Marcos Vasconcelos Aug 17 '18 at 02:18
  • @MikeM. That method does work, and is what I normally would do in this situation. However, I'm hoping to contain all annotation logic within my custom view, so that I can release it as a library. If I rely on separate EditTexts, I think that would require users of the library to essentially roll their own text-entry. – neekolawz Aug 17 '18 at 17:43
  • I didn't say "separate" `EditText`. You can easily include it in your custom `View`. – Mike M. Aug 17 '18 at 17:48
  • @MikeM. I was under the impression that I can't have a `View` within a `View`. Is that not so? Sorry if I'm misunderstanding what you're suggesting. If I can do this via an EditText within my view, I'd be game to try that out. – neekolawz Aug 17 '18 at 18:13
  • 2
    Your custom `View` doesn't have to be strictly a regular `View`. It can be a `ViewGroup` (which is a `View`, btw). You could put your drawing `View` and the `EditText` both inside some `ViewGroup`, and the whole thing would be your custom `View`; e.g., `CustomView extends FrameLayout` contains (`DrawingView` & `EditText`). You could even just have your drawing `View` extend a `ViewGroup`, and put the single `EditText` in that, 'cause you can draw to `ViewGroup`s basically the same way; e.g., `DrawingView extends FrameLayout` contains `EditText`. – Mike M. Aug 17 '18 at 20:39
  • 2
    Somebody just recently posted a question that I handled, and it had a custom drawing `View`, so I figured I'd grab it and modify it a bit to do a quick test run of my suggestion. It is _veeery_ rough, but it should give you some ideas. The positioning is approximate, it's only single-line atm, you'd probably want more attributes in `TextBlock`, etc. https://drive.google.com/file/d/1C9ChwRfVXhKHWBIFxZHaWRxWbHElKdys/view?usp=drivesdk. It's in drawing mode by default, so just call `setDrawingModeEnabled(false)` to switch to text mode, tap then click the `EditText`, type some text, and hit enter. – Mike M. Aug 18 '18 at 01:27
  • @MikeM. Thanks so much! I'll check it out. – neekolawz Aug 19 '18 at 02:25

1 Answers1

1

To get the text from keyboard events

1) Replace the following line in onCreateInputConnection():

outAttrs.inputType = InputType.TYPE_CLASS_TEXT; with this one:

outAttrs.inputType = InputType.TYPE_NULL; Per the documentation for InputType.TYPE_NULL: "This should be interpreted to mean that the target input connection is not rich, it can not process and show things like candidate text nor retrieve the current text, so the input method will need to run in a limited 'generate key events' mode."

2) Replace the following line in the same method:

BaseInputConnection fic = new BaseInputConnection(this, true);

with this one:

BaseInputConnection fic = new BaseInputConnection(this, false);

The false second argument puts the BaseInputConnection into "dummy" mode, which is also required in order for the raw key events to be sent to your view. In the BaseInputConnection code, you can find several comments such as the following: "only if dummy mode, a key event is sent for the new text and the current editable buffer cleared."

Now that you can capture the events, you can store the text input in a variable and then you can draw text using paint like this on your canvas:

Paint paint = new Paint(); 
paint.setColor(Color.WHITE); 
paint.setStyle(Style.FILL); 
canvas.drawPaint(paint); 

paint.setColor(Color.BLACK); 
paint.setTextSize(20); 
canvas.drawText("Some Text", 10, 25, paint); 
Zachary Sweigart
  • 1,051
  • 9
  • 23