androidandroid-support-librarypreferencefragment

PreferenceFragmentCompat custom layout


I need a custom layout for my PreferenceFragmentCompat. In the docs for PreferenceFragmentCompat it seems that you can possibly inflate and return a view in onCreateView().

However a NPE results:-

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView.setAdapter(android.support.v7.widget.RecyclerView$Adapter)' on a null object reference
                                                                       at android.support.v7.preference.PreferenceFragmentCompat.bindPreferences(PreferenceFragmentCompat.java:511)
                                                                       at android.support.v7.preference.PreferenceFragmentCompat.onActivityCreated(PreferenceFragmentCompat.java:316)
                                                                       at com.cls.example.MyPrefFrag.onActivityCreated(MyPrefFrag.java:42) 

After I checked the source of PreferenceFragmentCompat:onCreateView I found the following piece of code :-

 RecyclerView listView = this.onCreateRecyclerView(themedInflater, listContainer, savedInstanceState);
 if(listView == null) {
    throw new RuntimeException("Could not create RecyclerView");
 } else {
    this.mList = listView;   //problem
    ...
    return view;
 }

So if you override onCreateView() and return a custom layout the onCreateRecyclerView() is not called plus the RecyclerView private field mList will not be set. So the NPE on setAdapter() results.

Should I assume that having a custom layout is not feasible for PreferenceFragmentCompat ?


Solution

  • You can specify a custom layout in your theme.

    For example:

    styles.xml

    <style name="YourTheme" parent="Theme.AppCompat">
        <!-- ... -->
        <item name="preferenceTheme">@style/YourTheme.PreferenceThemeOverlay</item>
    </style>
    
    <style name="YourTheme.PreferenceThemeOverlay" parent="@style/PreferenceThemeOverlay">
        <item name="android:layout">@layout/fragment_your_preferences</item>
    </style>
    

    fragment_your_preferences.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <android.support.v7.widget.Toolbar
                android:id="@+id/custom_toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
    
        </android.support.design.widget.AppBarLayout>
    
        <!-- Required ViewGroup for PreferenceFragmentCompat -->
        <FrameLayout
            android:id="@android:id/list_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        </FrameLayout>
    
    </LinearLayout>
    

    And then in onViewCreated() of your fragment class you can start using the views:

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);
    
        Toolbar toolbar = (Toolbar)view.findViewById(R.id.custom_toolbar);
    
        if (toolbar != null)
        {
            getMainActivity().setSupportActionBar(toolbar);
        }
    }