Comment ajouter la barre d'action de la bibliothèque de support dans PreferenceActivity?

128

La compatibilité de la barre d'action a été ajoutée à la bibliothèque de support, révision 18. Elle a maintenant une ActionBarActivityclasse pour créer des activités avec la barre d'action sur les anciennes versions d'Android.

Existe-t-il un moyen d'ajouter la barre d'action de la bibliothèque de support dans PreferenceActivity?

Auparavant, j'utilisais ActionBarSherlock et c'est le cas SherlockPreferenceActivity.

romain
la source
peut-être encore pertinent: ActionBar dans PreferenceActivity
Matthias Robbers
1
Je viens de modifier ma réponse, j'ai trouvé une implémentation de préférence de travail basée sur support-v4 qui fonctionne très bien avec ActionBarActivity. Je l'utilise moi-même.
Ostkontentitan
Si vous le souhaitez, vous pouvez utiliser ma solution: github.com/AndroidDeveloperLB/MaterialStuffLibrary
développeur android

Réponses:

128

EDIT: Dans appcompat-v7 22.1.0, Google a ajouté la classe abstraite AppCompatDelegate en tant que délégué que vous pouvez utiliser pour étendre la prise en charge d'AppCompat à n'importe quelle activité.

Utilisez-le comme ceci:

...
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
...

public class SettingsActivity extends PreferenceActivity {

    private AppCompatDelegate mDelegate;

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

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

    public ActionBar getSupportActionBar() {
        return getDelegate().getSupportActionBar();
    }

    public void setSupportActionBar(@Nullable Toolbar toolbar) {
        getDelegate().setSupportActionBar(toolbar);
    }

    @Override
    public MenuInflater getMenuInflater() {
        return getDelegate().getMenuInflater();
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

    @Override
    public void setContentView(View view) {
        getDelegate().setContentView(view);
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().setContentView(view, params);
    }

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().addContentView(view, params);
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        getDelegate().onPostResume();
    }

    @Override
    protected void onTitleChanged(CharSequence title, int color) {
        super.onTitleChanged(title, color);
        getDelegate().setTitle(title);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getDelegate().onConfigurationChanged(newConfig);
    }

    @Override
    protected void onStop() {
        super.onStop();
        getDelegate().onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getDelegate().onDestroy();
    }

    public void invalidateOptionsMenu() {
        getDelegate().invalidateOptionsMenu();
    }

    private AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, null);
        }
        return mDelegate;
    }
}

Plus de piratage. Code extrait de AppCompatPreferenceActivity.java .

Ľubomír Kučera
la source
veuillez mettre vos extraits de code essentiels dans votre réponse. pour éviter un problème potentiel de lien rompu.
Yohanes Khosiawan 许先汉
merci, cela fonctionne pour moi - je changerais le nouveau LinearLayout dans le premier appel (ViewGroup) getWindow().getDecorView().getRootView()
gonflé
2
@petrsyn Cela fonctionne réellement. Regardez ici et ici pour savoir comment procéder.
Ľubomír Kučera
1
@swooby Vous pourriez souffrir de ce bogue.
Ľubomír Kučera
1
Bon alors où dois-je mettre la barre d'outils dans le XML? Je reçois une NullPointerException
Martin Erlic
78

Il n'y a actuellement aucun moyen de réaliser avec AppCompat. J'ai ouvert un bug en interne.

Chris Banes
la source
6
Merci @Chris. Ce sera bien d'avoir cette fonctionnalité.
Pablo
12
@Chris, avez-vous une idée de la date à laquelle nous pourrions nous attendre PreferenceActivityà être ajoutés ActionBarCompat?
imbryk
3
Je pense qu'il est très peu probable que cette fonctionnalité soit implémentée. Le nombre d'appareils exécutant des versions antérieures d'Android (<4.0) est inférieur à 30% à ce stade, et ce nombre diminue chaque mois.
Roman
1
Cela a été entré il y a presque un an et aucune résolution ??
Someone Somewhere
1
wtf attend toujours ??
Broak
24

J'ai réussi à créer une solution de contournement similaire à celle utilisée par le Google Play Store. Lien vers la réponse originale

Veuillez trouver le dépôt GitHub: ici


Très similaire à votre propre code mais ajouté xml pour permettre le titre défini:

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 :

exemple


MISE À JOUR (compatibilité pain d'épice):

Comme indiqué ici , Gingerbread Devices renvoie 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 TEINTE

Comme indiqué dans de nombreuses notes de développement, PreferenceActivityne prend pas en charge la teinte des éléments, mais en utilisant quelques classes internes, vous POUVEZ y parvenir. C'est jusqu'à ce que ces classes soient supprimées. (Fonctionne avec le support appCompat-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;

Remplacez ensuite la onCreateViewméthode:

@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:

exemple 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. Au lieu de cela, suivez ceci (toujours prioritaire 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;
}

ÉCRANS DE PRÉFÉRENCE EMBARQUÉS

Beaucoup de gens rencontrent des problèmes pour inclure la barre d'outils dans les <PreferenceScreen />s imbriqués , cependant, j'ai trouvé une solution !! - Après de nombreux essais et 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 cela PreferenceScreenest si pénible est parce qu'ils sont basés comme une boîte de dialogue wrapper, 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 Toolbarne permet pas d'élévation et d'ombrage dans les appareils antérieurs à la v21, donc si vous souhaitez avoir une élévation sur votre, Toolbarvous 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 build.gradlefichier:

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 étudié le problème de chevauchement signalé et je ne peux pas reproduire le problème.

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

entrez la description de l'image ici

S'il me manque quelque chose, veuillez me le faire savoir via ce dépôt et j'enquêterai .

David Passmore
la source
1
Génial! Fonctionne comme un charme :)
Tobi
Pourquoi cela ne fonctionne-t-il pas pour le PreferenceScreen imbriqué, mais uniquement pour le PreferenceScreen principal?
Tobi
@Tobi J'ai mis à jour ma réponse pour résoudre le problème imbriqué et expliqué pourquoi. :)
David Passmore
Merci! Votre conseil "AppCompat 22.1" a fait l'affaire pour moi :) Très utile!
Martin Pfeffer
1
pourquoi PreferenceActivity une telle douleur dans le cul à utiliser ??? c'est censé gagner du temps. Je pourrais aussi bien faire une activité régulière et disposer manuellement tous les paramètres dans une disposition linéaire moi-même. Fuuuuck!
Someone Somewhere
12

Nous avons trouvé une implémentation PreferenceFragment basée sur le fragment support-v4:

https://github.com/kolavar/android-support-v4-preferencefragment

Edit: je viens de le tester et ça marche très bien!

Ostkontentitan
la source
@Konstantin, pouvez-vous ajouter un exemple de code? Je l'ai téléchargé, changé l'importation de android.preference.PreferenceFragment à android.support.v4.preference.PreferenceFragment, et je vois qu'il a ajouté des en-têtes au milieu de l'écran, mais pas l'ActionBar en haut
Gavriel
Il n'ajoute pas la barre d'action qui est le travail de l'activité. Malheureusement, je n'ai pas de code d'échantillonnage sous la main, mais cela devrait fonctionner de la même manière que: developer.android.com/reference/android/preference/…
Ostkontentitan
3

L'intégration PreferenceActivityavec ABC n'est pas possible, du moins pour moi. J'ai essayé les deux possibilités que j'ai pu trouver mais aucune n'a fonctionné:

Option 1:

ActionBarPreferenceActivitys'étend PreferenceActivity. Lorsque vous faites cela, vous êtes limité par ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). Vous devez également mettre en œuvre ActionBar.Callbacksce qui n'est pas accessible

Option 2:

ActionBarPreferenceActivitys'étend ActionBarActivity. Cette approche nécessite la réécriture d'un tout nouveau PreferenceActivity, PreferenceManageret peut être PreferenceFragmentce qui signifie que vous avez besoin d'accéder à des classes cachées comme com.android.internal.util.XmlUtils La solution à cela ne peut venir que des développeurs de Google implémentant un ActionBarWrapperqui peut être ajouté à n'importe quelle activité.

Si vous avez vraiment besoin d'une activité de préférence, mon conseil pour le moment est ActionBarSherlock.

Cependant, j'ai réussi à l'implémenter ici .

Maxwell Weru
la source
1
Per4.0 provoque un crash. Je l'ai bifurqué et amélioré. gist.github.com/0e9fe2119b901921b160
TeeTracker
Vérifiez ma solution. Il n'utilise aucune solution de contournement stackoverflow.com/a/25603448/1276636
Sufian
Cela ne résout rien! J'espère que vous comprenez le problème
Maxwell Weru
3

Contexte du problème:

Le PO veut savoir comment mettre MenuItem s dans le ActionBarde PreferenceActivitypour pré-Honeycomb car la bibliothèque de support d'Android a un bogue qui ne permet pas que cela se produise.

Ma solution:

J'ai trouvé un moyen beaucoup plus propre, que déjà proposé, d'atteindre l'objectif (et je l'ai trouvé dans la documentation Android ):

android:parentActivityName

Le nom de classe du parent logique de l'activité. Le nom ici doit correspondre au nom de classe donné à l'attribut android: name de l'élément correspondant.

Le système lit cet attribut pour déterminer quelle activité doit être démarrée lorsque l'utilisation appuie sur le bouton Haut dans la barre d'action. Le système peut également utiliser ces informations pour synthétiser une pile arrière d'activités avec TaskStackBuilder.

Pour prendre en charge les niveaux d'API 4 à 16, vous pouvez également déclarer l'activité parente avec un élément qui spécifie une valeur pour "android.support.PARENT_ACTIVITY". Par exemple:

<activity
    android:name="com.example.app.ChildActivity"
    android:label="@string/title_child_activity"
    android:parentActivityName="com.example.myfirstapp.MainActivity" >
    <!-- Parent activity meta-data to support API level 4+ -->
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.example.app.MainActivity" />
</activity>

Maintenant, faites ce que vous feriez normalement dans votre fichier onOptionsItemSelected(). Puisqu'il fait partie d'Android Docs, il n'a aucun effet secondaire.

Bon codage. :)

Mettre à jour:

Cette solution ne fonctionne plus si vous ciblez Lollipop. Si vous utilisez AppCompat, cette réponse est ce que vous devriez rechercher.

Soufian
la source
Comment cela aide-t-il à obtenir la barre d'action dans l'activité Préférences?
Frozen Crayon
@ArjunU. solution facile - essayez-le et découvrez-le.
Sufian
@ArjunU. Le problème était que le PreferencesActivityn'avait aucun moyen de placer des éléments ActionBar, en particulier le bouton de retour. Ma réponse est une bonne solution pour cela.
Sufian
Merci pour le vote négatif. Une explication comment je pourrais améliorer ma réponse ou qu'est-ce qui n'allait pas?
Sufian
1
@Sufian Merci pour le lien vers ma réponse, heureux que cela aide tout le monde:)
David Passmore
1

J'ai pu obtenir android.app.Actionbaren utilisant getActionBar(). Il a renvoyé une valeur nulle au début ... puis je suis allé au manifeste et j'ai changé le thème en:

android:theme="@style/Theme.AppCompat"

Ensuite, j'ai pu à nouveau avoir la barre d'action. Je suppose que cela ne fonctionnera que pour certains niveaux de construction. Vous voudrez peut-être vérifier le numéro de build ou vérifier si la valeur renvoyée est nulle.

Ce sera bien pour moi car l'application sur laquelle je travaille est pour ICS/4.0+.

RCB
la source
Voici une solution plus simple, celle qui n'utilise aucune solution de contournement stackoverflow.com/a/25603448/1276636
Sufian