web-dev-qa-db-fra.com

Comment ajouter une barre d’action de la bibliothèque de support à PreferenceActivity?

La compatibilité de la barre d’action a été ajoutée à la bibliothèque de support, révision 18. Elle dispose maintenant de la classe ActionBarActivity pour la création d’activités avec Action Bar sur les anciennes versions d’Android.

Est-il possible d'ajouter Action Bar depuis la bibliothèque de support dans PreferenceActivity?

Auparavant, je utilisais ActionBarSherlock et il a SherlockPreferenceActivity.

128
Roman

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 à toute 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 .

128

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

78
Chris Banes

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

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


Très similaire à votre propre code mais ajouté XML pour permettre le titre du jeu:

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

Comme indiqué ici , les périphériques 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 dev, PreferenceActivity ne prend pas en charge la teinte 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

Beaucoup de gens rencontrent des problèmes pour inclure la barre d'outils dans les <PreferenceScreen /> Imbriqués, 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 est un problème, c'est parce qu'ils sont basés sur une boîte de dialogue wrapper. Nous devons donc capturer la présentation 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 périphériques 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.

24
David Passmore

Trouvé une implémentation de 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!

12
Ostkontentitan

Intégrer PreferenceActivity avec 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:

ActionBarPreferenceActivity étend PreferenceActivity. Lorsque vous faites cela, vous êtes limité par ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). Aussi, vous devez implémenter ActionBar.Callbacks qui n'est pas accessible

Option 2:

ActionBarPreferenceActivity étend ActionBarActivity. Cette approche nécessite la réécriture d’un tout nouveau PreferenceActivity, PreferenceManager et peut être PreferenceFragment, ce qui signifie que vous devez avoir accès à des classes cachées comme com.Android.internal.util.XmlUtils La solution à ce problème ne peut venir que des développeurs Google mettant en œuvre un ActionBarWrapper pouvant être ajouté à toute activité.

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

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

3
Maxwell Weru

Problème de fond:

L'OP veut savoir comment on peut mettre MenuItems dans ActionBar de PreferenceActivity pour 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 celui déjà proposé pour atteindre l'objectif (et je l'ai trouvé dans le Android Docs ):

Android:parentActivityName

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

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

Pour prendre en charge les niveaux 4 à 16 de l'API, vous pouvez également déclarer l'activité mère avec un élément spécifiant 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 onOptionsItemSelected(). Puisqu'il fait partie de Android Docs, il n'a pas d'effets secondaires.

Bonne codage. :)

Mise à jour:

Cette solution ne fonctionne plus si vous ciblez Lollipop. Si vous utilisez AppCompat, this answer est ce que vous devriez rechercher.

3
Sufian

J'ai pu obtenir Android.app.Actionbar En 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 avoir la barre d'action à nouveau. Je suppose que cela ne fonctionnera que pour certains niveaux de construction. Donc, vous voudrez peut-être vérifier le numéro de build ou vérifier si la valeur renvoyée est null.

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

1
RCB

Maintenant, la réponse officielle à ce problème a été publiée. Il s’agit de la bibliothèque v7/v14 Preference Support .

Voir Comment utiliser la bibliothèque de support de préférence v7/v14? pour la discussion sur l'utilisation.

0