Search code examples
androidandroid-fragmentsappcompatactivitypreferencefragmentsupport-preference

Can't get PreferenceFragmentCompat to work


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!


Solution

  • 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!