向首次用户介绍您的应用

使用 Compose 更好地构建
使用 Jetpack Compose for Android TV OS,以最少的代码创建精美的界面。

为了向首次用户展示如何充分利用您的应用,请在应用启动时显示新手入门信息。以下是新手入门信息的一些示例

  • 当用户首次访问频道应用时,显示有关可用频道的详细信息。
  • 突出显示应用中值得注意的功能。
  • 说明用户首次使用应用时需要或建议采取的任何步骤。

androidx.leanback 库提供了 OnboardingSupportFragment 类,用于呈现首次用户的信息。本指南介绍如何使用 OnboardingSupportFragment 类呈现应用首次启动时显示的介绍性信息。

OnboardingSupportFragment 使用电视界面最佳实践来呈现信息,使其与电视界面样式匹配,并且在电视设备上易于导航。

图 1. OnboardingSupportFragment 示例。

OnboardingSupportFragment 并非适用于所有用例。当您需要包含需要用户输入的界面元素(例如按钮和字段)时,请勿使用 OnboardingSupportFragment。此外,请勿将 OnboardingSupportFragment 用于用户会定期执行的任务。最后,如果您需要呈现需要用户输入的包含多页的界面,请考虑使用 GuidedStepSupportFragment

添加 OnboardingSupportFragment

要将 OnboardingSupportFragment 添加到您的应用中,请实现一个扩展 OnboardingSupportFragment 类的类。使用 Activity 的布局 XML 或以编程方式将此 fragment 添加到 Activity。确保 Activity 或 fragment 使用派生自 Theme_Leanback_Onboarding 的主题,如自定义主题部分所述。

在应用的 MainActivity 的 onCreate() 方法中,调用 startActivity() 并传入指向 OnboardingSupportFragment 父 Activity 的 Intent。这有助于确保您的 OnboardingSupportFragment 在应用启动时立即显示。

为帮助确保 OnboardingSupportFragment 仅在用户首次启动您的应用时显示,请使用 SharedPreferences 对象来跟踪用户是否已查看过 OnboardingSupportFragment。定义一个布尔值,当用户完成查看 OnboardingSupportFragment 时,该值变为 true。在您的 MainActivity 的 onCreate() 方法中检查此值,并且仅当该值为 false 时才启动 OnboardingSupportFragment 父 Activity。

以下示例展示了 onCreate() 的一个重写,该重写检查 SharedPreferences 值,如果该值未设置为 true,则调用 startActivity() 来显示 OnboardingSupportFragment

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 OnboardingSupportFragment
        if (!getBoolean(MyOnboardingSupportFragment.COMPLETED_ONBOARDING_PREF_NAME, false)) {
            // The user hasn't seen the OnboardingSupportFragment 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 OnboardingSupportFragment
    if (!sharedPreferences.getBoolean(
            MyOnboardingSupportFragment.COMPLETED_ONBOARDING_PREF_NAME, false)) {
        // The user hasn't seen the OnboardingSupportFragment yet, so show it
        startActivity(new Intent(this, OnboardingActivity.class));
    }
}

用户查看 OnboardingSupportFragment 后,使用 SharedPreferences 对象将其标记为已查看。为此,请在您的 OnboardingSupportFragment 中重写 onFinishFragment() 并将您的 SharedPreferences 值设置为 true,如以下示例所示

Kotlin

override fun onFinishFragment() {
    super.onFinishFragment()
    // User has seen OnboardingSupportFragment, so mark our SharedPreferences
    // flag as completed so that we don't show our OnboardingSupportFragment
    // 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 OnboardingSupportFragment, so mark our SharedPreferences
    // flag as completed so that we don't show our OnboardingSupportFragment
    // the next time the user launches the app
    SharedPreferences.Editor sharedPreferencesEditor =
            PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
    sharedPreferencesEditor.putBoolean(
            COMPLETED_ONBOARDING_PREF_NAME, true);
    sharedPreferencesEditor.apply();
}

添加 OnboardingSupportFragment 页面

一个 OnboardingSupportFragment 以一系列有序页面的形式显示内容。添加 OnboardingSupportFragment 后,您需要定义新手入门页面。每个页面都可以包含标题、说明以及可以包含图片或动画的多个子视图。

图 2. OnboardingSupportFragment 页面元素。

图 2 显示了一个示例页面,其中标注了您的 OnboardingSupportFragment 可提供的可自定义页面元素。页面元素包括

  1. 页面标题。
  2. 页面说明。
  3. 页面内容视图,在本例中是一个灰色框中的简单绿色对勾。此视图是可选的。使用此视图可说明页面详细信息。例如,您可以包含一个突出显示页面所描述的应用功能的屏幕截图。
  4. 页面背景视图,在本例中是一个简单的蓝色渐变。此视图始终在页面上其他视图的后面呈现。此视图是可选的。
  5. 页面前景视图,在本例中是一个徽标。此视图始终在页面上所有其他视图的前面呈现。此视图是可选的。

在您的 OnboardingSupportFragment 首次创建或附加到父 Activity 时初始化页面信息,因为系统在创建 fragment 视图时会请求页面信息。您可以在类构造函数中或重写 onAttach() 时初始化页面信息。

重写以下每个方法,这些方法向系统提供页面信息

重写以下每个方法,以提供可选的子视图来显示图片或动画

系统将您创建的 View 添加到页面布局中。以下示例重写 onCreateContentView() 并返回一个 ImageView

Kotlin

private lateinit var contentView: 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)
        contentView = this
    }
}

Java

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

添加初始徽标屏幕

您的 OnboardingSupportFragment 可以从一个可选的徽标屏幕开始,该屏幕介绍您的应用。如果您想将 Drawable 作为徽标屏幕显示,请在您的 OnboardingSupportFragmentonCreate() 方法中调用 setLogoResourceId() 并传入您的 Drawable 的 ID。系统会淡入并短暂显示 Drawable,然后在显示 OnboardingSupportFragment 的第一页之前淡出 Drawable

如果您想为徽标屏幕提供自定义动画,而不是调用 setLogoResourceId(),请重写 onCreateLogoAnimation() 并返回一个呈现自定义动画的 Animator 对象,如以下示例所示

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);
}

自定义页面动画

当显示 OnboardingSupportFragment 的第一页以及用户导航到不同页面时,系统会使用默认动画。您可以通过重写 OnboardingSupportFragment 中的方法来自定义这些动画。

要自定义第一页上显示的动画,请重写 onCreateEnterAnimation() 并返回一个 Animator。以下示例创建一个 Animator,用于水平缩放内容视图

Kotlin

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

Java

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

要自定义用户导航到不同页面时使用的动画,请重写 onPageChanged()。在您的 onPageChanged() 方法中,创建移除上一页并显示下一页的 Animator 对象,将这些对象添加到 AnimatorSet 中,然后播放该集合。以下示例使用淡出动画移除上一页,更新内容视图图片,并使用淡入动画显示下一页

Kotlin

override fun onPageChanged(newPage: Int, previousPage: Int) {
    // Create a fade-out animation for previousPage and, once
    // done, swap 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 for nextPage
    val fadeIn = ObjectAnimator.ofFloat(mContentView, View.ALPHA, 0.0f, 1.0f)
            .setDuration(ANIMATION_DURATION)
    // Create AnimatorSet with 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 for previousPage and, once
    // done, swap 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 for nextPage
    Animator fadeIn = ObjectAnimator.ofFloat(mContentView,
            View.ALPHA, 0.0f, 1.0f).setDuration(ANIMATION_DURATION);
    // Create AnimatorSet with fade-out and fade-in animators and start it
    AnimatorSet set = new AnimatorSet();
    set.playSequentially(fadeOut, fadeIn);
    set.start();
}

有关如何创建 Animator 对象和 AnimatorSet 对象的更多详细信息,请参阅属性动画概览

自定义主题

任何 OnboardingSupportFragment 实现都必须使用 Theme_Leanback_Onboarding 主题或继承自 Theme_Leanback_Onboarding 的主题。通过以下任一方式为您的 OnboardingSupportFragment 设置主题

  • OnboardingSupportFragment 的父 Activity 设置为使用所需主题。以下示例展示了如何在应用清单中将 Activity 设置为使用 Theme_Leanback_Onboarding
    <activity
       android:name=".OnboardingActivity"
       android:enabled="true"
       android:exported="true"
       android:theme="@style/Theme.Leanback.Onboarding">
    </activity>
  • 通过在自定义 Activity 主题中使用 LeanbackOnboardingTheme_onboardingTheme 属性来设置父 Activity 中的主题。将此属性指向另一个仅供您的 Activity 中的 OnboardingSupportFragment 对象使用的自定义主题。如果您的 Activity 已使用自定义主题且您不想将 OnboardingSupportFragment 样式应用于 Activity 中的其他视图,请使用此方法。
  • 重写 onProvideTheme() 并返回所需主题。如果多个 Activity 使用您的 OnboardingSupportFragment,或者父 Activity 无法使用所需主题,请使用此方法。以下示例重写 onProvideTheme() 并返回 Theme_Leanback_Onboarding

    Kotlin

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

    Java

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