Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Android, Can One Intercept Key Events Globally Before They Reach the Child View That Has Focus?

In most circumstances, I want key events to be processed by the (child) view that presently has focus, which is the default behavior, and also is what I presently have implemented.

However, under certain very specific circumstances, I would like to temporarily intercept and handle all key events (including specifically those normally handled by the child views) either in my current activity or, failing that, in my root view (it doesn't matter, so long as they are processed globally - I don't care about hardware buttons like volume +/-, as these are not handled in any of my child views anyway).

For touch events, we have onInterceptTouchEvent(), which allows a ViewGroup to watch touch events as they are dispatched to child views, and to block receipt of those events (when desired) by the child views for which they are intended.

Unfortunately, I can't find anything analogous to onInterceptTouchEvent() for key events. Am I missing something obvious, or is this an actual asymmetry in the OS?

Of course, I could just wire the current key event handler code of every child view to directly call a method on the main activity to handle the event if it wants to, and to have that activity-level method return a boolean that indicates whether it handled the event. Then, the child view could handle the event if and only if the activity method it invoked had not handled it.

But I'm hoping there's a cleaner way to simply intercept the traffic on its way "down" the hierarchy to the child view, just as onInterceptTouchEvent() does for touch events.

like image 365
Carl Avatar asked Feb 15 '13 08:02

Carl


1 Answers

You can add OnGlobalFocusChangeListener to your root view. Let's do that per activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViewById(android.R.id.content).getViewTreeObserver().addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() {
        @Override
        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
            if (newFocus instanceof EditText) {
                ((EditText) newFocus).addTextChangedListener(new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                        //process whatever you want
                    }

                    @Override
                    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                    }

                    @Override
                    public void afterTextChanged(Editable editable) {
                    }
                });
            }
        }
    });
}

Of course if you are interested in views other than EditText you have to figure out how to observe their changes.

As well if you want to add it globally to all of your activities use Application#registerActivityLifecycleCallbacks() to use that code in all of your activities.

like image 94
Krzysztof Misztal Avatar answered Oct 15 '22 22:10

Krzysztof Misztal