web-dev-qa-db-fra.com

Création d'un écran de préférences avec le support (v21) Barre d'outils

J'avais des difficultés à utiliser la nouvelle barre d'outils Material Design dans la bibliothèque de support sur un écran Préférences.

J'ai un fichier settings.xml comme ci-dessous:

<PreferenceScreen xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <PreferenceCategory
        Android:title="@string/AddingItems"
        Android:key="pref_key_storage_settings">

        <ListPreference
            Android:key="pref_key_new_items"
            Android:title="@string/LocationOfNewItems"
            Android:summary="@string/LocationOfNewItemsSummary"
            Android:entries="@array/new_items_entry"
            Android:entryValues="@array/new_item_entry_value"
            Android:defaultValue="1"/>

    </PreferenceCategory>
</PreferenceScreen>

Les chaînes sont définies ailleurs.

114
James Cross

Vous pouvez utiliser un PreferenceFragment comme alternative à PreferenceActivity. Donc, voici l’emballage Activity exemple:

public class MyPreferenceActivity extends ActionBarActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pref_with_actionbar);

        Android.support.v7.widget.Toolbar toolbar = (Android.support.v7.widget.Toolbar) findViewById(uk.japplications.jcommon.R.id.toolbar);
        setSupportActionBar(toolbar);

        getFragmentManager().beginTransaction().replace(R.id.content_frame, new MyPreferenceFragment()).commit();
    }
}

Et voici le fichier de mise en page (pref_with_actionbar):

<RelativeLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:orientation="vertical"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <Android.support.v7.widget.Toolbar
        Android:id="@+id/toolbar"
        Android:layout_height="@dimen/action_bar_height"
        Android:layout_width="match_parent"
        Android:minHeight="?attr/actionBarSize"
        Android:background="?attr/colorPrimary"
        app:theme="@style/ToolbarTheme.Base"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    <FrameLayout
        Android:id="@+id/content_frame"
        Android:layout_below="@+id/toolbar"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content" />

</RelativeLayout>

Et enfin le PreferenceFragment:

public static class MyPreferenceFragment extends PreferenceFragment{
    @Override
    public void onCreate(final Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.settings);
    }
}

J'espère que ça aidera quelqu'un.

106
James Cross

S'il vous plaît trouver le repo GitHub: Ici


Un peu tard pour la soirée, mais c’est la solution que j’utilise comme solution pour continuer à utiliser PreferenceActivity:

settings_toolbar.xml :

<?xml version="1.0" encoding="utf-8"?>
<Android.support.v7.widget.Toolbar
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:id="@+id/toolbar"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:minHeight="?attr/actionBarSize"
    app:navigationContentDescription="@string/abc_action_bar_up_description"
    Android:background="?attr/colorPrimary"
    app:navigationIcon="?attr/homeAsUpIndicator"
    app:title="@string/action_settings"
    />

SettingsActivity.Java :

public class SettingsActivity extends PreferenceActivity {

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

        LinearLayout root = (LinearLayout)findViewById(Android.R.id.list).getParent().getParent().getParent();
        Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
        root.addView(bar, 0); // insert at top
        bar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

}

Result :

example


UPDATE (compatibilité du pain d'épice):

Selon les commentaires, les appareils Gingerbread retournent une exception NullPointerException sur cette ligne:

LinearLayout root = (LinearLayout)findViewById(Android.R.id.list).getParent().getParent().getParent();

RÉPARER:

SettingsActivity.Java :

public class SettingsActivity extends PreferenceActivity {

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        Toolbar bar;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            LinearLayout root = (LinearLayout) findViewById(Android.R.id.list).getParent().getParent().getParent();
            bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
            root.addView(bar, 0); // insert at top
        } else {
            ViewGroup root = (ViewGroup) findViewById(Android.R.id.content);
            ListView content = (ListView) root.getChildAt(0);

            root.removeAllViews();

            bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);


            int height;
            TypedValue tv = new TypedValue();
            if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
                height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
            }else{
                height = bar.getHeight();
            }

            content.setPadding(0, height, 0, 0);

            root.addView(content);
            root.addView(bar);
        }

        bar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
}

Tout problème avec ce qui précède faites le moi savoir!


MISE À JOUR 2: CONTOURNEMENT DE LA TEINTE

Comme indiqué dans de nombreuses notes de développement, PreferenceActivity ne prend pas en charge la coloration des éléments. Toutefois, en utilisant quelques classes internes, vous POUVEZ y parvenir. C'est jusqu'à ce que ces classes sont supprimées. (Fonctionne avec appCompat support-v7 v21.0.3).

Ajoutez les importations suivantes:

import Android.support.v7.internal.widget.TintCheckBox;
import Android.support.v7.internal.widget.TintCheckedTextView;
import Android.support.v7.internal.widget.TintEditText;
import Android.support.v7.internal.widget.TintRadioButton;
import Android.support.v7.internal.widget.TintSpinner;

Puis substituez la méthode onCreateView:

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    // Allow super to try and create a view first
    final View result = super.onCreateView(name, context, attrs);
    if (result != null) {
        return result;
    }

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop) {
        // If we're running pre-L, we need to 'inject' our tint aware Views in place of the
        // standard framework versions
        switch (name) {
            case "EditText":
                return new TintEditText(this, attrs);
            case "Spinner":
                return new TintSpinner(this, attrs);
            case "CheckBox":
                return new TintCheckBox(this, attrs);
            case "RadioButton":
                return new TintRadioButton(this, attrs);
            case "CheckedTextView":
                return new TintCheckedTextView(this, attrs);
        }
    }

    return null;
}

Result:

example 2


AppCompat 22.1

AppCompat 22.1 a introduit de nouveaux éléments teintés, ce qui signifie qu'il n'est plus nécessaire d'utiliser les classes internes pour obtenir le même effet que la dernière mise à jour. Suivez plutôt ceci (en remplaçant toujours onCreateView):

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    // Allow super to try and create a view first
    final View result = super.onCreateView(name, context, attrs);
    if (result != null) {
        return result;
    }

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop) {
        // If we're running pre-L, we need to 'inject' our tint aware Views in place of the
        // standard framework versions
        switch (name) {
            case "EditText":
                return new AppCompatEditText(this, attrs);
            case "Spinner":
                return new AppCompatSpinner(this, attrs);
            case "CheckBox":
                return new AppCompatCheckBox(this, attrs);
            case "RadioButton":
                return new AppCompatRadioButton(this, attrs);
            case "CheckedTextView":
                return new AppCompatCheckedTextView(this, attrs);
        }
    }

    return null;
}

ECRANS DE PREFERENCE NESTED

De nombreuses personnes rencontrent des problèmes pour inclure la barre d'outils dans un <PreferenceScreen /> imbriqué. Cependant, j'ai trouvé une solution! - Après beaucoup d'essais et d'erreurs!

Ajoutez ce qui suit à votre SettingsActivity:

@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    super.onPreferenceTreeClick(preferenceScreen, preference);

    // If the user has clicked on a preference screen, set up the screen
    if (preference instanceof PreferenceScreen) {
        setUpNestedScreen((PreferenceScreen) preference);
    }

    return false;
}

public void setUpNestedScreen(PreferenceScreen preferenceScreen) {
    final Dialog dialog = preferenceScreen.getDialog();

    Toolbar bar;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        LinearLayout root = (LinearLayout) dialog.findViewById(Android.R.id.list).getParent();
        bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
        root.addView(bar, 0); // insert at top
    } else {
        ViewGroup root = (ViewGroup) dialog.findViewById(Android.R.id.content);
        ListView content = (ListView) root.getChildAt(0);

        root.removeAllViews();

        bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);

        int height;
        TypedValue tv = new TypedValue();
        if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
            height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
        }else{
            height = bar.getHeight();
        }

        content.setPadding(0, height, 0, 0);

        root.addView(content);
        root.addView(bar);
    }

    bar.setTitle(preferenceScreen.getTitle());

    bar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            dialog.dismiss();
        }
    });
}

La raison pour laquelle PreferenceScreen pose un problème, c'est parce qu'ils sont basés sur une boîte de dialogue d'emballage. Nous devons donc capturer la disposition de la boîte de dialogue pour y ajouter la barre d'outils.


Ombre de la barre d'outils

De par sa conception, l'importation de Toolbar ne permet pas l'élévation ni l'ombrage dans les appareils antérieurs à la v21, donc si vous souhaitez avoir une élévation sur votre Toolbar, vous devez l'envelopper dans un AppBarLayout:

settings_toolbar.xml:

<Android.support.design.widget.AppBarLayout
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content">

   <Android.support.v7.widget.Toolbar
       .../>

</Android.support.design.widget.AppBarLayout>

Sans oublier d’ajouter l’ajout de la bibliothèque Design Support en tant que dépendance dans le fichier build.gradle:

compile 'com.Android.support:support-v4:22.2.0'
compile 'com.Android.support:appcompat-v7:22.2.0'
compile 'com.Android.support:design:22.2.0'

Android 6.0

J'ai enquêté sur le problème de chevauchement signalé et je ne peux pas le reproduire.

Le code complet utilisé ci-dessus produit les éléments suivants:

enter image description here

Si je manque quelque chose s'il vous plaît faites le moi savoir via ce repo et je vais enquêter.

108
David Passmore

Complètement nouvelle mise à jour.

Avec quelques expériences, il semble que j'ai trouvé la solution de travail AppCompat 22.1+ pour les écrans de préférences imbriqués.

Premièrement, comme cela est mentionné dans de nombreuses réponses (dont une ici), vous devrez utiliser le nouveau AppCompatDelegate. Utilisez le fichier AppCompatPreferenceActivity.Java dans les démonstrations de support ( https://Android.googlesource.com/platform/development/+/58bf5b99e6132332afb8b44b4c8cedf5756ad464/samples/Sort7Demos/sort app/AppCompatPreferenceActivity.Java ) et étendez-le simplement ou copiez les fonctions correspondantes dans votre propre PreferenceActivity. Je vais montrer la première approche ici:

public class SettingsActivity extends AppCompatPreferenceActivity {

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

    setContentView(R.layout.settings_page);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    ActionBar bar = getSupportActionBar();
    bar.setHomeButtonEnabled(true);
    bar.setDisplayHomeAsUpEnabled(true);
    bar.setDisplayShowTitleEnabled(true);
    bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
    bar.setTitle(...);
  }

  @Override
  protected boolean isValidFragment(String fragmentName) {
    return SettingsFragment.class.getName().equals(fragmentName);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case Android.R.id.home:
        onBackPressed();
        break;
    }
    return super.onOptionsItemSelected(item);
  }
}

La mise en page est assez simple et habituelle (layout/settings_page.xml):

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:layout_margin="0dp"
    Android:orientation="vertical"
    Android:padding="0dp">
  <Android.support.v7.widget.Toolbar
      Android:id="@+id/toolbar"
      Android:layout_width="match_parent"
      Android:layout_height="?attr/actionBarSize"
      Android:background="?attr/colorPrimary"
      Android:elevation="4dp"
      Android:theme="@style/..."/>
  <ListView
      Android:id="@id/Android:list"
      Android:layout_width="match_parent"
      Android:layout_height="match_parent"/>
</LinearLayout>

Les préférences elles-mêmes sont définies comme d'habitude (xml/settings.xml):

<preference-headers xmlns:Android="http://schemas.Android.com/apk/res/Android">
  <header
      Android:fragment="com.example.SettingsFragment"
      Android:summary="@string/..."
      Android:title="@string/...">
    <extra
        Android:name="page"
        Android:value="page1"/>
  </header>
  <header
      Android:fragment="com.example.SettingsFragment"
      Android:summary="@string/..."
      Android:title="@string/...">
    <extra
        Android:name="page"
        Android:value="page2"/>
  </header>
  ...
</preference-headers>

Aucune différence réelle dans les solutions sur le net jusqu'à ce point. En fait, vous pouvez l'utiliser même si vous n'avez pas d'écrans imbriqués, pas d'en-têtes, juste un seul écran.

Nous utilisons un PreferenceFragment commun pour toutes les pages plus profondes, différencié par les paramètres extra dans les en-têtes. Chaque page aura un XML séparé avec un PreferenceScreen commun (xml/settings_page1.xml et al.) Commun. Le fragment utilise la même présentation que l'activité, y compris la barre d'outils.

public class SettingsFragment extends PreferenceFragment {

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getActivity().setTheme(R.style...);

    if (getArguments() != null) {
      String page = getArguments().getString("page");
      if (page != null)
        switch (page) {
          case "page1":
            addPreferencesFromResource(R.xml.settings_page1);
            break;
          case "page2":
            addPreferencesFromResource(R.xml.settings_page2);
            break;
          ...
        }
    }
  }

  @Override
  public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View layout = inflater.inflate(R.layout.settings_page, container, false);
    if (layout != null) {
      AppCompatPreferenceActivity activity = (AppCompatPreferenceActivity) getActivity();
      Toolbar toolbar = (Toolbar) layout.findViewById(R.id.toolbar);
      activity.setSupportActionBar(toolbar);

      ActionBar bar = activity.getSupportActionBar();
      bar.setHomeButtonEnabled(true);
      bar.setDisplayHomeAsUpEnabled(true);
      bar.setDisplayShowTitleEnabled(true);
      bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
      bar.setTitle(getPreferenceScreen().getTitle());
    }
    return layout;
  }

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

    if (getView() != null) {
      View frame = (View) getView().getParent();
      if (frame != null)
        frame.setPadding(0, 0, 0, 0);
    }
  }
}

Enfin, un bref résumé de la façon dont cela fonctionne réellement. La nouvelle AppCompatDelegate nous permet d'utiliser n'importe quelle activité avec les fonctionnalités d'AppCompat, pas seulement celles qui s'étendent des activités réellement présentes dans AppCompat. Cela signifie que nous pouvons transformer le bon vieux PreferenceActivity en un nouveau et ajouter la barre d’outils comme d’habitude. À partir de ce moment, nous pouvons nous en tenir aux anciennes solutions concernant les écrans de préférence et les en-têtes, sans aucun écart par rapport à la documentation existante. Il y a juste un point important: n'utilisez pas onCreate() dans l'activité car cela entraînerait des erreurs. Utilisez onBuildHeaders() pour toutes les opérations telles que l'ajout de la barre d'outils.

La seule différence réelle réside dans le fait que vous pouvez utiliser la même approche avec les fragments pour que cela fonctionne avec les écrans imbriqués. Vous pouvez utiliser leur onCreateView() de la même manière, en gonflant votre propre présentation au lieu de celle du système, en ajoutant la barre d’outils de la même manière que dans l’activité.

48
Gábor

Si vous souhaitez utiliser PreferenceHeaders, vous pouvez utiliser l'approche suivante:

import Android.support.v7.widget.Toolbar;

public class MyPreferenceActivity extends PreferenceActivity

   Toolbar mToolbar;

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

        ViewGroup root = (ViewGroup) findViewById(Android.R.id.content);
        LinearLayout content = (LinearLayout) root.getChildAt(0);
        LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.activity_settings, null);

        root.removeAllViews();
        toolbarContainer.addView(content);
        root.addView(toolbarContainer);

        mToolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar);
    }

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

    // Other methods

}

layout/activity_settings.xml

<LinearLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:orientation="vertical"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <Android.support.v7.widget.Toolbar
        Android:id="@+id/toolbar"
        Android:layout_height="?attr/actionBarSize"
        Android:layout_width="match_parent"
        Android:minHeight="?attr/actionBarSize"
        Android:background="?attr/colorPrimary"
        app:theme="@style/AppTheme"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

</LinearLayout>

Vous pouvez utiliser la disposition que vous préférez ici. Assurez-vous simplement de l'ajuster dans le code Java.

Et enfin, votre fichier avec en-têtes (xml/pref_headers.xml)

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

    <header
        Android:fragment="com.example.FirstFragment"
        Android:title="@string/pref_header_first" />
    <header
        Android:fragment="com.example.SecondFragment"
        Android:title="@string/pref_header_second" />

</preference-headers>
18
Sven Dubbeld

Avec la sortie de Android Support Library 22.1.0 et de la nouvelle AppCompatDelegate, vous trouverez ici un bon exemple d’une implémentation de PreferenceActivity avec prise en charge matérielle avec compatibilité ascendante.

Update Il fonctionne également sur les écrans imbriqués.

https://Android.googlesource.com/platform/development/+/Marshmallow-mr3-release/samples/Support7Demos/src/com/example/Android/supportv7/app/AppCompatPreferenceActivity.Java

17
MrBrightside

Moi aussi, je cherchais une solution pour ajouter la barre d’outillage de support v7 ( API 25 ) à AppCompatPreferenceActivity (créé automatiquement par AndroidStudio lors de l’ajout de SettingsActivity). Après avoir lu plusieurs solutions et essayé chacune d’elles, j’ai eu du mal à faire afficher également les exemples générés PreferenceFragment avec une barre d’outils.

Une solution modifiée qui fonctionnait en quelque sorte allait de " Gabor ".

L'une des mises en garde à laquelle j'ai dû faire face était que 'onBuildHeaders' a déjà tiré une fois. Si vous tournez un périphérique (un téléphone, par exemple) sur le côté, la vue est recréée et PreferenceActivity est à nouveau sans barre d’outils. Toutefois, PreferenceFragments conservera le sien.

J'ai essayé d'utiliser 'onPostCreate' pour appeler 'setContentView', cette opération permettant de recréer la barre d'outils lorsque l'orientation changeant, PreferenceFragments serait alors vide.

Ce que j'ai proposé exploite à peu près tous les conseils et réponses que je pourrais lire sur ce sujet. J'espère que d'autres le trouveront utile aussi.

Nous allons commencer avec le Java

Premier dans (le généré) AppCompatPreferenceActivity.Java J'ai modifié 'setSupportActionBar' ainsi:

public void setSupportActionBar(@Nullable Toolbar toolbar) {
    getDelegate().setSupportActionBar(toolbar);
    ActionBar bar = getDelegate().getSupportActionBar();
    bar.setHomeButtonEnabled(true);
    bar.setDisplayHomeAsUpEnabled(true);
}

Deuxièmement , j'ai créé une nouvelle classe nommée AppCompatPreferenceFragment.Java (c'est courant un nom non utilisé, même s’il ne reste peut-être pas comme ça!):

abstract class AppCompatPreferenceFragment extends PreferenceFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_settings, container, false);
        if (view != null) {
            Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar_settings);
            ((AppCompatPreferenceActivity) getActivity()).setSupportActionBar(toolbar);
        }
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        View frame = (View) getView().getParent();
        if (frame != null) frame.setPadding(0, 0, 0, 0);
    }
}

C'est la partie de la réponse de Gabor qui a fonctionné.

Last , Pour plus de cohérence, nous devons apporter quelques modifications à SettingsActivity.Java :

public class SettingsActivity extends AppCompatPreferenceActivity {

    boolean mAttachedFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mAttachedFragment = false;
        super.onCreate(savedInstanceState);
    }

    @Override
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }

    @Override
    public void onAttachFragment(Fragment fragment) {
        mAttachedFragment = true;
        super.onAttachFragment(fragment);
    }

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

        //if we didn't attach a fragment, go ahead and apply the layout
        if (!mAttachedFragment) {
            setContentView(R.layout.activity_settings);
            setSupportActionBar((Toolbar)findViewById(R.id.toolbar_settings));
        }
    }

    /**
     * This fragment shows general preferences only. It is used when the
     * activity is showing a two-pane settings UI.
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public static class GeneralPreferenceFragment extends AppCompatPreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            addPreferencesFromResource(R.xml.pref_general);
            setHasOptionsMenu(true);

            bindPreferenceSummaryToValue(findPreference("example_text"));
            bindPreferenceSummaryToValue(findPreference("example_list"));
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
            if (id == Android.R.id.home) {
                startActivity(new Intent(getActivity(), SettingsActivity.class));
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
}

Certains codes ont été omis de l'activité par souci de brièveté. Les composants clés ici sont ' onAttachedFragment ', ' onPostCreate ', et que 'GeneralPreferenceFragment' étend désormais la coutume ' AppCompatPreferenceFragment ' au lieu de PreferenceFragment.

Résumé du code : si un fragment est présent, le fragment injecte la nouvelle présentation et appelle la fonction modifiée 'setSupportActionBar'. Si le fragment n'est pas présent, SettingsActivity injecte la nouvelle présentation sur 'onPostCreate'

Passons maintenant au XML (très simple):

activity_settings.xml :

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

    <include
        layout="@layout/app_bar_settings"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />

</LinearLayout>

app_bar_settings.xml :

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:id="@+id/content_frame"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true"
    tools:context=".SettingsActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/AppTheme.NoActionBar.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar_settings"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            Android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.NoActionBar.PopupOverlay" />

    </Android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_settings" />

</Android.support.design.widget.CoordinatorLayout>

content_settings.xml :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:id="@+id/content"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:paddingBottom="@dimen/activity_vertical_margin"
    Android:paddingLeft="@dimen/activity_horizontal_margin"
    Android:paddingRight="@dimen/activity_horizontal_margin"
    Android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".SettingsActivity"
    tools:showIn="@layout/app_bar_settings">

    <ListView
        Android:id="@Android:id/list"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content" />

</RelativeLayout>

Résultat final :

SettingsActivity

GeneralPreferenceFragment

6
SilverX

Bien que les réponses ci-dessus semblent complexes, si vous voulez une solution rapide pour utiliser Toolbar avec l’API 7 et plus tout en prolongeant PreferenceActivity, j’ai reçu l’aide de ce projet ci-dessous.

https://github.com/AndroidDeveloperLB/ActionBarPreferenceActivity

activity_settings.xml

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical" >

<Android.support.v7.widget.Toolbar
    Android:id="@+id/toolbar"
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:background="@color/app_theme_light"
    app:popupTheme="@style/Theme.AppCompat.Light"
    app:theme="@style/Theme.AppCompat" />

<FrameLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:padding="@dimen/padding_medium" >

    <ListView
        Android:id="@Android:id/list"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent" />
</FrameLayout>

SettingsActivity.Java

public class SettingsActivity extends PreferenceActivity {

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

    setContentView(R.layout.activity_settings);

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

    addPreferencesFromResource(R.xml.preferences);

    toolbar.setClickable(true);
    toolbar.setNavigationIcon(getResIdFromAttribute(this, R.attr.homeAsUpIndicator));
    toolbar.setTitle(R.string.menu_settings);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            finish();
        }
    });

}

private static int getResIdFromAttribute(final Activity activity, final int attr) {
    if (attr == 0) {
        return 0;
    }
    final TypedValue typedvalueattr = new TypedValue();
    activity.getTheme().resolveAttribute(attr, typedvalueattr, true);
    return typedvalueattr.resourceId;
}
}
6
midhunhk

J'ai une nouvelle solution (peut-être plus simple) qui utilise les AppCompatPreferenceActivity des exemples de Support v7. Avec ce code en main, j'ai créé ma propre mise en page qui comprend une barre d'outils:

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto" xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent" Android:layout_height="match_parent"
    Android:fitsSystemWindows="true" tools:context="edu.adelphi.Adelphi.ui.activity.MainActivity">

    <Android.support.design.widget.AppBarLayout Android:id="@+id/appbar"
        Android:layout_width="match_parent" Android:layout_height="wrap_content"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar Android:id="@+id/toolbar"
            Android:layout_width="match_parent" Android:layout_height="?attr/actionBarSize"
            Android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay"/>

    </Android.support.design.widget.AppBarLayout>

    <FrameLayout Android:id="@+id/content"
        Android:layout_width="match_parent" Android:layout_height="match_parent"/>

</Android.support.design.widget.CoordinatorLayout>

Ensuite, dans mon AppCompatPreferenceActivity, j'ai modifié setContentView pour créer une nouvelle mise en page et placer la mise en page fournie dans mon FrameLayout:

@Override
public void setContentView(@LayoutRes int layoutResID) {
    View view = getLayoutInflater().inflate(R.layout.toolbar, null);
    FrameLayout content = (FrameLayout) view.findViewById(R.id.content);
    getLayoutInflater().inflate(layoutResID, content, true);
    setContentView(view);
}

Ensuite, je prolonge simplement AppCompatPreferenceActivity, ce qui me permet d’appeler setSupportActionBar((Toolbar) findViewById(R.id.toolbar)) et de gonfler les éléments de menu de la barre d’outils. Tout en gardant les avantages d'un PreferenceActivity.

5
Bryan

Restons simples et propres ici, sans casser aucune disposition incorporée

import Android.support.design.widget.AppBarLayout;
import Android.support.v4.app.NavUtils;
import Android.support.v7.widget.Toolbar;

private void setupActionBar() {
    Toolbar toolbar = new Toolbar(this);

    AppBarLayout appBarLayout = new AppBarLayout(this);
    appBarLayout.addView(toolbar);

    final ViewGroup root = (ViewGroup) findViewById(Android.R.id.content);
    final ViewGroup window = (ViewGroup) root.getChildAt(0);
    window.addView(appBarLayout, 0);

    setSupportActionBar(toolbar);

    // Show the Up button in the action bar.
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
    });
}
5
Samuel

J'ai trouvé cette solution simple en travaillant dessus. Nous devons d’abord créer une mise en page pour l’activité paramètres.

activity_settings.xml

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    tools:context="com.my.package">

    <Android.support.v7.widget.Toolbar
        Android:id="@+id/tool_bar"
        Android:layout_width="match_parent"
        Android:layout_height="?attr/actionBarSize"
        Android:background="?attr/colorPrimary"
        Android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:elevation="@dimen/appbar_elevation"
        app:navigationIcon="?attr/homeAsUpIndicator"
        app:navigationContentDescription="@string/abc_action_bar_up_description"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <ListView
        Android:id="@Android:id/list"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_below="@+id/tool_bar" />

</RelativeLayout>

Assurez-vous d’ajouter une vue de liste avec Android:id="@Android:id/list", sinon cela jettera NullPointerException

La prochaine étape consiste à ajouter (remplacer) la méthode onCreate dans l'activité de paramétrage.

Settings.Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_settings);
    Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);
    toolbar.setTitle(R.string.action_settings);
    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            finish();
        }
    });
}

Assurez-vous d'importer Android.suppoer.v7.widget.Toolbar. Cela devrait fonctionner à peu près sur toutes les API de plus de 16 ans (haricot gelée et plus)

4
Apurva

Je voudrais continuer la solution marquée de James Cross, car après cela, il y a un problème consistant à fermer uniquement l'écran imbriqué actif (PreferenceFragment) afin de ne pas fermer également SettingsActivity.

En fait, cela fonctionne sur tous les écrans imbriqués (donc, je ne comprends pas la solution de Gábor que j'ai essayée sans succès. Cela fonctionne jusqu'à un certain point, mais c'est un fouillis de barres d'outils multiples), car lorsque l'utilisateur clique sur un écran de préférence secondaire , seul le fragment est modifié (voir <FrameLayout Android:id="@+id/content_frame" .../>) pas la barre d’outils qui reste toujours active et visible, mais un comportement personnalisé doit être implémenté pour fermez chaque fragment en conséquence.

Dans la classe principale SettingsActivity qui étend ActionBarActivity, les méthodes suivantes doivent être implémentées. Notez que private setupActionBar() est appelé à partir de onCreate()

private void setupActionBar() {
    Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
    //Toolbar will now take on default Action Bar characteristics
    setSupportActionBar(toolbar);
    getSupportActionBar().setHomeButtonEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case Android.R.id.home:
        onBackPressed();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0) {
        getFragmentManager().popBackStackImmediate();
        //If the last fragment was removed then reset the title of main
        // fragment (if so the previous popBackStack made entries = 0).
        if (getFragmentManager().getBackStackEntryCount() == 0) {
            getSupportActionBar()
                .setTitle(R.string.action_settings_title);
        }
    } else {
        super.onBackPressed();
    }
}

Pour le title de l'écran imbriqué choisi, vous devez obtenir la référence de votre barre d'outils et définir le titre approprié avec toolbar.setTitle(R.string.pref_title_general); (par exemple).

Il y a pas besoin d'implémenter la getSupportActionBar() dans tous les PreferenceFragment puisque seule la vue du fragment est modifiée à chaque validation, pas la barre d'outils;

Il y a inutile de créer une fausse classe ToolbarPreference à ajouter à chaque préférence.xml (voir la réponse de Gábor).

1
Davideas

Voici une bibliothèque que j'ai créée et basée sur du code AOSP, qui ajoute une teinte aux préférences et aux boîtes de dialogue, ajoute une barre d'actions et prend en charge toutes les versions de l'API 7:

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary

1

Eh bien, cela reste un problème pour moi aujourd'hui (18 nov. 2015). J'ai essayé toutes les solutions de ce fil, mais il y avait deux choses principales que je ne pouvais pas résoudre:

  • Les écrans de préférences imbriquées sont apparus sans barre d'outils
  • Les préférences n'avaient pas l'apparence Matériau sur les appareils pré-Lollipop

J'ai donc fini par créer une bibliothèque avec une solution plus compliquée. Fondamentalement, je devais appliquer en interne des styles aux préférences si nous utilisions un périphérique pré-Lollipop et je gérais également les écrans imbriqués à l'aide d'un fragment personnalisé (la restauration de toute la hiérarchie imbriquée en tirant parti de PreferenceScreen clé ).

La bibliothèque est celle-ci: https://github.com/ferrannp/material-preferences

Et si vous êtes intéressé par le code source (trop long pour le poster ici), il s’agit essentiellement du noyau: https://github.com/ferrannp/material-preferences/blob/master/library/ src/main/Java/com/fnp/materialpreferences/PreferenceFragment.Java

1
Ferran Negre