Catégories des en - têtes Android dans L'activité de préférence avec fragment de préférence

j'aimerais afficher un écran de préférences comme celui de L'application Android settings : utiliser les en-têtes, PreferenceActivity, PreferenceFragment et headers catégories.

Je ne veux pas ce résultat sur une tablette:

enter image description here

et celui - ci sur un smartphone:

enter image description here

cela fonctionne si j'utilise juste les en-têtes de base, mais si j'essaie de ajouter des catégories, il fonctionne sur le smartphone, et se planter sur la tablette, où je reçois l'exception "java.lang.NullPointerException: nom == null" :

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{fr.ifremer.testandroid/fr.ifremer.testandroid.models.preferences.MainPreferenceActivity}: java.lang.NullPointerException: name == null
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2110)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2135)
    at android.app.ActivityThread.access0(ActivityThread.java:140)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4921)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1038)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException: name == null
    at java.lang.VMClassLoader.findLoadedClass(Native Method)
    at java.lang.ClassLoader.findLoadedClass(ClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:491)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
    at android.app.Fragment.instantiate(Fragment.java:574)
    at android.preference.PreferenceActivity.switchToHeaderInner(PreferenceActivity.java:1222)
    at android.preference.PreferenceActivity.switchToHeader(PreferenceActivity.java:1255)
    at android.preference.PreferenceActivity.onCreate(PreferenceActivity.java:630)
    at fr.ifremer.testandroid.models.preferences.MainPreferenceActivity.onCreate(MainPreferenceActivity.java:19)
    at android.app.Activity.performCreate(Activity.java:5206)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1094)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2074)
    ... 11 more

ci-dessous sont les morceaux de code. Je les ai obtenus principalement à partir de L'application Android settings app source.

une idée ?

Merci d'avance


activité principale de référence:

public class MainPreferenceActivity extends PreferenceActivity {

    private static List<Header> _headers;

    @Override
    public void onBuildHeaders(List<Header> headers) {

        _headers = headers;
        loadHeadersFromResource(R.xml.preference_headers, headers);
    }

    @Override
    public void setListAdapter(ListAdapter adapter) {

        if (adapter == null) {
            super.setListAdapter(null);
        } else {
            super.setListAdapter(new HeaderAdapter(this, _headers));
        }
    }
}

PreferencesFragment:

public class PreferencesFragment extends PreferenceFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        String settings = getArguments().getString("settings");

        if (settings.equals("DIVE")) {

            addPreferencesFromResource(R.xml.preference_dive_tile);
        }
        else if (settings.equals("MAP")) {

            addPreferencesFromResource(R.xml.preference_map_tile);
        }
    }
}

preference_headers.xml:

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

    <header
        android:id="@+id/header_section_1"
        android:title="Section 1" />

    <header
        android:fragment="fr.ifremer.testandroid.models.preferences.PreferencesFragment"
        android:summary="DIVE summary"
        android:title="DIVE title" >
        <extra
            android:name="settings"
            android:value="DIVE" />
    </header>
    <header
        android:fragment="fr.ifremer.testandroid.models.preferences.PreferencesFragment"
        android:summary="MAP summary"
        android:title="MAP title" >
        <extra
            android:name="settings"
            android:value="MAP" />
    </header>

</preference-headers>

Last mais pas moins, HeaderAdapter :

public class HeaderAdapter extends ArrayAdapter<Header> {

    static final int HEADER_TYPE_CATEGORY = 0;
    static final int HEADER_TYPE_NORMAL = 1;
    private static final int HEADER_TYPE_COUNT = HEADER_TYPE_NORMAL + 1;

    private LayoutInflater mInflater;

    private static class HeaderViewHolder {
        ImageView icon;
        TextView title;
        TextView summary;
    }

    public HeaderAdapter(Context context, List<Header> objects) {

        super(context, 0, objects);

        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    static int getHeaderType(Header header) {

        if (header.fragment == null && header.intent == null) return HEADER_TYPE_CATEGORY;
        else return HEADER_TYPE_NORMAL;
    }

    @Override
    public int getItemViewType(int position) {
        Header header = getItem(position);
        return getHeaderType(header);
    }

    @Override
    public boolean areAllItemsEnabled() { return false; /* because of categories */ }

    @Override
    public boolean isEnabled(int position) { return getItemViewType(position) != HEADER_TYPE_CATEGORY; }

    @Override
    public int getViewTypeCount() { return HEADER_TYPE_COUNT; }

    @Override
    public boolean hasStableIds() { return true; }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        HeaderViewHolder holder;
        Header header = getItem(position);
        int headerType = getHeaderType(header);
        View view = null;

        if (convertView == null) {

            holder = new HeaderViewHolder();

            switch (headerType) {

                case HEADER_TYPE_CATEGORY:

                    view = new TextView(getContext(), null, android.R.attr.listSeparatorTextViewStyle);
                    holder.title = (TextView) view;
                    break;

                case HEADER_TYPE_NORMAL:

                    view = mInflater.inflate(R.layout.preference_header_item, parent, false);
                    holder.icon = (ImageView) view.findViewById(R.id.icon);
                    holder.title = (TextView) view.findViewById(R.id.title);
                    holder.summary = (TextView) view.findViewById(R.id.summary);
                    break;
            }

            view.setTag(holder);
        }
        else {

            view = convertView;
            holder = (HeaderViewHolder) view.getTag();
        }

        // All view fields must be updated every time, because the view may be recycled
        switch (headerType) {

            case HEADER_TYPE_CATEGORY :

                holder.title.setText(header.getTitle(getContext().getResources()));
                break;

            case HEADER_TYPE_NORMAL :

                holder.icon.setImageResource(header.iconRes);

                holder.title.setText(header.getTitle(getContext().getResources()));
                CharSequence summary = header.getSummary(getContext().getResources());

                if (!TextUtils.isEmpty(summary)) {

                    holder.summary.setVisibility(View.VISIBLE);
                    holder.summary.setText(summary);
                }
                else {
                    holder.summary.setVisibility(View.GONE);
                }
                break;
        }

        return view;
    }
}
6
demandé sur Tim Autin 2013-03-21 19:28:08

4 réponses

peut-être que le premier en-tête est le menu sélectionné par défaut. Si oui, il devrait avoir fragment d'attribut pour montrer côté droit.

1
répondu bestofbest1 2013-04-02 08:03:20

comme bestofbest1 dit, le problème était que Android a essayé de montrer le premier élément dans le preferences_headers.xml, qui ne contenait pas de fragment.

pour le corriger, j'ai ajouté dans Mainpreference Activit's onCreate la ligne ci-dessous (avant super.onCreate) pour sélectionner un fragment par défaut lors de l'utilisation d'une tablette:

if(onIsMultiPane()) getIntent().putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, PreferencesFragment.class.getName());

j'ai aussi défini un fragment par défaut dans PreferencesFragment:

String settings = "DIVE";
if(getArguments() != null) settings = getArguments().getString("settings");

Puis un dernier problème, PreferenceActivity.EXTRA_SHOW_FRAGMENT ne sélectionne pas l'en-tête du côté gauche. Pour le corriger dans MainPreferencesActivity enregistrer une référence à vos en-têtes( dans onbuild headers), et ajouter:

@Override
protected void onResume() {

    // Call super :
    super.onResume();

    // Select the displayed fragment in the headers (when using a tablet) :
    // This should be done by Android, it is a bug fix
    if(_headers != null) {

        final String displayedFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
        if (displayedFragment != null) {
            for (final Header header : _headers) {
                if (displayedFragment.equals(header.fragment)) {
                    switchToHeader(header);
                    break;
                }
            }
        }
    }
}
6
répondu Tim Autin 2013-05-28 13:51:10

j'ai eu des problèmes pour obtenir la solution de Tim de travailler pour moi (le programme serait toujours crash). J'ai travaillé sur cette question d'une manière différente en sélectionnant simplement le premier en-tête de non-catégorie par défaut au lieu du premier dans la liste. Pour ce faire, j'ai remplacé la méthode onGetInitialHeader dans mon PreferenceActivity

@Override
public Header onGetInitialHeader() {
    for (int i = 0; i < mHeaders.size(); i++) {
        Header h = mHeaders.get(i);
        if (!isCategory(h)) {
            return h;
        }
    }
}

protected static boolean isCategory(Header h) {
    return h.fragment == null;
}

mHeaders n'est qu'une référence à la liste d'en-tête enregistrée dans l'appel à onBuildHeaders . Il convient également de noter qu'il ne s'agit que d'un problème avant 4.3, il a depuis été corrigé. Espérons que cela aide quelqu'un

2
répondu StackJP 2013-10-11 15:21:16

comme une forme plus simple de la solution de Tim Autin, désactiver multi-pane tout à fait pour produire une seule vitre, le téléphone-comme l'affichage sur les tablettes.

public class PreferencesActivity extends PreferenceActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if(onIsMultiPane())
            getIntent().putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true);
        super.onCreate(savedInstanceState);
    }
...
}
1
répondu nbyoung 2015-11-10 02:39:31