lightbulb_outline Please take our October 2018 developer survey. Start survey

Introduce first-time users to your app

To show a first-time user how to get the most from your app, present onboarding information at app startup. Here are some examples of onboarding information:

  • Present detailed information on which channels are available when a user first accesses a channel app.
  • Call attention to noteworthy features in your app.
  • Illustrate any required or recommended steps that users should take when using the app for the first time.

The v17 Leanback support library provides the OnboardingFragment class for presenting first-time user information. This lesson describes how to use the OnboardingFragment class to present introductory information that is shown when the app launches for the first time. OnboardingFragment uses TV UI best practices to present the information in a way that matches TV UI styles, and is easy to navigate on TV devices.

Figure 1. An example OnboardingFragment.

Your OnboardingFragment should not contain UI elements that require user input, such as buttons and fields. Similarly, it should not be used as a UI element for a task the user will do regularly. If you need to present a multi-page UI that requires user input, consider using a GuidedStepFragment.

Add an OnboardingFragment

To add an OnboardingFragment to your app, implement a class that extends the OnboardingFragment class. Add this fragment to an activity, either via the activity's layout XML, or programmatically. Make sure the activity or fragment is using a theme derived from Theme_Leanback_Onboarding, as described in Customize themes.

In the onCreate() method of your app's main activity, call startActivity() with an Intent that points to your OnboardingFragment's parent activity. This ensures that your OnboardingFragment appears as soon as your app starts.

To ensure that the OnboardingFragment only appears the first time that the user starts your app, use a SharedPreferences object to track whether the user has already viewed the OnboardingFragment. Define a boolean value that changes to true when the user finishes viewing the OnboardingFragment. Check this value in your main activity’s onCreate(), and only start the OnboardingFragment parent activity if the value is false. The following example shows an override of onCreate() that checks for a SharedPreferences value and, if not set to true, calls startActivity() to show the OnboardingFragment:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    PreferenceManager.getDefaultSharedPreferences(this).apply {
        // Check if we need to display our OnboardingFragment
        if (!getBoolean(MyOnboardingFragment.COMPLETED_ONBOARDING_PREF_NAME, false)) {
            // The user hasn't seen the OnboardingFragment yet, so show it
            startActivity(Intent(this@OnboardingActivity, OnboardingActivity::class.java))
        }
    }
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    SharedPreferences sharedPreferences =
            PreferenceManager.getDefaultSharedPreferences(this);
    // Check if we need to display our OnboardingFragment
    if (!sharedPreferences.getBoolean(
            MyOnboardingFragment.COMPLETED_ONBOARDING_PREF_NAME, false)) {
        // The user hasn't seen the OnboardingFragment yet, so show it
        startActivity(new Intent(this, OnboardingActivity.class));
    }
}

After the user views the OnboardingFragment, mark it as viewed using the SharedPreferences object. To do this, in your OnboardingFragment, override onFinishFragment() and set your SharedPreferences value to true, as shown in the following example:

Kotlin

override fun onFinishFragment() {
    super.onFinishFragment()
    // User has seen OnboardingFragment, so mark our SharedPreferences
    // flag as completed so that we don't show our OnboardingFragment
    // the next time the user launches the app.
    PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
        putBoolean(COMPLETED_ONBOARDING_PREF_NAME, true)
        apply()
    }
}

Java

@Override
protected void onFinishFragment() {
    super.onFinishFragment();
    // User has seen OnboardingFragment, so mark our SharedPreferences
    // flag as completed so that we don't show our OnboardingFragment
    // the next time the user launches the app.
    SharedPreferences.Editor sharedPreferencesEditor =
            PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
    sharedPreferencesEditor.putBoolean(
            COMPLETED_ONBOARDING_PREF_NAME, true);
    sharedPreferencesEditor.apply();
}

Add OnboardingFragment pages

After you add your OnboardingFragment, you need to define the onboarding pages. An OnboardingFragment displays content in a series of ordered pages. Each page can have a title, description, and several sub-views that can contain images or animations.

Figure 2. OnboardingFragment page elements.

Figure 2 shows an example page with callouts marking customizable page elements that your OnboardingFragment can provide. The page elements are:

  1. The page title.
  2. The page description.
  3. The page content view, in this case a simple green checkmark in a grey box. This view is optional. Use this view to illustrate page details such as a screenshot that highlights the app feature that the page describes.
  4. The page background view, in this case a simple blue gradient. This view always renders behind other views on the page. This view is optional.
  5. The page foreground view, in this case a logo. This view always renders in front of all other views on the page. This view is optional.

Initialize page information when your OnboardingFragment is first created or attached to the parent activity, as the system requests page information when it creates the fragment's view. You can initialize page information in your class constructor or in an override of onAttach().

Override each of the following methods that provide page information to the system:

Override each of the following methods to provide optional sub-views used to display images or animations:

The system adds the View that you create to the page layout. The following example overrides onCreateContentView() and returns an ImageView:

Kotlin

private lateinit var mContentView: ImageView
...
override fun onCreateContentView(inflater: LayoutInflater?, container: ViewGroup?): View? {
    return ImageView(context).apply {
        scaleType = ImageView.ScaleType.CENTER_INSIDE
        setImageResource(R.drawable.onboarding_content_view)
        setPadding(0, 32, 0, 32)
        mContentView = this
    }
}

Java

private ImageView mContentView;
...
@Override
protected View onCreateContentView(LayoutInflater inflater, ViewGroup container) {
    mContentView = new ImageView(getContext());
    mContentView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
    mContentView.setImageResource(R.drawable.onboarding_content_view);
    mContentView.setPadding(0, 32, 0, 32);
    return mContentView;
}

Add an initial logo screen

Your OnboardingFragment can start with an optional logo screen that introduces your app. If you want to display a Drawable as your logo screen, in your OnboardingFragment's onCreate() method, call setLogoResourceId() with the ID of your Drawable. The system will fade in and briefly display this Drawable, and then fade out the Drawable before displaying the first page of your OnboardingFragment.

If you want to provide a custom animation for your logo screen, instead of calling setLogoResourceId(), override onCreateLogoAnimation() and return an Animator object that renders your custom animation, as shown in the following example:

Kotlin

public override fun onCreateLogoAnimation(): Animator =
        AnimatorInflater.loadAnimator(context, R.animator.onboarding_logo_screen_animation)

Java

@Override
public Animator onCreateLogoAnimation() {
    return AnimatorInflater.loadAnimator(getContext(),
            R.animator.onboarding_logo_screen_animation);
}

Customize page animations

The system uses default animations when displaying the first page of your OnboardingFragment and when the user navigates to a different page. You can customize these animations by overriding methods in your OnboardingFragment.

To customize the animation that appears on your first page, override onCreateEnterAnimation() and return an Animator. The following example creates an Animator that scales the content view horizontally:

Kotlin

override fun onCreateEnterAnimation(): Animator =
    ObjectAnimator.ofFloat(mContentView, View.SCALE_X, 0.2f, 1.0f)
            .setDuration(ANIMATION_DURATION)

Java

@Override
protected Animator onCreateEnterAnimation() {
    Animator startAnimator = ObjectAnimator.ofFloat(mContentView,
            View.SCALE_X, 0.2f, 1.0f).setDuration(ANIMATION_DURATION);
    return startAnimator;
}

To customize the animation used when the user navigates to a different page, override onPageChanged(). In your onPageChanged() method, create Animators that remove the previous page and display the next page, add these to an AnimatorSet, and play the set. The following example uses a fade-out animation to remove the previous page, updates the content view image, and uses a fade-in animation to display the next page:

Kotlin

override fun onPageChanged(newPage: Int, previousPage: Int) {
    // Create a fade-out animation used to fade out previousPage and, once
    // done, swaps the contentView image with the next page's image.
    val fadeOut = ObjectAnimator.ofFloat(mContentView, View.ALPHA, 1.0f, 0.0f)
            .setDuration(ANIMATION_DURATION)
            .apply {
                addListener(object : AnimatorListenerAdapter() {

                    override fun onAnimationEnd(animation: Animator) {
                        mContentView.setImageResource(pageImages[newPage])
                    }
                })
            }
    // Create a fade-in animation used to fade in nextPage
    val fadeIn = ObjectAnimator.ofFloat(mContentView, View.ALPHA, 0.0f, 1.0f)
            .setDuration(ANIMATION_DURATION)
    // Create AnimatorSet with our fade-out and fade-in animators, and start it
    AnimatorSet().apply {
        playSequentially(fadeOut, fadeIn)
        start()
    }
}

Java

@Override
protected void onPageChanged(final int newPage, int previousPage) {
    // Create a fade-out animation used to fade out previousPage and, once
    // done, swaps the contentView image with the next page's image.
    Animator fadeOut = ObjectAnimator.ofFloat(mContentView,
            View.ALPHA, 1.0f, 0.0f).setDuration(ANIMATION_DURATION);
    fadeOut.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            mContentView.setImageResource(pageImages[newPage]);
        }
    });
    // Create a fade-in animation used to fade in nextPage
    Animator fadeIn = ObjectAnimator.ofFloat(mContentView,
            View.ALPHA, 0.0f, 1.0f).setDuration(ANIMATION_DURATION);
    // Create AnimatorSet with our fade-out and fade-in animators, and start it
    AnimatorSet set = new AnimatorSet();
    set.playSequentially(fadeOut, fadeIn);
    set.start();
}

For more details about how to create Animators and AnimatorSets, see Property animations.

Customize themes

Any OnboardingFragment implementation must use either the Theme_Leanback_Onboarding theme or a theme that inherits from Theme_Leanback_Onboarding. Set the theme for your OnboardingFragment by doing one of the following:

  • Set the OnboardingFragment's parent activity to use the desired theme. The following example shows how to set an activity to use Theme_Leanback_Onboarding in the app manifest:
    <activity
       android:name=".OnboardingActivity"
       android:enabled="true"
       android:exported="true"
       android:theme="@style/Theme.Leanback.Onboarding">
    </activity>
    
  • Set the theme in the parent activity by using the LeanbackOnboardingTheme_onboardingTheme attribute in a custom activity theme. Point this attribute to another custom theme that only the OnboardingFragment objects in your activity use. Use this approach if your activity already uses a custom theme and you don't want to apply OnboardingFragment styles to other views in the activity.
  • Override onProvideTheme() and return the desired theme. Use this approach if multiple activities use your OnboardingFragment or if the parent activity can't use the desired theme. The following example overrides onProvideTheme() and returns Theme_Leanback_Onboarding:

    Kotlin

    override fun onProvideTheme(): Int = R.style.Theme_Leanback_Onboarding
    

    Java

    @Override
    public int onProvideTheme() {
       return R.style.Theme_Leanback_Onboarding;
    }