Whenever I speak to non-Android Native coders, I always say that Android is tricky! There are so many obscure things that you just have to know and no where is this more evident than in XML layouts.
I don’t even want to know how many hours & days, I’ve spent banging my head against the wall trying to diagnose symptoms, causes and cures for some layout bugs! In this series, I hope to save you from the head banging I’ve had to endure.
As in medicine, a symptom can have multiple causes, however, the cures/solutions focused on in this article were particularly difficult to diagnose and solve. If I’ve missed an exception, or you think more information in a section could be useful, please drop a comment below and I’ll include it. 🙂
Illness #1: Jerky Scroll/Ghost Scrolling
- Scrolling in a
NestedScrollViewisn’t smooth — seems jerky/doesn’t have momentum.
NestedScrollViewis scrolling down without user input. (Layout contains an
Both of the symptoms are related to focus (part of a UI selected by either the user/system):
- Scrolling isn’t smooth because the system is unsure where to focus even if you don’t have any
EditTextsin your layout (This typically only occurs on very large layouts of a
NestedScrollViewthat also contains a
RecyclerView). Think about it this way — the system is like someone with ADHD that tries to focus on everything but doesn’t actually focus on anything in particular, and this results in jerky scrolling.
- By default the system will always give focus to the
EditText(or focusable element) closest to the top of the layout. If the top-most
EditTextis toward the bottom of the layout (contained in the
NestedScrollView), the view will scroll upwards (ghost scroll). This ghost scroll may not occur consistently, making this rather tricky to debug.
There are two solutions, but I’d recommend the first one where possible.
1) Tell Android where to focus in your xml. Typically the focus should be placed on the highest level layout contained within the
NestedScrollView. Usually, this would be a
ConstrainLayout. In some cases, you can set the focus in the parent layout containing the
2) Tell Android not to focus on any elements (decedents) below the parent layout. Only do this if the first method doesn’t work because blocking focus means not views can gain focus even if the user clicks on the view i.e. an
EditText wouldn’t be editable because it can’t be selected. But here’s the kicker: the keyboard might be displayed even though the
EditText can’t be focused and buttons & other views can still register click events.
<?xml version="1.0" encoding="utf-8"?>
android:focusableInTouchMode="true<!-- OR --> android:descendantFocusability="blocksDescendants"><!--XML Content for your layout--> </android.support.constraint.ConstraintLayout></android.support.v4.widget.NestedScrollView>
Other Potential Causes
Doing too much on the MainThread resulting in frames being lost. It’s uncommon to be intentionally doing so much on the MainThread that you lose frames — it’s much more likely that you have a memory leak or you’re doing network calls on the main thread (the latter is very naughty and not in the good way).
Illness #2: CollapsingToolbar — Not Collapsing
You have an activity or fragment containing a
CollapsingToolbar and the content below it has variable length. You’re sure that you’ve got the right scroll flags but it just doesn’t scroll occasionally.
When content below the
CollapsingToolbar doesn’t fill more than the visible area available to it, the
CollapsingToolbar will only collapse if you scroll on it (not if you scroll by placing your finger on the content & drag up). This behaviour makes sense if your content is a standard size but if it isn’t the user will expect it to scroll.
The easiest way to solve this issue is if you set a minimum height on the
NestedScrollView or whatever is the main layout below the
CollapsingToolbar. I’ve found a minimum height of
android:minHeight=”600dp” to work well for most cases.
There are some complications that can arise when up use a
NestedScrollView in a fragment. If you encounter issues here, you are most welcome to get in touch with me and I’d be happy to help.
Illness #3: Shadows/Elevation Cut Off
Usually cut off at the bottom (can be a
CardView or any layout/view with an elevation).
- It appears that a parent (to the layout with elevation) layout’s bounds cutoff the shadow of its children for some reason.
- Shadows set with
android:elevationare cutoff by the child’s bounds. Extending the bounds of the view with margin or padding does not have an effect. It appears that they are cut off by the child’s original bounds.
- It therefore seems that the parent is clipping the child.
- Set padding on the parent (if needed) and set
android:clipToPadding="false"on that parent.
- By default shadow is determined by view’s background, so if there is no background, there will be no shadow. You may therefore also need to set a non-transparent background color on the child layout that has the elevation. Alternatively you can also
android:outlineProvider="bounds"which will provide the color it requires
If you have any suggestions for topics that could covered in a Part II, please drop a comment below and attempt diagnose and put forward cure. Lastly some shameful self-promotion: if you liked this article — give it some claps; if you learned something — give me a follow and you’ll notified when Part II is released.