Come aggiungere la barra delle azioni dalla libreria di supporto in PreferenceActivity?


128

La compatibilità della barra delle azioni è stata aggiunta nella libreria di supporto, revisione 18. Ora ha una ActionBarActivityclasse per la creazione di attività con la barra delle azioni su versioni precedenti di Android.

Esiste un modo per aggiungere la barra delle azioni dalla libreria di supporto in PreferenceActivity?

In precedenza ho usato ActionBarSherlock e ha SherlockPreferenceActivity.



1
ho appena modificato la mia risposta, ho trovato un'implementazione di frammentazione delle preferenze funzionante basata su support-v4 che funziona benissimo con ActionBarActivity. Lo sto usando da solo.
Ostkontentitan,

Se lo desideri, puoi utilizzare la mia soluzione: github.com/AndroidDeveloperLB/MaterialStuffLibrary
sviluppatore Android

Risposte:


128

EDIT: In appcompat-v7 22.1.0 Google ha aggiunto la classe astratta AppCompatDelegate come delegato che puoi utilizzare per estendere il supporto di AppCompat a qualsiasi attività.

Usalo in questo modo:

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

Niente più hacking. Codice preso da AppCompatPreferenceActivity.java .


inserisci i frammenti di codice essenziali nella risposta. per evitare potenziali problemi di collegamento interrotto.
Yohanes Khosiawan 许先汉

grazie, questo funziona per me - cambierei il nuovo LinearLayout nella prima chiamata di gonfiaggio a:(ViewGroup) getWindow().getDecorView().getRootView()
ahmedre

2
@petrsyn Funziona davvero. Guarda qui e qui per scoprire come dovresti farlo.
Ľubomír Kučera,

1
@swooby Potresti soffrire di questo errore.
Ľubomír Kučera,

1
Okay, allora dove inserisco la barra degli strumenti nel file XML? Ricevo una NullPointerException
Martin Erlic il

78

Al momento non è possibile ottenere risultati con AppCompat. Ho aperto un bug internamente.


6
Grazie @Chris. Sarà bello avere questa funzione.
Pablo,

12
@Chris, hai idea di quando potremmo aspettarci PreferenceActivitydi essere aggiunti ActionBarCompat?
imbryk,

3
Penso che sia molto improbabile che questa funzione venga implementata. Il numero di dispositivi che eseguono versioni precedenti di Android (<4.0) a questo punto è inferiore al 30% e questo numero diminuisce ogni mese.
Romano

1
Questo è stato inserito quasi un anno fa e nessuna risoluzione ??
Qualcuno da qualche parte

1
cosa stai ancora aspettando ??
Broak,

24

Sono riuscito a creare una soluzione alternativa simile a quella utilizzata dal Google Play Store. Link alla risposta originale

Si prega di trovare il Repo GitHub: qui


Molto simile al proprio codice ma aggiunto xml per consentire il titolo impostato:

Continuando ad usare 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 :

esempio


AGGIORNAMENTO (Compatibilità Gingerbread):

Come sottolineato qui , Gingerbread Devices sta restituendo NullPointerException su questa linea:

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

FIX:

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

Eventuali problemi con quanto sopra fammi sapere!


AGGIORNAMENTO 2: Workaround di colorazione

Come sottolineato in molte note di sviluppo PreferenceActivitynon supporta la colorazione degli elementi, tuttavia utilizzando alcune classi interne è possibile raggiungere questo obiettivo. Questo fino a quando queste classi non vengono rimosse. (Funziona con appCompat support-v7 v21.0.3).

Aggiungi le seguenti importazioni:

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;

Quindi sovrascrivere il onCreateViewmetodo:

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

esempio 2


AppCompat 22.1

AppCompat 22.1 ha introdotto nuovi elementi colorati, il che significa che non è più necessario utilizzare le classi interne per ottenere lo stesso effetto dell'ultimo aggiornamento. Invece segui questo (ancora prevalente 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;
}

SCHERMATE DI PREFERENZA NIDATE

Molte persone stanno riscontrando problemi con l'inclusione della barra degli strumenti nei nidificati <PreferenceScreen />, tuttavia ho trovato una soluzione !! - Dopo molte prove ed errori!

Aggiungi quanto segue al tuo 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();
        }
    });
}

Il motivo per cui PreferenceScreenè un tale dolore è perché sono basati come una finestra di dialogo wrapper, quindi è necessario acquisire il layout della finestra di dialogo per aggiungere la barra degli strumenti.


Barra degli strumenti Ombra

Progettando l'importazione, il Toolbarnon consente elevazione e ombreggiamento nei dispositivi precedenti alla v21, quindi se desideri avere l'elevazione su di te, Toolbardevi avvolgerla in 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>

Senza dimenticare di aggiungere la libreria di supporto per il design come dipendenza nel build.gradlefile:

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

Ho esaminato il problema di sovrapposizione segnalato e non riesco a riprodurre il problema.

Il codice completo in uso come sopra produce quanto segue:

inserisci qui la descrizione dell'immagine

Se mi manca qualcosa per favore fatemi sapere tramite questo repository e indagherò.


1
Grande! Funziona come un incanto :)
Tobi,

Perché questo non funziona per PreferenceScreen nidificato, ma solo per PreferenceScreen principale?
Tobi,

@Tobi Ho aggiornato la mia risposta per risolvere il problema nidificato e ho spiegato il perché. :)
David Passmore,

Grazie! Il tuo consiglio "AppCompat 22.1" mi ha aiutato :) Molto utile!
Martin Pfeffer,

1
perché è PreferenceActivity un tale dolore nel culo da usare ??? dovrebbe risparmiare tempo. Potrei anche fare un'attività regolare e strutturare manualmente tutte le impostazioni in un layout lineare. Fuuuuck!
Someone Somewhere,

12

Trovato un'implementazione PreferenceFragment basata sul frammento support-v4:

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

Modifica: l'ho appena provato e funziona alla grande!


@ Konstantin, puoi aggiungere qualche esempio di codice? L'ho scaricato, ho cambiato l'importazione da android.preference.PreferenceFragment in android.support.v4.preference.PreferenceFragment e vedo che ha aggiunto alcune intestazioni al centro dello schermo, ma non l'ActionBar in alto
Gavriel

Non aggiunge la barra delle azioni che è il lavoro dell'attività. Sfortunatamente non ho un codice di campionamento a portata di mano, ma dovrebbe funzionare in modo simile a: developer.android.com/reference/android/preference/…
Ostkontentitan

3

L'integrazione PreferenceActivitycon ABC non è possibile, almeno per me. Ho provato le due possibilità che ho trovato, ma nessuna ha funzionato:

Opzione 1:

ActionBarPreferenceActivitysi estende PreferenceActivity. Quando lo fai ti viene limitato da ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). Inoltre è necessario implementare ciò ActionBar.Callbacksche non è accessibile

Opzione 2:

ActionBarPreferenceActivitysi estende ActionBarActivity. Questo approccio richiede riscrivendo un nuovo PreferenceActivity, PreferenceManagere può essere PreferenceFragmentche significa che è necessario l'accesso alle classi nascosti, come com.android.internal.util.XmlUtils La soluzione a questo può venire solo da Google sviluppatori l'implementazione di un ActionBarWrapperche può essere aggiunto a qualsiasi attività.

Se hai davvero bisogno di un'attività di preferenza, il mio consiglio per ora è ActionBarSherlock.

Tuttavia, sono riuscito a implementarlo qui .


1
Per4.0 porta crash. L'ho biforcuta e migliorata. gist.github.com/0e9fe2119b901921b160
TeeTracker

Controlla la mia soluzione. Non utilizza alcuna soluzione alternativa stackoverflow.com/a/25603448/1276636
Sufian

Non risolve nulla! Spero che tu capisca il problema a portata di mano
Maxwell Weru,

3

Sfondo del problema:

Il PO vuole sapere come possiamo mettere MenuItems in ActionBarsu PreferenceActivityper il pre-Honeycomb perché libreria di supporto di Android ha un bug che non permette che questo accada.

La mia soluzione:

Ho trovato un modo molto più pulito, rispetto a quello già proposto, per raggiungere l'obiettivo (e l'ho trovato nei documenti Android ):

android:parentActivityName

Il nome della classe del genitore logico dell'attività. Il nome qui deve corrispondere al nome della classe dato all'attributo android: name dell'attributo corrispondente.

Il sistema legge questo attributo per determinare quale attività deve essere avviata quando l'uso preme il pulsante Su nella barra delle azioni. Il sistema può anche utilizzare queste informazioni per sintetizzare un back stack di attività con TaskStackBuilder.

Per supportare i livelli API 4 - 16, puoi anche dichiarare l'attività principale con un elemento che specifica un valore per "android.support.PARENT_ACTIVITY". Per esempio:

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

Ora fai quello che faresti normalmente nel tuo onOptionsItemSelected(). Dal momento che fa parte di Android Docs, non ha effetti collaterali.

Buona codifica. :)

Aggiornare:

Questa soluzione non funziona più se scegli come target Lollipop. Se stai usando AppCompat, questa risposta è ciò che dovresti cercare.


In che modo aiuta a ottenere la barra delle azioni nell'attività Preferenza?
Frozen Crayon,

@ArjunU. soluzione semplice: provalo e scoprilo.
Sufian,

@ArjunU. Il problema era che PreferencesActivitynon avrebbero avuto modo di inserire oggetti ActionBar, in particolare il pulsante Indietro. La mia risposta è una buona soluzione per questo.
Sufian,

Grazie per il downvote. Qualche spiegazione su come potrei migliorare la mia risposta o cosa non andava?
Sufian,

1
@Sufian Grazie per il collegamento alla mia risposta, felice che aiuti tutti:)
David Passmore,

1

Sono stato in grado di ottenere android.app.Actionbarutilizzando getActionBar(). Inizialmente ha restituito un valore nullo ... poi sono andato al manifest e ho cambiato il tema in:

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

Quindi sono stato in grado di riavere la barra delle azioni. Suppongo che funzionerà solo per determinati livelli di build. Quindi potresti voler fare un controllo per il numero di build o verificare se il valore restituito è null.

Andrà bene per me perché l'app a cui sto lavorando è per ICS/4.0+.


Ecco una soluzione più semplice, che non utilizza soluzioni alternative stackoverflow.com/a/25603448/1276636
Sufian,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.