java.lang.Object

androidx.fragment.app.Fragment

↳androidx.preference.PreferenceFragmentCompat

Subclasses:

LeanbackPreferenceFragmentCompat, BaseLeanbackPreferenceFragmentCompat

Gradle dependencies

compile group: 'androidx.preference', name: 'preference', version: '1.2.0'

  • groupId: androidx.preference
  • artifactId: preference
  • version: 1.2.0

Artifact androidx.preference:preference:1.2.0 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.preference:preference com.android.support:preference-v7

Androidx class mapping:

androidx.preference.PreferenceFragmentCompat android.support.v7.preference.PreferenceFragmentCompat

Overview

A PreferenceFragmentCompat is the entry point to using the Preference library. This Fragment displays a hierarchy of Preference objects to the user. It also handles persisting values to the device. To retrieve an instance of android.content.SharedPreferences that the preference hierarchy in this fragment will use by default, call PreferenceManager with a context in the same package as this fragment.

You can define a preference hierarchy as an XML resource, or you can build a hierarchy in code. In both cases you need to use a PreferenceScreen as the root component in your hierarchy.

To inflate from XML, use the PreferenceFragmentCompat.setPreferencesFromResource(int, String). An example example XML resource is shown further down.

To build a hierarchy from code, use PreferenceManager.createPreferenceScreen(Context) to create the root PreferenceScreen. Once you have added other Preferences to this root screen with PreferenceGroup.addPreference(Preference), you then need to set the screen as the root screen in your hierarchy with PreferenceFragmentCompat.setPreferenceScreen(PreferenceScreen).

As a convenience, this fragment implements a click listener for any preference in the current hierarchy, see PreferenceFragmentCompat.onPreferenceTreeClick(Preference).

Developer Guides

For more information about building a settings screen using the AndroidX Preference library, see Settings.

Sample Code

The following sample code shows a simple settings screen using an XML resource. The XML resource is as follows:

frameworks/support/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml preferences

The fragment that loads the XML resource is as follows:

frameworks/support/samples/SupportPreferenceDemos/src/main/java/com/example/androidx/preference/Preferences.java preferences

Summary

Fields
public static final java.lang.StringARG_PREFERENCE_ROOT

Fragment argument used to specify the tag of the desired root PreferenceScreen object.

from FragmentmPreviousWho
Constructors
publicPreferenceFragmentCompat()

Methods
public voidaddPreferencesFromResource(int preferencesResId)

Inflates the given XML resource and adds the preference hierarchy to the current preference hierarchy.

public PreferencefindPreference(java.lang.CharSequence key)

public FragmentgetCallbackFragment()

A wrapper for getParentFragment which is v17+.

public final RecyclerViewgetListView()

public PreferenceManagergetPreferenceManager()

Returns the PreferenceManager used by this fragment.

public PreferenceScreengetPreferenceScreen()

Gets the root of the preference hierarchy that this fragment is showing.

protected voidonBindPreferences()

Used by Settings.

public voidonCreate(Bundle savedInstanceState)

Called to do initial creation of a fragment.

protected RecyclerView.AdapteronCreateAdapter(PreferenceScreen preferenceScreen)

Creates the root adapter.

public RecyclerView.LayoutManageronCreateLayoutManager()

Called from PreferenceFragmentCompat.onCreateRecyclerView(LayoutInflater, ViewGroup, Bundle) to create the for the created RecyclerView.

public abstract voidonCreatePreferences(Bundle savedInstanceState, java.lang.String rootKey)

Called during PreferenceFragmentCompat.onCreate(Bundle) to supply the preferences for this fragment.

public RecyclerViewonCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)

Creates the RecyclerView used to display the preferences.

public ViewonCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

Called to have the fragment instantiate its user interface view.

public voidonDestroyView()

Called when the view previously created by Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) has been detached from the fragment.

public voidonDisplayPreferenceDialog(Preference preference)

Called when a preference in the tree requests to display a dialog.

public voidonNavigateToScreen(PreferenceScreen preferenceScreen)

Called by PreferenceScreen.onClick() in order to navigate to a new screen of preferences.

public booleanonPreferenceTreeClick(Preference preference)

public voidonSaveInstanceState(Bundle outState)

Called to ask the fragment to save its current dynamic state, so it can later be reconstructed in a new instance if its process is restarted.

public voidonStart()

Called when the Fragment is visible to the user.

public voidonStop()

Called when the Fragment is no longer started.

protected voidonUnbindPreferences()

Used by Settings.

public voidonViewCreated(View view, Bundle savedInstanceState)

Called immediately after Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) has returned, but before any saved state has been restored in to the view.

public voidscrollToPreference(Preference preference)

public voidscrollToPreference(java.lang.String key)

public voidsetDivider(Drawable divider)

Sets the Drawable that will be drawn between each item in the list.

public voidsetDividerHeight(int height)

Sets the height of the divider that will be drawn between each item in the list.

public voidsetPreferenceScreen(PreferenceScreen preferenceScreen)

Sets the root of the preference hierarchy that this fragment is showing.

public voidsetPreferencesFromResource(int preferencesResId, java.lang.String key)

Inflates the given XML resource and replaces the current preference hierarchy (if any) with the preference hierarchy rooted at key.

from Fragmentdump, equals, getActivity, getAllowEnterTransitionOverlap, getAllowReturnTransitionOverlap, getArguments, getChildFragmentManager, getContext, getDefaultViewModelCreationExtras, getDefaultViewModelProviderFactory, getEnterTransition, getExitTransition, getFragmentManager, getHost, getId, getLayoutInflater, getLayoutInflater, getLifecycle, getLoaderManager, getParentFragment, getParentFragmentManager, getReenterTransition, getResources, getRetainInstance, getReturnTransition, getSavedStateRegistry, getSharedElementEnterTransition, getSharedElementReturnTransition, getString, getString, getTag, getTargetFragment, getTargetRequestCode, getText, getUserVisibleHint, getView, getViewLifecycleOwner, getViewLifecycleOwnerLiveData, getViewModelStore, hashCode, hasOptionsMenu, instantiate, instantiate, isAdded, isDetached, isHidden, isInLayout, isMenuVisible, isRemoving, isResumed, isStateSaved, isVisible, onActivityCreated, onActivityResult, onAttach, onAttach, onAttachFragment, onConfigurationChanged, onContextItemSelected, onCreateAnimation, onCreateAnimator, onCreateContextMenu, onCreateOptionsMenu, onDestroy, onDestroyOptionsMenu, onDetach, onGetLayoutInflater, onHiddenChanged, onInflate, onInflate, onLowMemory, onMultiWindowModeChanged, onOptionsItemSelected, onOptionsMenuClosed, onPause, onPictureInPictureModeChanged, onPrepareOptionsMenu, onPrimaryNavigationFragmentChanged, onRequestPermissionsResult, onResume, onViewStateRestored, postponeEnterTransition, postponeEnterTransition, registerForActivityResult, registerForActivityResult, registerForContextMenu, requestPermissions, requireActivity, requireArguments, requireContext, requireFragmentManager, requireHost, requireParentFragment, requireView, setAllowEnterTransitionOverlap, setAllowReturnTransitionOverlap, setArguments, setEnterSharedElementCallback, setEnterTransition, setExitSharedElementCallback, setExitTransition, setHasOptionsMenu, setInitialSavedState, setMenuVisibility, setReenterTransition, setRetainInstance, setReturnTransition, setSharedElementEnterTransition, setSharedElementReturnTransition, setTargetFragment, setUserVisibleHint, shouldShowRequestPermissionRationale, startActivity, startActivity, startActivityForResult, startActivityForResult, startIntentSenderForResult, startPostponedEnterTransition, toString, unregisterForContextMenu
from java.lang.Objectclone, finalize, getClass, notify, notifyAll, wait, wait, wait

Fields

public static final java.lang.String ARG_PREFERENCE_ROOT

Fragment argument used to specify the tag of the desired root PreferenceScreen object.

Constructors

public PreferenceFragmentCompat()

Methods

public void onCreate(Bundle savedInstanceState)

Called to do initial creation of a fragment. This is called after Fragment.onAttach(Activity) and before Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle).

Note that this can be called while the fragment's activity is still in the process of being created. As such, you can not rely on things like the activity's content view hierarchy being initialized at this point. If you want to do work once the activity itself is created, add a LifecycleObserver on the activity's Lifecycle, removing it when it receives the callback.

Any restored child fragments will be created before the base Fragment.onCreate method returns.

Parameters:

savedInstanceState: If the fragment is being re-created from a previous saved state, this is the state.

public abstract void onCreatePreferences(Bundle savedInstanceState, java.lang.String rootKey)

Called during PreferenceFragmentCompat.onCreate(Bundle) to supply the preferences for this fragment. Subclasses are expected to call PreferenceFragmentCompat.setPreferenceScreen(PreferenceScreen) either directly or via helper methods such as PreferenceFragmentCompat.addPreferencesFromResource(int).

Parameters:

savedInstanceState: If the fragment is being re-created from a previous saved state, this is the state.
rootKey: If non-null, this preference fragment should be rooted at the PreferenceScreen with this key.

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

Called to have the fragment instantiate its user interface view. This is optional, and non-graphical fragments can return null. This will be called between Fragment.onCreate(Bundle) and Fragment.onViewCreated(View, Bundle).

A default View can be returned by calling Fragment.Fragment(int) in your constructor. Otherwise, this method returns null.

It is recommended to only inflate the layout in this method and move logic that operates on the returned View to Fragment.onViewCreated(View, Bundle).

If you return a View from here, you will later be called in Fragment.onDestroyView() when the view is being released.

Parameters:

inflater: The LayoutInflater object that can be used to inflate any views in the fragment,
container: If non-null, this is the parent view that the fragment's UI should be attached to. The fragment should not add the view itself, but this can be used to generate the LayoutParams of the view.
savedInstanceState: If non-null, this fragment is being re-constructed from a previous saved state as given here.

Returns:

Return the View for the fragment's UI, or null.

public void setDivider(Drawable divider)

Sets the Drawable that will be drawn between each item in the list.

Note: If the drawable does not have an intrinsic height, you should also call PreferenceFragmentCompat.setDividerHeight(int).

Parameters:

divider: The drawable to use

public void setDividerHeight(int height)

Sets the height of the divider that will be drawn between each item in the list. Calling this will override the intrinsic height as set by PreferenceFragmentCompat.setDivider(Drawable).

Parameters:

height: The new height of the divider in pixels

public void onViewCreated(View view, Bundle savedInstanceState)

Called immediately after Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) has returned, but before any saved state has been restored in to the view. This gives subclasses a chance to initialize themselves once they know their view hierarchy has been completely created. The fragment's view hierarchy is not however attached to its parent at this point.

Parameters:

view: The View returned by Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle).
savedInstanceState: If non-null, this fragment is being re-constructed from a previous saved state as given here.

public void onStart()

Called when the Fragment is visible to the user. This is generally tied to of the containing Activity's lifecycle.

public void onStop()

Called when the Fragment is no longer started. This is generally tied to of the containing Activity's lifecycle.

public void onDestroyView()

Called when the view previously created by Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) has been detached from the fragment. The next time the fragment needs to be displayed, a new view will be created. This is called after Fragment.onStop() and before Fragment.onDestroy(). It is called regardless of whether Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) returned a non-null view. Internally it is called after the view's state has been saved but before it has been removed from its parent.

public void onSaveInstanceState(Bundle outState)

Called to ask the fragment to save its current dynamic state, so it can later be reconstructed in a new instance if its process is restarted. If a new instance of the fragment later needs to be created, the data you place in the Bundle here will be available in the Bundle given to Fragment.onCreate(Bundle), Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle), and Fragment.onViewCreated(View, Bundle).

This corresponds to and most of the discussion there applies here as well. Note however: this method may be called at any time before Fragment.onDestroy(). There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.

Parameters:

outState: Bundle in which to place your saved state.

public PreferenceManager getPreferenceManager()

Returns the PreferenceManager used by this fragment.

Returns:

The PreferenceManager used by this fragment

public PreferenceScreen getPreferenceScreen()

Gets the root of the preference hierarchy that this fragment is showing.

Returns:

The PreferenceScreen that is the root of the preference hierarchy

public void setPreferenceScreen(PreferenceScreen preferenceScreen)

Sets the root of the preference hierarchy that this fragment is showing.

Parameters:

preferenceScreen: The root PreferenceScreen of the preference hierarchy

public void addPreferencesFromResource(int preferencesResId)

Inflates the given XML resource and adds the preference hierarchy to the current preference hierarchy.

Parameters:

preferencesResId: The XML resource ID to inflate

public void setPreferencesFromResource(int preferencesResId, java.lang.String key)

Inflates the given XML resource and replaces the current preference hierarchy (if any) with the preference hierarchy rooted at key.

Parameters:

preferencesResId: The XML resource ID to inflate
key: The preference key of the PreferenceScreen to use as the root of the preference hierarchy, or null to use the root PreferenceScreen.

public boolean onPreferenceTreeClick(Preference preference)

public void onNavigateToScreen(PreferenceScreen preferenceScreen)

Called by PreferenceScreen.onClick() in order to navigate to a new screen of preferences. Calls PreferenceFragmentCompat.OnPreferenceStartScreenCallback.onPreferenceStartScreen(PreferenceFragmentCompat, PreferenceScreen) if the target fragment or containing activity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback.

Parameters:

preferenceScreen: The PreferenceScreen to navigate to

public Preference findPreference(java.lang.CharSequence key)

protected void onBindPreferences()

Used by Settings.

protected void onUnbindPreferences()

Used by Settings.

public final RecyclerView getListView()

public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)

Creates the RecyclerView used to display the preferences. Subclasses may override this to return a customized RecyclerView.

Parameters:

inflater: The LayoutInflater object that can be used to inflate the RecyclerView.
parent: The parent ViewGroup that the RecyclerView will be attached to. This method should not add the view itself, but this can be used to generate the layout params of the view.
savedInstanceState: If non-null, this view is being re-constructed from a previous saved state as given here.

Returns:

A new RecyclerView object to be placed into the view hierarchy

public RecyclerView.LayoutManager onCreateLayoutManager()

Called from PreferenceFragmentCompat.onCreateRecyclerView(LayoutInflater, ViewGroup, Bundle) to create the for the created RecyclerView.

Returns:

A new instance

protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen)

Creates the root adapter.

Parameters:

preferenceScreen: The PreferenceScreen object to create the adapter for

Returns:

An adapter that contains the preferences contained in this PreferenceScreen

public void onDisplayPreferenceDialog(Preference preference)

Called when a preference in the tree requests to display a dialog. Subclasses should override this method to display custom dialogs or to handle dialogs for custom preference classes.

Parameters:

preference: The Preference object requesting the dialog

public Fragment getCallbackFragment()

A wrapper for getParentFragment which is v17+. Used by the leanback preference lib.

Returns:

The Fragment to possibly use as a callback

public void scrollToPreference(java.lang.String key)

public void scrollToPreference(Preference preference)

Source

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.preference;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.XmlRes;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

/**
 * A PreferenceFragmentCompat is the entry point to using the Preference library. This
 * {@link Fragment} displays a hierarchy of {@link Preference} objects to the user. It also
 * handles persisting values to the device. To retrieve an instance of
 * {@link android.content.SharedPreferences} that the preference hierarchy in this fragment will
 * use by default, call
 * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)} with a context
 * in the same package as this fragment.
 *
 * <p>You can define a preference hierarchy as an XML resource, or you can build a hierarchy in
 * code. In both cases you need to use a {@link PreferenceScreen} as the root component in your
 * hierarchy.
 *
 * <p>To inflate from XML, use the {@link #setPreferencesFromResource(int, String)}. An example
 * example XML resource is shown further down.
 *
 * <p>To build a hierarchy from code, use
 * {@link PreferenceManager#createPreferenceScreen(Context)} to create the root
 * {@link PreferenceScreen}. Once you have added other {@link Preference}s to this root screen
 * with {@link PreferenceScreen#addPreference(Preference)}, you then need to set the screen as
 * the root screen in your hierarchy with {@link #setPreferenceScreen(PreferenceScreen)}.
 *
 * <p>As a convenience, this fragment implements a click listener for any preference in the
 * current hierarchy, see {@link #onPreferenceTreeClick(Preference)}.
 *
 * <div class="special reference"> <h3>Developer Guides</h3> <p>For more information about
 * building a settings screen using the AndroidX Preference library, see
 * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.</p> </div>
 *
 * <a name="SampleCode"></a>
 * <h3>Sample Code</h3>
 *
 * <p>The following sample code shows a simple settings screen using an XML resource. The XML
 * resource is as follows:</p>
 *
 * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml preferences}
 *
 * <p>The fragment that loads the XML resource is as follows:</p>
 *
 * {@sample frameworks/support/samples/SupportPreferenceDemos/src/main/java/com/example/androidx/preference/Preferences.java preferences}
 *
 * @see Preference
 * @see PreferenceScreen
 */
public abstract class PreferenceFragmentCompat extends Fragment implements
        PreferenceManager.OnPreferenceTreeClickListener,
        PreferenceManager.OnDisplayPreferenceDialogListener,
        PreferenceManager.OnNavigateToScreenListener,
        DialogPreference.TargetFragment {

    private static final String TAG = "PreferenceFragment";

    /**
     * Fragment argument used to specify the tag of the desired root {@link PreferenceScreen}
     * object.
     */
    public static final String ARG_PREFERENCE_ROOT =
            "androidx.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";

    private static final String PREFERENCES_TAG = "android:preferences";

    private static final String DIALOG_FRAGMENT_TAG =
            "androidx.preference.PreferenceFragment.DIALOG";

    private static final int MSG_BIND_PREFERENCES = 1;

    private final DividerDecoration mDividerDecoration = new DividerDecoration();
    private PreferenceManager mPreferenceManager;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    RecyclerView mList;
    private boolean mHavePrefs;
    private boolean mInitDone;
    private int mLayoutResId = R.layout.preference_list_fragment;
    private Runnable mSelectPreferenceRunnable;

    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_BIND_PREFERENCES:
                    bindPreferences();
                    break;
            }
        }
    };

    final private Runnable mRequestFocus = new Runnable() {
        @Override
        public void run() {
            mList.focusableViewAvailable(mList);
        }
    };

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final TypedValue tv = new TypedValue();
        requireContext().getTheme().resolveAttribute(R.attr.preferenceTheme, tv, true);
        int theme = tv.resourceId;
        if (theme == 0) {
            // Fallback to default theme.
            theme = R.style.PreferenceThemeOverlay;
        }
        requireContext().getTheme().applyStyle(theme, false);

        mPreferenceManager = new PreferenceManager(requireContext());
        mPreferenceManager.setOnNavigateToScreenListener(this);
        final Bundle args = getArguments();
        final String rootKey;
        if (args != null) {
            rootKey = getArguments().getString(ARG_PREFERENCE_ROOT);
        } else {
            rootKey = null;
        }
        onCreatePreferences(savedInstanceState, rootKey);
    }

    /**
     * Called during {@link #onCreate(Bundle)} to supply the preferences for this fragment.
     * Subclasses are expected to call {@link #setPreferenceScreen(PreferenceScreen)} either
     * directly or via helper methods such as {@link #addPreferencesFromResource(int)}.
     *
     * @param savedInstanceState If the fragment is being re-created from a previous saved state,
     *                           this is the state.
     * @param rootKey            If non-null, this preference fragment should be rooted at the
     *                           {@link PreferenceScreen} with this key.
     */
    public abstract void onCreatePreferences(@Nullable Bundle savedInstanceState,
            @Nullable String rootKey);

    @NonNull
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {

        TypedArray a = requireContext().obtainStyledAttributes(null,
                R.styleable.PreferenceFragmentCompat,
                R.attr.preferenceFragmentCompatStyle,
                0);

        mLayoutResId = a.getResourceId(R.styleable.PreferenceFragmentCompat_android_layout,
                mLayoutResId);

        final Drawable divider = a.getDrawable(
                R.styleable.PreferenceFragmentCompat_android_divider);
        final int dividerHeight = a.getDimensionPixelSize(
                R.styleable.PreferenceFragmentCompat_android_dividerHeight, -1);
        final boolean allowDividerAfterLastItem = a.getBoolean(
                R.styleable.PreferenceFragmentCompat_allowDividerAfterLastItem, true);

        a.recycle();

        final LayoutInflater themedInflater = inflater.cloneInContext(requireContext());

        final View view = themedInflater.inflate(mLayoutResId, container, false);

        final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER);
        if (!(rawListContainer instanceof ViewGroup)) {
            throw new IllegalStateException("Content has view with id attribute "
                    + "'android.R.id.list_container' that is not a ViewGroup class");
        }

        final ViewGroup listContainer = (ViewGroup) rawListContainer;

        final RecyclerView listView = onCreateRecyclerView(themedInflater, listContainer,
                savedInstanceState);
        if (listView == null) {
            throw new RuntimeException("Could not create RecyclerView");
        }

        mList = listView;

        listView.addItemDecoration(mDividerDecoration);
        setDivider(divider);
        if (dividerHeight != -1) {
            setDividerHeight(dividerHeight);
        }
        mDividerDecoration.setAllowDividerAfterLastItem(allowDividerAfterLastItem);

        // If mList isn't present in the view hierarchy, add it. mList is automatically inflated
        // on an Auto device so don't need to add it.
        if (mList.getParent() == null) {
            listContainer.addView(mList);
        }
        mHandler.post(mRequestFocus);

        return view;
    }

    /**
     * Sets the {@link Drawable} that will be drawn between each item in the list.
     *
     * <p><strong>Note:</strong> If the drawable does not have an intrinsic height, you should also
     * call {@link #setDividerHeight(int)}.
     *
     * @param divider The drawable to use
     * {@link android.R.attr#divider}
     */
    public void setDivider(@Nullable Drawable divider) {
        mDividerDecoration.setDivider(divider);
    }

    /**
     * Sets the height of the divider that will be drawn between each item in the list. Calling
     * this will override the intrinsic height as set by {@link #setDivider(Drawable)}.
     *
     * @param height The new height of the divider in pixels
     * {@link android.R.attr#dividerHeight}
     */
    public void setDividerHeight(int height) {
        mDividerDecoration.setDividerHeight(height);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (savedInstanceState != null) {
            Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG);
            if (container != null) {
                final PreferenceScreen preferenceScreen = getPreferenceScreen();
                if (preferenceScreen != null) {
                    preferenceScreen.restoreHierarchyState(container);
                }
            }
        }

        if (mHavePrefs) {
            bindPreferences();
            if (mSelectPreferenceRunnable != null) {
                mSelectPreferenceRunnable.run();
                mSelectPreferenceRunnable = null;
            }
        }

        mInitDone = true;
    }

    @Override
    public void onStart() {
        super.onStart();
        mPreferenceManager.setOnPreferenceTreeClickListener(this);
        mPreferenceManager.setOnDisplayPreferenceDialogListener(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        mPreferenceManager.setOnPreferenceTreeClickListener(null);
        mPreferenceManager.setOnDisplayPreferenceDialogListener(null);
    }

    @Override
    public void onDestroyView() {
        mHandler.removeCallbacks(mRequestFocus);
        mHandler.removeMessages(MSG_BIND_PREFERENCES);
        if (mHavePrefs) {
            unbindPreferences();
        }
        mList = null;
        super.onDestroyView();
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);

        final PreferenceScreen preferenceScreen = getPreferenceScreen();
        if (preferenceScreen != null) {
            Bundle container = new Bundle();
            preferenceScreen.saveHierarchyState(container);
            outState.putBundle(PREFERENCES_TAG, container);
        }
    }

    /**
     * Returns the {@link PreferenceManager} used by this fragment.
     *
     * @return The {@link PreferenceManager} used by this fragment
     */
    public PreferenceManager getPreferenceManager() {
        return mPreferenceManager;
    }

    /**
     * Gets the root of the preference hierarchy that this fragment is showing.
     *
     * @return The {@link PreferenceScreen} that is the root of the preference hierarchy
     */
    public PreferenceScreen getPreferenceScreen() {
        return mPreferenceManager.getPreferenceScreen();
    }

    /**
     * Sets the root of the preference hierarchy that this fragment is showing.
     *
     * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy
     */
    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
        if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
            onUnbindPreferences();
            mHavePrefs = true;
            if (mInitDone) {
                postBindPreferences();
            }
        }
    }

    /**
     * Inflates the given XML resource and adds the preference hierarchy to the current
     * preference hierarchy.
     *
     * @param preferencesResId The XML resource ID to inflate
     */
    public void addPreferencesFromResource(@XmlRes int preferencesResId) {
        requirePreferenceManager();

        setPreferenceScreen(mPreferenceManager.inflateFromResource(requireContext(),
                preferencesResId, getPreferenceScreen()));
    }

    /**
     * Inflates the given XML resource and replaces the current preference hierarchy (if any) with
     * the preference hierarchy rooted at {@code key}.
     *
     * @param preferencesResId The XML resource ID to inflate
     * @param key              The preference key of the {@link PreferenceScreen} to use as the
     *                         root of the preference hierarchy, or {@code null} to use the root
     *                         {@link PreferenceScreen}.
     */
    public void setPreferencesFromResource(@XmlRes int preferencesResId, @Nullable String key) {
        requirePreferenceManager();

        final PreferenceScreen xmlRoot = mPreferenceManager.inflateFromResource(requireContext(),
                preferencesResId, null);

        final Preference root;
        if (key != null) {
            root = xmlRoot.findPreference(key);
            if (!(root instanceof PreferenceScreen)) {
                throw new IllegalArgumentException("Preference object with key " + key
                        + " is not a PreferenceScreen");
            }
        } else {
            root = xmlRoot;
        }

        setPreferenceScreen((PreferenceScreen) root);
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("deprecation")
    @Override
    public boolean onPreferenceTreeClick(@NonNull Preference preference) {
        if (preference.getFragment() != null) {
            boolean handled = false;
            if (getCallbackFragment() instanceof OnPreferenceStartFragmentCallback) {
                handled = ((OnPreferenceStartFragmentCallback) getCallbackFragment())
                        .onPreferenceStartFragment(this, preference);
            }
            //  If the callback fragment doesn't handle OnPreferenceStartFragmentCallback, looks up
            //  its parent fragment in the hierarchy that implements the callback until the first
            //  one that returns true
            Fragment callbackFragment = this;
            while (!handled && callbackFragment != null) {
                if (callbackFragment instanceof OnPreferenceStartFragmentCallback) {
                    handled = ((OnPreferenceStartFragmentCallback) callbackFragment)
                            .onPreferenceStartFragment(this, preference);
                }
                callbackFragment = callbackFragment.getParentFragment();
            }
            if (!handled && getContext() instanceof OnPreferenceStartFragmentCallback) {
                handled = ((OnPreferenceStartFragmentCallback) getContext())
                        .onPreferenceStartFragment(this, preference);
            }
            // Check the Activity as well in case getContext was overridden to return something
            // other than the Activity.
            if (!handled && getActivity() instanceof OnPreferenceStartFragmentCallback) {
                handled = ((OnPreferenceStartFragmentCallback) getActivity())
                        .onPreferenceStartFragment(this, preference);
            }
            if (!handled) {
                Log.w(TAG,
                        "onPreferenceStartFragment is not implemented in the parent activity - "
                                + "attempting to use a fallback implementation. You should "
                                + "implement this method so that you can configure the new "
                                + "fragment that will be displayed, and set a transition between "
                                + "the fragments.");
                final FragmentManager fragmentManager = getParentFragmentManager();
                final Bundle args = preference.getExtras();
                final Fragment fragment = fragmentManager.getFragmentFactory().instantiate(
                        requireActivity().getClassLoader(), preference.getFragment());
                fragment.setArguments(args);
                fragment.setTargetFragment(this, 0);
                fragmentManager.beginTransaction()
                        // Attempt to replace this fragment in its root view - developers should
                        // implement onPreferenceStartFragment in their activity so that they can
                        // customize this behaviour and handle any transitions between fragments
                        .replace(((View) requireView().getParent()).getId(), fragment)
                        .addToBackStack(null)
                        .commit();
            }
            return true;
        }
        return false;
    }

    /**
     * Called by {@link PreferenceScreen#onClick()} in order to navigate to a new screen of
     * preferences. Calls
     * {@link PreferenceFragmentCompat.OnPreferenceStartScreenCallback#onPreferenceStartScreen}
     * if the target fragment or containing activity implements
     * {@link PreferenceFragmentCompat.OnPreferenceStartScreenCallback}.
     *
     * @param preferenceScreen The {@link PreferenceScreen} to navigate to
     */
    @Override
    public void onNavigateToScreen(@NonNull PreferenceScreen preferenceScreen) {
        boolean handled = false;
        if (getCallbackFragment() instanceof OnPreferenceStartScreenCallback) {
            handled = ((OnPreferenceStartScreenCallback) getCallbackFragment())
                    .onPreferenceStartScreen(this, preferenceScreen);
        }
        //  If the callback fragment doesn't handle OnPreferenceStartScreenCallback, looks up
        //  its parent fragment in the hierarchy that implements the callback until the first
        //  one that returns true
        Fragment callbackFragment = this;
        while (!handled && callbackFragment != null) {
            if (callbackFragment instanceof OnPreferenceStartScreenCallback) {
                handled = ((OnPreferenceStartScreenCallback) callbackFragment)
                        .onPreferenceStartScreen(this, preferenceScreen);
            }
            callbackFragment = callbackFragment.getParentFragment();
        }
        if (!handled && getContext() instanceof OnPreferenceStartScreenCallback) {
            handled = ((OnPreferenceStartScreenCallback) getContext())
                    .onPreferenceStartScreen(this, preferenceScreen);
        }
        // Check the Activity as well in case getContext was overridden to return something other
        // than the Activity.
        if (!handled && getActivity() instanceof OnPreferenceStartScreenCallback) {
            ((OnPreferenceStartScreenCallback) getActivity())
                    .onPreferenceStartScreen(this, preferenceScreen);
        }
    }

    @Override
    @SuppressWarnings("TypeParameterUnusedInFormals")
    @Nullable
    public <T extends Preference> T findPreference(@NonNull CharSequence key) {
        if (mPreferenceManager == null) {
            return null;
        }
        return mPreferenceManager.findPreference(key);
    }

    private void requirePreferenceManager() {
        if (mPreferenceManager == null) {
            throw new RuntimeException("This should be called after super.onCreate.");
        }
    }

    private void postBindPreferences() {
        if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
        mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void bindPreferences() {
        final PreferenceScreen preferenceScreen = getPreferenceScreen();
        if (preferenceScreen != null) {
            getListView().setAdapter(onCreateAdapter(preferenceScreen));
            preferenceScreen.onAttached();
        }
        onBindPreferences();
    }

    private void unbindPreferences() {
        getListView().setAdapter(null);
        final PreferenceScreen preferenceScreen = getPreferenceScreen();
        if (preferenceScreen != null) {
            preferenceScreen.onDetached();
        }
        onUnbindPreferences();
    }

    /**
     * Used by Settings.
     * @hide
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    protected void onBindPreferences() {}

    /**
     * Used by Settings.
     * @hide
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    protected void onUnbindPreferences() {}

    public final RecyclerView getListView() {
        return mList;
    }

    /**
     * Creates the {@link RecyclerView} used to display the preferences.
     * Subclasses may override this to return a customized {@link RecyclerView}.
     *
     * @param inflater           The LayoutInflater object that can be used to inflate the
     *                           {@link RecyclerView}.
     * @param parent             The parent {@link ViewGroup} that the RecyclerView will be attached
     *                           to. This method should not add the view itself, but this can be
     *                           used to generate the layout params of the view.
     * @param savedInstanceState If non-null, this view is being re-constructed from a previous
     *                           saved state as given here.
     * @return A new {@link RecyclerView} object to be placed into the view hierarchy
     */
    @SuppressWarnings("deprecation")
    @NonNull
    public RecyclerView onCreateRecyclerView(@NonNull LayoutInflater inflater,
            @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) {
        // If device detected is Auto, use Auto's custom layout that contains a custom ViewGroup
        // wrapping a RecyclerView
        if (requireContext().getPackageManager().hasSystemFeature(PackageManager
                .FEATURE_AUTOMOTIVE)) {
            RecyclerView recyclerView = parent.findViewById(R.id.recycler_view);
            if (recyclerView != null) {
                return recyclerView;
            }
        }
        RecyclerView recyclerView = (RecyclerView) inflater
                .inflate(R.layout.preference_recyclerview, parent, false);

        recyclerView.setLayoutManager(onCreateLayoutManager());
        recyclerView.setAccessibilityDelegateCompat(
                new PreferenceRecyclerViewAccessibilityDelegate(recyclerView));

        return recyclerView;
    }

    /**
     * Called from {@link #onCreateRecyclerView} to create the {@link RecyclerView.LayoutManager}
     * for the created {@link RecyclerView}.
     *
     * @return A new {@link RecyclerView.LayoutManager} instance
     */
    @NonNull
    public RecyclerView.LayoutManager onCreateLayoutManager() {
        return new LinearLayoutManager(requireContext());
    }

    /**
     * Creates the root adapter.
     *
     * @param preferenceScreen The {@link PreferenceScreen} object to create the adapter for
     * @return An adapter that contains the preferences contained in this {@link PreferenceScreen}
     */
    @NonNull
    protected RecyclerView.Adapter onCreateAdapter(@NonNull PreferenceScreen preferenceScreen) {
        return new PreferenceGroupAdapter(preferenceScreen);
    }

    /**
     * Called when a preference in the tree requests to display a dialog. Subclasses should
     * override this method to display custom dialogs or to handle dialogs for custom preference
     * classes.
     *
     * @param preference The {@link Preference} object requesting the dialog
     */
    @SuppressWarnings("deprecation")
    @Override
    public void onDisplayPreferenceDialog(@NonNull Preference preference) {

        boolean handled = false;
        if (getCallbackFragment() instanceof OnPreferenceDisplayDialogCallback) {
            handled = ((OnPreferenceDisplayDialogCallback) getCallbackFragment())
                    .onPreferenceDisplayDialog(this, preference);
        }
        //  If the callback fragment doesn't handle OnPreferenceDisplayDialogCallback, looks up
        //  its parent fragment in the hierarchy that implements the callback until the first
        //  one that returns true
        Fragment callbackFragment = this;
        while (!handled && callbackFragment != null) {
            if (callbackFragment instanceof OnPreferenceDisplayDialogCallback) {
                handled = ((OnPreferenceDisplayDialogCallback) callbackFragment)
                        .onPreferenceDisplayDialog(this, preference);
            }
            callbackFragment = callbackFragment.getParentFragment();
        }
        if (!handled && getContext() instanceof OnPreferenceDisplayDialogCallback) {
            handled = ((OnPreferenceDisplayDialogCallback) getContext())
                    .onPreferenceDisplayDialog(this, preference);
        }
        // Check the Activity as well in case getContext was overridden to return something other
        // than the Activity.
        if (!handled && getActivity() instanceof OnPreferenceDisplayDialogCallback) {
            handled = ((OnPreferenceDisplayDialogCallback) getActivity())
                    .onPreferenceDisplayDialog(this, preference);
        }

        if (handled) {
            return;
        }

        // check if dialog is already showing
        if (getParentFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
            return;
        }

        final DialogFragment f;
        if (preference instanceof EditTextPreference) {
            f = EditTextPreferenceDialogFragmentCompat.newInstance(preference.getKey());
        } else if (preference instanceof ListPreference) {
            f = ListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
        } else if (preference instanceof MultiSelectListPreference) {
            f = MultiSelectListPreferenceDialogFragmentCompat.newInstance(preference.getKey());
        } else {
            throw new IllegalArgumentException(
                    "Cannot display dialog for an unknown Preference type: "
                            + preference.getClass().getSimpleName()
                            + ". Make sure to implement onPreferenceDisplayDialog() to handle "
                            + "displaying a custom dialog for this Preference.");
        }
        f.setTargetFragment(this, 0);
        f.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG);
    }

    /**
     * A wrapper for getParentFragment which is v17+. Used by the leanback preference lib.
     *
     * @return The {@link Fragment} to possibly use as a callback
     * @hide
     */
    @Nullable
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public Fragment getCallbackFragment() {
        return null;
    }

    public void scrollToPreference(@NonNull String key) {
        scrollToPreferenceInternal(null, key);
    }

    public void scrollToPreference(@NonNull Preference preference) {
        scrollToPreferenceInternal(preference, null);
    }

    private void scrollToPreferenceInternal(@Nullable final Preference preference,
            @Nullable final String key) {
        final Runnable r = new Runnable() {
            @Override
            public void run() {
                final RecyclerView.Adapter<?> adapter = mList.getAdapter();
                if (!(adapter instanceof
                        PreferenceGroup.PreferencePositionCallback)) {
                    if (adapter != null) {
                        throw new IllegalStateException("Adapter must implement "
                                + "PreferencePositionCallback");
                    } else {
                        // Adapter was set to null, so don't scroll
                        return;
                    }
                }
                final int position;
                if (preference != null) {
                    position = ((PreferenceGroup.PreferencePositionCallback) adapter)
                            .getPreferenceAdapterPosition(preference);
                } else {
                    position = ((PreferenceGroup.PreferencePositionCallback) adapter)
                            .getPreferenceAdapterPosition(key);
                }
                if (position != RecyclerView.NO_POSITION) {
                    mList.scrollToPosition(position);
                } else {
                    // Item not found, wait for an update and try again
                    adapter.registerAdapterDataObserver(
                            new ScrollToPreferenceObserver(adapter, mList, preference, key));
                }
            }
        };
        if (mList == null) {
            mSelectPreferenceRunnable = r;
        } else {
            r.run();
        }
    }

    /**
     * Interface that the fragment's containing activity should implement to be able to process
     * preference items that wish to switch to a specified fragment.
     */
    public interface OnPreferenceStartFragmentCallback {
        /**
         * Called when the user has clicked on a preference that has a fragment class name
         * associated with it. The implementation should instantiate and switch to an instance
         * of the given fragment.
         *
         * @param caller The fragment requesting navigation
         * @param pref   The preference requesting the fragment
         * @return {@code true} if the fragment creation has been handled
         */
        boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat caller,
                @NonNull Preference pref);
    }

    /**
     * Interface that the fragment's containing activity should implement to be able to process
     * preference items that wish to switch to a new screen of preferences.
     */
    public interface OnPreferenceStartScreenCallback {
        /**
         * Called when the user has clicked on a {@link PreferenceScreen} in order to navigate to
         * a new screen of preferences.
         *
         * @param caller The fragment requesting navigation
         * @param pref   The preference screen to navigate to
         * @return {@code true} if the screen navigation has been handled
         */
        boolean onPreferenceStartScreen(@NonNull PreferenceFragmentCompat caller,
                @NonNull PreferenceScreen pref);
    }

    /**
     * Interface that the fragment's containing activity should implement to be able to process
     * preference items that wish to display a dialog.
     */
    public interface OnPreferenceDisplayDialogCallback {
        /**
         * @param caller The fragment containing the preference requesting the dialog
         * @param pref   The preference requesting the dialog
         * @return {@code true} if the dialog creation has been handled
         */
        boolean onPreferenceDisplayDialog(@NonNull PreferenceFragmentCompat caller,
                @NonNull Preference pref);
    }

    private static class ScrollToPreferenceObserver extends RecyclerView.AdapterDataObserver {
        private final RecyclerView.Adapter<?> mAdapter;
        private final RecyclerView mList;
        private final Preference mPreference;
        private final String mKey;

        ScrollToPreferenceObserver(RecyclerView.Adapter<?> adapter, RecyclerView list,
                Preference preference, String key) {
            mAdapter = adapter;
            mList = list;
            mPreference = preference;
            mKey = key;
        }

        private void scrollToPreference() {
            mAdapter.unregisterAdapterDataObserver(this);
            final int position;
            if (mPreference != null) {
                position = ((PreferenceGroup.PreferencePositionCallback) mAdapter)
                        .getPreferenceAdapterPosition(mPreference);
            } else {
                position = ((PreferenceGroup.PreferencePositionCallback) mAdapter)
                        .getPreferenceAdapterPosition(mKey);
            }
            if (position != RecyclerView.NO_POSITION) {
                mList.scrollToPosition(position);
            }
        }

        @Override
        public void onChanged() {
            scrollToPreference();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            scrollToPreference();
        }

        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            scrollToPreference();
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            scrollToPreference();
        }

        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            scrollToPreference();
        }

        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            scrollToPreference();
        }
    }

    private class DividerDecoration extends RecyclerView.ItemDecoration {

        private Drawable mDivider;
        private int mDividerHeight;
        private boolean mAllowDividerAfterLastItem = true;

        DividerDecoration() {}

        @Override
        public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent,
                @NonNull RecyclerView.State state) {
            if (mDivider == null) {
                return;
            }
            final int childCount = parent.getChildCount();
            final int width = parent.getWidth();
            for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
                final View view = parent.getChildAt(childViewIndex);
                if (shouldDrawDividerBelow(view, parent)) {
                    int top = (int) view.getY() + view.getHeight();
                    mDivider.setBounds(0, top, width, top + mDividerHeight);
                    mDivider.draw(c);
                }
            }
        }

        @Override
        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
                @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
            if (shouldDrawDividerBelow(view, parent)) {
                outRect.bottom = mDividerHeight;
            }
        }

        private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
            final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
            final boolean dividerAllowedBelow = holder instanceof PreferenceViewHolder
                    && ((PreferenceViewHolder) holder).isDividerAllowedBelow();
            if (!dividerAllowedBelow) {
                return false;
            }
            boolean nextAllowed = mAllowDividerAfterLastItem;
            int index = parent.indexOfChild(view);
            if (index < parent.getChildCount() - 1) {
                final View nextView = parent.getChildAt(index + 1);
                final RecyclerView.ViewHolder nextHolder = parent.getChildViewHolder(nextView);
                nextAllowed = nextHolder instanceof PreferenceViewHolder
                        && ((PreferenceViewHolder) nextHolder).isDividerAllowedAbove();
            }
            return nextAllowed;
        }

        public void setDivider(Drawable divider) {
            if (divider != null) {
                mDividerHeight = divider.getIntrinsicHeight();
            } else {
                mDividerHeight = 0;
            }
            mDivider = divider;
            mList.invalidateItemDecorations();
        }

        public void setDividerHeight(int dividerHeight) {
            mDividerHeight = dividerHeight;
            mList.invalidateItemDecorations();
        }

        public void setAllowDividerAfterLastItem(boolean allowDividerAfterLastItem) {
            mAllowDividerAfterLastItem = allowDividerAfterLastItem;
        }
    }
}