Skip to content

A trap in Android ViewGroup customization

Posted on:October 20, 2023

In this post, we won’t delve into the process of customizing a ViewGroup since there are many excellent guides available for that purpose. Instead, I’ll highlight a trap in a crucial function within ViewGroup that is often overlooked by many developers.

The crucial function in ViewGroup is shouldDelayChildPressedState(). Let’s expoler it intended use.

The Function

public boolean shouldDelayChildPressedState() {
    return true;
}

In class ViewGroup, the default implementation return true. As it’s named, the pressed state should be delayed for children or descendants of this ViewGroup. It is invoked by isInScrollingContainer() in ViewGroup.

public boolean isInScrollingContainer() {
    ViewParent p = getParent();
    while (p != null && p instanceof ViewGroup) {
      if (((ViewGroup) p).shouldDelayChildPressedState()) {
        return true;
      }
      p = p.getParent();
    }
    return false;
}

This function checks whether its parents are a scrolling container or not. It is invoked in the View’s touch event logic.

public boolean onTouchEvent(MotionEvent event) {
    ...
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            if (isInScrollingContainer()) { // <----
                postDelayed(mPendingCheckForTap,
                    ViewConfiguration.getTapTimeout());
            } else {
                setPressed(true, x, y);
            }
        break;
    }
    ...
}

As I mentioned before, when the view that has been touch is inside a scrolling container, it will delay the pressed feedback for a short period.

The short period refers to ViewConfiguration.getTapTimeout(), the value of which is 100ms

The Trap

Regardless of when you touch the view, it will introduce a 100ms delay to verify the pressed state.

OK, 100ms is a short duration for a single view, but it can become a significant factor when you prioritize app performance with a large number of views.

So, we do not need this 100ms delay if the ViewGroup is not scrollable, that means implementation of shouldDelayChildPressedState should return false.

The trap lies in the default implementation, which returns true, and surprisingly, there are no official guides that mention it. It’s easy to overlooked this.

The implementations of ViewGroup’s non-scrollable subclasses, such as FrameLayout or LinearLayout, return false.

I prefer to use these subclasses for customizing rather than using ViewGroup directly to avoid these unexpected traps.