ChaniBlog











{February 5, 2014}   Android’s EditText is a minefield

I’ve been doing a lot of Android dev lately. Which of course means even more swearing than usual (yep, that is possible). It’s actually been fun, in a bizarre why-am-I-enjoying-this-torture kind of way, but there’s been a steady stream of WTFs along the way, and a surprising number of them involve EditText.

god damn fucking edittext. It’s just a little widget to get some text from the user. how hard could that be, right? :P

I had problems right off the bat with my personal app I started over the holidays. That one was simple – I used the standard fragments+viewpager template, threw an edittext on my layout, and tried to get me some text. Nope. By default, EditText is multiline, so you have an enter key that inserts a newline instead of a submit button. I was okay with multiline in the sense that it should grow and wrap text if I type a lot, but I didn’t want actual newlines, I wanted a ‘done’ button. I could have added a separate button to my UI, but that’s no fun :P so I fought with getting that button to show on the keyboard. Turns out, you need not only android:imeOptions="actionDone" (or submit or next or whatever), but also a sensible value for android:inputType (which took ages of trial and error to figure out – autocorrect vs autocomplete was not clearly explained at all) which in my case was android:inputType="text|textCapSentences|textAutoCorrect" (I c&p that as the default for all my edittexts now).

Then there are the weird issues with focus when you leave fragments or click another edittext. mostly they just cause warnings, but on kitkat some of those warnings turn into crashes.

Oh, and another weird thing – clicking ‘done’ or ‘submit’ or whatever does not automagically dismiss the keyboard. nor does leaving the fragment (and the keyboard does not like it when it’s still open and its edittext has vanished). The code to manually hide it is so ugly that I was sure it must be a dangerous hack, and was reluctant to use it, but it appears to be the only way.

    static public void hideKeyboard(View focusView) {
        InputMethodManager imm = (InputMethodManager) focusView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
    }
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        //Log.e(TAG, String.format("onEditorAction %d", actionId));
        if (actionId == EditorInfo.IME_ACTION_DONE) {
            setAndSaveText(v.getText().toString());
            Helpers.hideKeyboard(v);
            return true;
        }
        return false;
    }
//I have to manually call this whenever I do a fragment transaction.
    private void onFragmentChange() {
        View focus = getCurrentFocus();
        if (focus != null) {
            //Log.e(TAG, "focus: " + focus.toString());
            //keyboard should go away when leaving a fragment. how is this not standard?!
            Helpers.hideKeyboard(focus);

            //fix focusout bug on api 15
            //notes: dispatchWindowFocusChanged does nothing.
            //clearFocus works on api 15, but also calls onfocusin,
            // and gives worrying error messages on kitkat (nexus 5).
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                focus.clearFocus();
            }
        }
    }

Okay, that’s it for the problems encountered with a simple, common view. But then I started a more interesting project, where I needed to dynamically set up layouts at runtime. Yep, some of those layouts had edittexts in them, and that gets interesting. In a throw-your-computer-out-the-window kind of way. :P

The natural approach to a dynamic UI would be a ListView, right? It would just make sense, right? Wrong :P ListView and EditText do not get along. Period. Nothing’s stopping you from trying to put an EditText in a ListView, but if you do, spooky shit will start happening. Your keyboard will go crazy. Text will fail to appear, or focus will jump to the wrong place, or the cursor will show up in the wrong part of the screen… and on kitkat, it’ll just outright crash sometimes. After much googling and some rather depressing stackoverflow comments, I decided it was easier to just use a LinearLayout in a ScrollView. (luckily I won’t have enough rows for performance to matter.)

Everything seemed rosy again for a while, but here and there I was having issues with the text blanking out, or getting the text of a different EditText. Then I added a subscreen, and on returning to the screen with edittexts, poof, loads of text was disappearing. Oh, and radiobuttons were resetting too. Eventually I tracked it down to a really insidious side-effect of android’s helpful automatic view save/restore code. It was assuming that every view in the fragment has a completely unique ID, and since I’d instantiated multiple rows off the same layout, this was not the case.

I knew ListView had to have been preventing this somehow, so I dug in (thank god most of android is still open-source) and found what it had done: blocking dispatchSaveInstanceState and dispatchRestoreInstanceState. I made my own subclass of LinearLayout that did the same blocking, and all my widgets were happy again. :)

public class FormContainer extends LinearLayout {
    public FormContainer(Context context) {
        super(context);
    }
    public FormContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public FormContainer(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    //prevent save/restore of our children
    @Override
    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        super.dispatchFreezeSelfOnly(container);
    }
    @Override
    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        super.dispatchThawSelfOnly(container);
    }
}

Well, there are still some warnings when I jump directly from one edittext to another. But I think this is as good as it gets :P



markg85 says:

“code less, create more” doesn’t seem to be an android principle.
You can try to install Qt 5.2 on android and continue development in QML. Or is that giving you a massive performance penalty compared to native apps?



Chani says:

I dunno, never tried it. Can you submit to the app store with that?



markg85 says:

I suppose you can since there are Qt apps in the play store.
Do note that apps will get a lot bigger. The downside of dragging in a massive framework like Qt.



Comments are closed.

et cetera
Follow

Get every new post delivered to your Inbox.

Join 356 other followers

%d bloggers like this: