android - How to set custom layout for PreferenceActivity in Android 3.0?

morphium picture morphium · Oct 21, 2011 · Viewed 12.5k times · Source

I am developing app with minSdkVersion="11", that is app for tablets and Android 4.0 and newer. I have scrutinized internet on this topic, but have not found much.

To implement custom layout for previous versions of Android SDK we just have to create layout (say preference.xml) with ListView and its id equals to android.R.id.list and use setContentView method.

preference.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" > 
    <ListView android:id="@android:id/list"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent" /> 
</RelativeLayout>

In Android 3.0 things have changed and Preferences are implemented with use of fragments. That is how my preference_headers.xml file looks like:

<preference-headers
        xmlns:android="http://schemas.android.com/apk/res/android">

    <header android:fragment="com.example.MyPreferenceActivity$GeneralSettingsFragment" 
            android:title="General"
            android:summary="Common settings." />
    <header  
            android:title="Example.com" >
        <intent android:action="android.intent.action.VIEW"
                android:data="http://www.example.com" />
    </header> 

</preference-headers>

MyPreferenceActivity.java:

public class MyPreferenceActivity extends PreferenceActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.preference);
        // Add a button to the header list.
        if (hasHeaders()) {
            Button button = new Button(this); 
            button.setText("Log out");
            setListFooter(button);

        }

    }


    /**
     * Populate the activity with the top-level headers.
     */
    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.preference_headers, target);
    }

    public static class GeneralSettingsFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Load the preferences from an XML resource
            addPreferencesFromResource(R.xml.fragmented_preferences);

        }
    }
}

Now if I run MyPreferenceActivity I will see this error in LogCat:

> java.lang.IllegalArgumentException: No view found for id 0x10202be for
> fragment GeneralSettingsFragment{4077f8c0 #0 id=0x10202be}
> E/AndroidRuntime(17103): at
> android.app.FragmentManagerImpl.moveToState(FragmentManager.java:729)
> E/AndroidRuntime(17103): at
> android.app.FragmentManagerImpl.moveToState(FragmentManager.java:926)
> E/AndroidRuntime(17103): at
> android.app.FragmentManagerImpl.moveToState(FragmentManager.java:909)
> E/AndroidRuntime(17103): at
> android.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:1584)
> E/AndroidRuntime(17103): at
> android.app.Activity.performStart(Activity.java:4377)
> E/AndroidRuntime(17103): at
> android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1724)
> E/AndroidRuntime(17103):  ... 11 more

I know what causes this problem. FragmentManager just cannot find a view to insert fragment GeneralSettingsFragment in. But I don't know how to solve it.

By the way, if I run the same app on Android 4.0, then I can see the first Preference Activity with headers. But if I click on General, app will crash and I will receive similar error in LogCat:

java.lang.IllegalArgumentException: No view found for id 0x10202cd for fragment GeneralSettingsFragment{4134b4e0 #0 id=0x10202cd}
E/AndroidRuntime(2170):     at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:789)
E/AndroidRuntime(2170):     at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:998)
E/AndroidRuntime(2170):     at android.app.BackStackRecord.run(BackStackRecord.java:622)
E/AndroidRuntime(2170):     at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1330)
E/AndroidRuntime(2170):     at android.app.Activity.performStart(Activity.java:4474)
E/AndroidRuntime(2170):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1928)

Answer

Waza_Be picture Waza_Be · Jan 26, 2012

I had the same issue than you.

I tried a lot of stuff but here is my full code:

https://github.com/iRail/BeTrains-for-Android/blob/master/src/tof/cv/mpp/MyPreferenceActivity.java

The trick is to add the setContentView in the onBuildHeaders section, but NOT in the onCreate.

I also made some tests in fragment and not perfectly sure why this is working, but I promise you: I have a custom layout on tablet header section!

https://github.com/iRail/BeTrains-for-Android/blob/master/src/tof/cv/mpp/view/StockPreferenceFragment.java