Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 536
Lakshman Chilukuri Avatar asked Mar 13 '16 05:03

Lakshman Chilukuri


2 Answers

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);
    }
}
like image 153
Byte Welder Avatar answered Sep 24 '22 12:09

Byte Welder


I had the same problem, however my requirements were a bit different, I just needed to show a layout above the settings list, so I did this

<NestedScrollView>
  <ConstrainLayout>
    <CustomView>
    <SettingsFragmentHolder/>
  </ConstraintLayout>
</NestedScrollView>

Then later in code I just do this

FragmentManager.beginTransaction().replace(holder, PreferencesFragment()).commit()

One thing to note is that, preferences fragment has its own scrollview or a list, that will cause the NestedScrollView to scroll to the beginning of PreferencesFragment layout, to fix that add this to the parent layout of the PreferencesFragment, in this case it is ConstraintLayout

android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true"

That will stop the default scrolling behavior

like image 27
abdu Avatar answered Sep 21 '22 12:09

abdu