web-dev-qa-db-fra.com

Comment créer des groupes d'en-têtes de préférence dans Android PreferenceActivity?

J'utilise des en-têtes de préférence pour créer une activité de paramètres à l'aide de PreferenceActivity. J'essaie de diviser les en-têtes en catégories/groupes, comme celui-ci (il existe des catégories Sans fil et réseaux, Appareil, Personnel, ...):

Quoi qu'il en soit, même que Android Developers concerne cette façon de créer une activité de préférence, je n'ai trouvé aucun moyen de créer la même activité de préférence comme sur l'image. Le seul que j'ai réussi faire est une simple liste d'en-têtes de préférence.

La seule chose que j'ai trouvée est ceci , mais cela fonctionne un peu ... étrange. Cela ne semble donc pas être une option.

Ma question est donc la suivante: comment créer PreferenceActivity en utilisant des en-têtes de préférence avec possibilité de diviser les en-têtes en catégories et avec la possibilité d'utiliser des interrupteurs principaux marche/arrêt?

Une partie de mon code:

preference_headers.xml :

<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <header 
        Android:fragment="cz.vse.myevents.activity.SettingsActivity$EventsFragment"
        Android:title="@string/settings_events"
        Android:icon="@Android:drawable/ic_menu_agenda" />
    <header 
        Android:fragment="cz.vse.myevents.activity.SettingsActivity$OrganizationsFragment"
        Android:title="@string/settings_subscribed_organizations"
        Android:icon="@Android:drawable/ic_menu_view"  />
</preference-headers>

ParamètresActivité :

@Override
public void onBuildHeaders(List<Header> target) {
    super.onBuildHeaders(target);
    loadHeadersFromResource(R.xml.preference_headers, target);
}

Je ne poste pas de fragments de ressources, je pense que ce n'est pas nécessaire.

25
James

La meilleure solution semble être la création de trois blocs de code différents - un pour pré-Honeycomb, un pour post-Honeycomb et un pour tablettes.

L'utilisation des en-têtes de préférence n'est efficace que sur les tablettes, elles restent donc uniquement sur les tablettes. Aucun regroupement n'est utilisé ici.

Les en-têtes de préférence sur post-Honeycomb sont un peu inutiles, donc le meilleur est l'utilisation de PreferenceScreen typique dans un PreferenceFragment. Les groupes peuvent être créés facilement par PreferenceCategory.

Et enfin, pour la pré-Honeycomb, la méthode obsolète sans utiliser PrefrenceFragment est la seule façon.

Malheureusement, il y a beaucoup de duplication de code, mais la bibliothèque UnifiedPreference mentionnée dans la réponse de Leandros est boguée - elle ignore totalement PreferenceFragment donc elle est inutile (du moins pour moi).

5
James

Ceci est un exemple de catégorie de préférence, vous pouvez utiliser la catégorie de préférence et définir le fragment respectif et y parvenir, faites-moi savoir si j'ai mal compris votre cas.

Voici un exemple de disposition

<PreferenceCategory Android:title="Heading1">
        <Preference 
            Android:title="title1"
            Android:summary="summary1"
            Android:key="keyName"/>

       <Preference 
            Android:title="title2"
            Android:summary="summary2"
            Android:key="keyName"/>
</PreferenceCategory>

<PreferenceCategory Android:title="Heading2">
        <Preference 
            Android:title="title3"
            Android:summary="summary3"
            Android:key="keyName"/>
</PreferenceCategory>
9
Pawan Maheshwari

Pour développer la réponse de T. Folsom , voici mon implémentation:

res/layout/preference_header_item.xml

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:background="?android:attr/activatedBackgroundIndicator"
    Android:baselineAligned="false"
    Android:gravity="center_vertical"
    Android:minHeight="48dp"
    Android:paddingRight="?android:attr/scrollbarSize" >

    <LinearLayout
        Android:layout_width="@dimen/header_icon_width"
        Android:layout_height="wrap_content"
        Android:layout_marginLeft="6dip"
        Android:layout_marginRight="6dip" >

        <ImageView
            Android:id="@+id/icon"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_gravity="center" />
    </LinearLayout>

    <RelativeLayout
        Android:layout_width="0dip"
        Android:layout_height="wrap_content"
        Android:layout_marginBottom="6dip"
        Android:layout_marginLeft="2dip"
        Android:layout_marginRight="6dip"
        Android:layout_marginTop="6dip"
        Android:layout_weight="1" >

        <TextView
            Android:id="@+Android:id/title"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:ellipsize="Marquee"
            Android:fadingEdge="horizontal"
            Android:singleLine="true"
            Android:textAppearance="?android:attr/textAppearanceMedium" />

       <TextView
            Android:id="@+Android:id/summary"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_below="@Android:id/title"
            Android:ellipsize="end"
            Android:maxLines="2"
            Android:textAppearance="?android:attr/textAppearanceSmall" />
    </RelativeLayout>

</LinearLayout>

res/values ​​/ dimens.xml

<resources>

    <dimen name="header_icon_width">28dp</dimen>

</resources>

dans votre classe PreferenceActivity:

    @Override
protected void onCreate(Bundle savedInstanceState) {

    if (savedInstanceState != null) {
        /*
         *  the headers must be restored before the super call in order
         *  to be ready for the call to setListAdapter() 
         */
        if (savedInstanceState.containsKey("headers")) {
            setHeaders((ArrayList<Header>)savedInstanceState.getSerializable("headers"));
        }
    }

    // as suggest by https://stackoverflow.com/questions/15551673/Android-headers-categories-in-preferenceactivity-with-preferencefragment
    if(onIsMultiPane()) getIntent().putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, PreferencesFragment.class.getName());

    super.onCreate(savedInstanceState);

    ...

}

@Override
protected void onResume() {
    super.onResume();

    // https://stackoverflow.com/questions/15551673/Android-headers-categories-in-preferenceactivity-with-preferencefragment
    // Select the displayed fragment in the headers (when using a tablet) :
    // This should be done by Android, it is a bug fix
    if(getHeaders() != null) {

        final String displayedFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
        if (displayedFragment != null) {
            for (final Header header : getHeaders()) {
                if (displayedFragment.equals(header.fragment)) {
                    switchToHeader(header);
                    break;
                }
            }
        }
    }

    ...

}

/**
 * Populate the activity with the top-level headers.
 */
@Override
public void onBuildHeaders(List<Header> target) {
    // we have to save the headers as the API call getHeaders() is hidden.
    setHeaders(target);
    loadHeadersFromResource(R.xml.settings_headers, target);
}

private List<Header> headers;

private void setHeaders(List<Header> headers) {
    this.headers = headers;
}

private List<Header> getHeaders() {
    return headers;
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putSerializable("headers", (ArrayList<PreferenceActivity.Header>)headers);
    super.onSaveInstanceState(outState);
}

@Override
public void setListAdapter(ListAdapter adapter) {
    if (adapter == null) {
        super.setListAdapter(null);
    } else {
        super.setListAdapter(new HeaderAdapter(this, getHeaders()));
    }
}

private static 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 static class HeaderViewHolder {
        ImageView icon;
        TextView title;
        TextView summary;
    }

    private LayoutInflater mInflater;

    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;
    }

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

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

    }

    @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(Android.R.id.title);
                holder.summary = (TextView) view
                        .findViewById(Android.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;
    }

}

Avec tout ce code en place, créer des en-têtes est tout simplement:

<preference-headers xmlns:Android="http://schemas.Android.com/apk/res/Android" >
     <header Android:title="atitle" />
</preference-headers>

J'espère que cela aide quelqu'un. Je sais qu'il m'a fallu un certain temps pour bien travailler.

2
cYrixmorten

C'est en fait assez simple. D'après ce que j'ai trouvé, la racine PreferenceActivity elle-même ne prend pas en charge l'ajout de titres de catégorie/section, il semble que vous ne pouvez ajouter que Headers - ce qui n'est pas très intéressant.

Donc, ce que vous devez d'abord faire, c'est ne pas faire de gros travaux dans votre PreferenceActivity lui-même et passer directement au chargement d'un PreferenceFragment:

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

    setTitle("Settings");

    // Display the fragment as the main content.
    getFragmentManager().beginTransaction()
            .replace(Android.R.id.content, new PreferencesFragment())
            .commit();

}

public static class PreferencesFragment extends PreferenceFragment {

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

        addPreferencesFromResource(R.xml.prefs);
    }
}

Une fois que vous avez fait cela, vous pouvez maintenant faire tout le travail dans votre PreferenceFragment, et la grande nouvelle est que vous pouvez maintenant utiliser des catégories!

Votre fichier R.xml.prefs devrait ressembler à ceci:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:Android="http://schemas.Android.com/apk/res/Android" >
    <PreferenceCategory
        Android:summary="Login credentials"
        Android:title="Login credentials" >
        <EditTextPreference
            Android:key="username"
            Android:summary="Username"
            Android:title="Username" />
        <EditTextPreference
            Android:key="password"
            Android:summary="Password"
            Android:title="Password" />
    </PreferenceCategory>

    <PreferenceCategory
        Android:summary="Settings"
        Android:title="Settings" >
        <CheckBoxPreference
            Android:key="persist"
            Android:summary="Yes/No"
            Android:title="Keep me signed in" />

    </PreferenceCategory>
</PreferenceScreen>

Créez simplement un PreferenceCategory pour chaque nouvelle catégorie que vous souhaitez ajouter.

1
Jonathan Ellis