Can't get PreferenceFragmentCompat to work

10.4k views Asked by At

I am trying to create an Activity that extends AppCompatActivity and has two fragments inside of it (one below another - just by using a LinearLayout). I would like the first fragment to extend the PreferenceFragmentCompat class from the support-v7 library.

I followed Google's short example regarding PreferenceFragmentCompat as shown at https://developer.android.com/reference/android/support/v7/preference/PreferenceFragmentCompat.html.

Here is my current code:

GroupDetailsActivity.java

public class GroupDetailsActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_group_details);

        GroupDetailsPrefFragment prefFragment = GroupDetailsPrefFragment.newInstance();
        GroupDetailsMembersFragment membersFragment = GroupDetailsMembersFragment.newInstance();

        FragmentManager fm = getSupportFragmentManager();
        fm.beginTransaction()
            .add(R.id.flPrefFragment, prefFragment, GroupDetailsPrefFragment.TAG)
            .add(R.id.flMembersFragment, membersFragment, GroupDetailsMembersFragment.TAG)
            .commit();
    }
}

GroupDetailsPrefFragment .java - The problematic fragment

public class GroupDetailsPrefFragment extends PreferenceFragmentCompat {

    public static final String TAG = "GroupDetailsPrefFragment";

    @Override
    public void onCreatePreferences(Bundle bundle, String s) {
        setPreferencesFromResource(R.xml.group_details_preferences, s);
    }

    public static GroupDetailsPrefFragment newInstance() {
        GroupDetailsPrefFragment fragment = new GroupDetailsPrefFragment();
        return fragment;
    }
}

GroupDetailsMembersFragment.java - Completely empty for now..

public class GroupDetailsMembersFragment extends Fragment {

    public static final String TAG = "GroupDetailsMembersFragment";

    public static GroupDetailsMembersFragment newInstance() {
        GroupDetailsMembersFragment fragment = new     GroupDetailsMembersFragment();
        return fragment;
    }
}

activity_group_details.xml - Activity's layout

<?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">

    <FrameLayout
        android:id="@+id/flPrefFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <FrameLayout
        android:id="@+id/flMembersFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

group_details_preferences.xml - The preference XML file

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <Preference
        android:title="@string/remove_me"
        android:key="@string/pref_key_settings_remove_me"/>
    <Preference
        android:title="@string/delete_group"
        android:key="@string/pref_key_settings_delete_group"/>

</PreferenceScreen>

Trying to compile and run the code above lead me into a few errors, the first one was regarding a preference theme that was not set. I have quickly scanned the internet and found you need to add the following line into your Activity's theme : <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>

To do so, I was needed to add the support-v14 library to gradle!

Trying to run the code again, lead me to another error, which is the reason I am posting this, and so far I didn't find any way to solve this issue. Here is the crash log :

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.cochat.android/com.cochat.android.ui.groups.details.GroupDetailsActivity}: java.lang.RuntimeException: Content has view with id attribute 'android.R.id.list_container' that is not a ViewGroup class
                                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2305)
                                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2363)
                                                                       at android.app.ActivityThread.access$900(ActivityThread.java:161)
                                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1265)
                                                                       at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                       at android.os.Looper.loop(Looper.java:157)
                                                                       at android.app.ActivityThread.main(ActivityThread.java:5356)
                                                                       at java.lang.reflect.Method.invokeNative(Native Method)
                                                                       at java.lang.reflect.Method.invoke(Method.java:515)
                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
                                                                       at dalvik.system.NativeStart.main(Native Method)
                                                                    Caused by: java.lang.RuntimeException: Content has view with id attribute 'android.R.id.list_container' that is not a ViewGroup class
                                                                       at android.support.v7.preference.PreferenceFragmentCompat.onCreateView(PreferenceFragmentCompat.java:269)
                                                                       at android.support.v4.app.Fragment.performCreateView(Fragment.java:2184)
                                                                       at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1298)
                                                                       at android.support.v4.app.FragmentManagerImpl.moveFragmentsToInvisible(FragmentManager.java:2323)
                                                                       at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2136)
                                                                       at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2092)
                                                                       at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1998)
                                                                       at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:388)
                                                                       at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:607)
                                                                       at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:181)
                                                                       at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1189)
                                                                       at android.app.Activity.performStart(Activity.java:5441)
                                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
                                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2363) 
                                                                       at android.app.ActivityThread.access$900(ActivityThread.java:161) 
                                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1265) 
                                                                       at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                       at android.os.Looper.loop(Looper.java:157) 
                                                                       at android.app.ActivityThread.main(ActivityThread.java:5356) 
                                                                       at java.lang.reflect.Method.invokeNative(Native Method) 
                                                                       at java.lang.reflect.Method.invoke(Method.java:515) 
                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265) 
                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081) 
                                                                       at dalvik.system.NativeStart.main(Native Method) 

Been stuck on this for a while now, tried looking up different posts on stackoverflow or other websites, seen some solutions, but for some reason none of them managed to solve my problem.

Edit:

My gradle file contains the following:

compileSdkVersion 25
buildToolsVersion '25.0.2'
...
compile 'com.android.support:preference-v7:25.1.0'
compile 'com.android.support:preference-v14:25.1.0'

Update Jan 02 '17

I've been looking into the source code of PreferenceFragmentCompat and seen it is trying to load the following layout: R.layout.preference_list_fragment. In the onCreateView() method of the class, it is inflating the layout, and trying to look for the id android.R.id.list_container. The problem is that there is no such id within the layout.

Here is a code snippet from the PreferenceFragmentCompat:

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

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

While

private int mLayoutResId = R.layout.preference_list_fragment;

Still looking for a solution, thanks!

6

There are 6 answers

0
mgR On BEST ANSWER

So I have found a work around for this, might not be the best, but it is the only thing I have managed to get working! It seems like a problem/bug with the support library..

I have copied the original PreferenceFragmentCompat to a local class, and made minor changed to it. I have replace the following line

private int mLayoutResId = android.support.v7.preference.R.layout.preference_list_fragment;

with

private int mLayoutResId = R.layout.preference_fragment_compat_container;

which is a layout I have made, that it very simple and only contains a container for the list. Here is the layout code:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@android:id/list_container"
          android:orientation="vertical"
          android:layout_width="match_parent"
          android:layout_height="match_parent">

Doing the above will allow you to use PreferenceFragmentCompat with the specific gradle settings above (see original post) without any problems.

The down side is that upgrading the support libraries will not upgrade your PreferenceFragmentCompat since it is copied of course. You will need to keep track of the support libraries, and when ever the problem is fixed, you may delete the copied class and use the original one.

If you have any other solutions or ideas please share!

3
Oleg Bogdanov On

According to this article:

Note that Google changed the needed id from R.id.list_container (in revision 23.4.0) to android.R.id.list_container (in revision 24.0.0). Android Studio says the new id requires API 24, but it also works on older APIs.

ViewGroup in your layout file (activity_group_details.xml) should have id "@android:id/list_container”. to make it work. It can look like this:

<FrameLayout
    android:id="@android:id/list_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

<FrameLayout
    android:id="@+id/flMembersFragment"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

And you have to use this id for your transaction too, note that the other fragment of yours can use any Id for container, only pref is 'special'

0
Marc On

Checked out my answer here https://stackoverflow.com/a/53560798/1247248

you need a layout resource with that id

0
Andrew On

I got past this using the following bugfix: https://github.com/Gericop/Android-Support-Preference-V7-Fix

Simple 3 step process is to update the app's build.gradle,
Remove:

compile 'com.android.support:preference-v7:25.0.1'
compile 'com.android.support:preference-v14:25.0.1'

Add

compile 'com.takisoft.fix:preference-v7:25.0.1.0'

Then update your app's theme for preferences to use PreferenceFixTheme

(You're already using PreferenceFragmentCompat so good to go).

UPDATE:
On further investigation, with api 25.1.0, AppCompat worked fine once I realized the very strict restrictions on the style for Preferences.

I found this resource very helpful in getting everything set up nicely:

0
Ingrid On

Adding this line to your AppTheme in the styles.xml file should do the trick:

<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
0
skylerb On

I think you just need to use the support PreferenceScreen and Preferences:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.v7.preference.Preference
        android:title="@string/remove_me"
        android:key="@string/pref_key_settings_remove_me"/>
    <android.support.v7.preference.Preference
        android:title="@string/delete_group"
        android:key="@string/pref_key_settings_delete_group"/>

</android.support.v7.preference.PreferenceScreen>