Distraction-Free Reading for your Android app — Part I

Pocket’s Auto-Fullscreen mode in action

Outline

This guide aims to make all of that more accessible, and hopefully save you time and frustration if you’re looking to build something similar. It’ll assume no more than a mid-beginner level of experience with Android development.

What we’re going to build

  • A basic article view with a Toolbar on top, a translucent system navigation bar and a Floating Action Button (FAB) to make things a bit more interesting.
  • When the user scrolls down the article, the Toolbar, system bars and FAB (“chrome” collectively) will animate away all at once.
  • Scrolling up will animate the chrome back in.
  1. The text content should not jump or snap in any way when the chrome animates in or out. Chrome should fade away seamlessly, without disrupting the user.
  2. It should never be necessary for the user to trigger auto-hiding of chrome in order to read all parts of the article. In other words, it should still be possible to read the entire article even if chrome happened to get stuck in a permanently-visible state (this is actually related to the previous constraint — more on that later).

Getting Started

Let’s dive in. Open up Android Studio and create a new project with a blank activity.

Theme

By default your app’s theme will inherit from Theme.AppCompat.Light.DarkActionBar which will automatically add an Action bar/Toolbar to your activity windows. We’re going to want to add the Toolbar to our Activity layout manually to precisely control its placement, so this isn’t what we want. Change your AppTheme to inherit from AppCompat’s NoActionBar theme instead:

app/src/main/res/values/styles.xml

Build Dependencies

Since we want a FAB we’ll need to add a dependency on Google’s Design Support Library. Add this to the dependencies section of your app module’s build script:

compile 'com.android.support:design:25.3.1'

Strings

We’ll need some dummy article content for our reader activity, and enough of it to extend outside of the screen to make the view scrollable.

<string name="lorem">Lorem ipsum dolor sit amet ... </string>

Initial Activity Layout

Open up activity_main.xml and replace whatever’s in there with this:

app/src/main/res/layout/activity_main.xml

A quick refresher on FrameLayout

So what’s going on here is we have two full-size (i.e. width and height set to match_parent) view groups stacked on top of each other inside a root FrameLayout. The first view group is for the article text (the NestedScrollView) and the second is for the UI chrome (the inner FrameLayout).

Initial Activity Code

There isn’t much code to write at the point, all we need to do is ensure some text is displayed in the Toolbar.

app/src/main/java/<com.yourpackagehere>/MainActivity.java
Our inappropriately-titled first version

Removing Distractions, One Step at a Time

Disappearing FAB

It turns out hiding the FAB when the user scrolls is straightforward enough, so let’s start there. Jump back into editing MainActivity in Android Studio and add a new field for the FAB:

private FloatingActionButton actionButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
actionButton = (FloatingActionButton) findViewById(R.id.fab);
setupToolbar();
}

Listening out for Scroll direction changes

NestedScrollView can have an OnScrollChangeListener set on it via its aptly-named setOnScrollChangeListener() method. This listener gives us all of the info we need (and then some!) to know whether to hide or show the FAB via its single callback method:

void onScrollChange (NestedScrollView v, 
int scrollX,
int scrollY,
int oldScrollX,
int oldScrollY)
app/src/main/java/<com.yourpackagehere>/OnScrollDirectionChangedListener.java
private void setupScrollView() {
NestedScrollView scrollView =
(NestedScrollView) findViewById(R.id.nested_scrollview);

scrollView.setOnScrollChangeListener(
new OnScrollDirectionChangedListener() {
@Override
public void onStartScrollingDown() {
actionButton.hide();
}

@Override
public void onStartScrollingUp() {
actionButton.show();
}
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
actionButton = (FloatingActionButton) findViewById(R.id.fab);
setupToolbar();
setupScrollView();
}

Disappearing Toolbar

Since we have a scroll listener set up, it’s not too much extra work to get the Toolbar to appear and re-appear as well.

app/src/main/java/<com.yourpackagehere>/MainActivity.java
app/src/main/java/<com.yourpackagehere>/MainActivity.java

Further Discussion

Q: Why not use CoordinatorLayout + AppBarLayout from the design support library to hide the toolbar on scrolls?

A: This is a valid question — the design support library provides an alternative way to do everything we’ve seen in the sample until now with a combination of CoordinatorLayout, AppBarLayout, NestedScrollView and custom Behaviors. It’s good to use existing solutions where possible to avoid reinventing the wheel, and in fact this is where I started. Ultimately however, I moved away from using CoordinatorLayout for a number of reasons.

Q: What about just using a custom CoordinatorLayout Behavior to hide the FAB?

Yes that can be done — the recommended approach was to use CoordinatorLayout as your root layout, create a new ScrollAwareFABBehavior class that extends FloatingActionButton.Behavior, setting it as the FAB’s layout_behavior in XML, and this will work fine.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Matt Clarke

Matt Clarke

Android engineer living in Auckland, New Zealand. Twitter: @kiwiandroiddev