Wie füge ich eine Aktionsleiste aus der Support-Bibliothek zu PreferenceActivity hinzu?

128

Die Kompatibilität ActionBarActivitymit der Aktionsleiste wurde der Support-Bibliothek, Version 18, hinzugefügt. Sie verfügt jetzt über eine Klasse zum Erstellen von Aktivitäten mit der Aktionsleiste auf älteren Android-Versionen.

Gibt es eine Möglichkeit, die Aktionsleiste aus der Support-Bibliothek hinzuzufügen PreferenceActivity?

Zuvor habe ich ActionBarSherlock verwendet und es hat SherlockPreferenceActivity.

römisch
quelle
vielleicht noch relevant: ActionBar in PreferenceActivity
Matthias Robbers
1
Ich habe gerade meine Antwort bearbeitet und eine funktionierende Präferenzfragment-Implementierung gefunden, die auf support-v4 basiert und hervorragend mit ActionBarActivity zusammenarbeitet. Ich benutze es alleine.
Ostkontentitan
Wenn Sie möchten, können Sie meine Lösung verwenden: github.com/AndroidDeveloperLB/MaterialStuffLibrary
Android-Entwickler

Antworten:

128

BEARBEITEN: In appcompat-v7 22.1.0 hat Google die abstrakte AppCompatDelegate-Klasse als Delegat hinzugefügt, mit dem Sie die Unterstützung von AppCompat auf alle Aktivitäten ausweiten können.

Verwenden Sie es so:

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

Kein Hacken mehr. Code aus AppCompatPreferenceActivity.java .

Ľubomír Kučera
quelle
Bitte geben Sie Ihre wesentlichen Codefragmente in Ihre Antwort ein. um mögliche Probleme mit defekten Links zu vermeiden.
Yohanes Khosiawan 2
danke, das funktioniert bei mir - ich würde das neue LinearLayout beim ersten Aufblasen ändern auf:(ViewGroup) getWindow().getDecorView().getRootView()
ahmedre
2
@petrsyn Es funktioniert tatsächlich. Schauen Sie hier und hier , um herauszufinden, wie Sie es tun sollten.
Kučera
1
@swooby Sie könnten unter diesem Fehler leiden .
Ľubomír Kučera
1
Okay, wo platziere ich die Symbolleiste in der XML? Ich bekomme eine NullPointerException
Martin Erlic
78

Mit AppCompat ist dies derzeit nicht möglich. Ich habe intern einen Fehler geöffnet.

Chris Banes
quelle
6
Danke @Chris. Wird schön sein, diese Funktion zu haben.
Pablo
12
@ Chris, hast du eine Idee, wann wir damit rechnen könnten PreferenceActivity, hinzugefügt zu werden ActionBarCompat?
Imbryk
3
Ich halte es für sehr unwahrscheinlich, dass diese Funktion implementiert wird. Die Anzahl der Geräte, auf denen ältere Android-Versionen (<4.0) ausgeführt werden, liegt derzeit unter 30%, und diese Anzahl sinkt jeden Monat.
Roman
1
Dies wurde vor fast einem Jahr eingegeben und keine Auflösung?
Jemand irgendwo
1
WTF noch warten?
Broak
24

Ich habe es geschafft, eine Problemumgehung zu erstellen, die der im Google Play Store verwendeten ähnelt. Link zur Originalantwort

Das GitHub Repo finden Sie hier


Sehr ähnlich zu Ihrem eigenen Code, aber XML hinzugefügt, um den festgelegten Titel zu ermöglichen:

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

Beispiel


UPDATE (Lebkuchen-Kompatibilität):

Wie hier ausgeführt , geben Gingerbread Devices in dieser Zeile NullPointerException zurück:

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

Alle Probleme mit den oben genannten lassen Sie es mich wissen!


UPDATE 2: TINTING WORKAROUND

Wie in vielen Entwicklungsnotizen erwähnt, wird PreferenceActivitydas Abtönen von Elementen nicht unterstützt. Wenn Sie jedoch einige interne Klassen verwenden, können Sie dies erreichen. Bis diese Klassen entfernt werden. (Funktioniert mit appCompat support-v7 v21.0.3).

Fügen Sie die folgenden Importe hinzu:

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;

Überschreiben Sie dann die onCreateViewMethode:

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

Beispiel 2


AppCompat 22.1

AppCompat 22.1 führte neue getönte Elemente ein, sodass die internen Klassen nicht mehr verwendet werden müssen, um den gleichen Effekt wie beim letzten Update zu erzielen. Folgen Sie stattdessen diesem (immer noch überschreibenden 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;
}

NESTED PREFERENCE SCREENS

Viele Leute haben Probleme mit der Aufnahme der Symbolleiste in verschachtelte <PreferenceScreen />s, aber ich habe eine Lösung gefunden !! - Nach viel Versuch und Irrtum!

Fügen Sie Folgendes hinzu 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();
        }
    });
}

Der Grund dafür PreferenceScreenist, dass sie als Wrapper-Dialogfeld basieren. Daher müssen wir das Dialogfeld-Layout erfassen, um die Symbolleiste hinzuzufügen.


Symbolleistenschatten

Durch das Importieren von Designs ist das ToolbarAufheben und Abschatten in Geräten vor Version 21 nicht möglich. Wenn Sie also eine Höhe auf Ihrem Gerät haben möchten, müssen ToolbarSie diese in Folgendes einschließen 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>

Nicht zu vergessen, dass Sie die Design Support-Bibliothek als Abhängigkeit in der build.gradleDatei hinzufügen :

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

Ich habe das gemeldete überlappende Problem untersucht und kann das Problem nicht reproduzieren.

Der oben verwendete vollständige Code erzeugt Folgendes:

Geben Sie hier die Bildbeschreibung ein

Wenn mir etwas fehlt, lass es mich bitte über dieses Repo wissen und ich werde es untersuchen.

David Passmore
quelle
1
Toll! Funktioniert wie ein Zauber :)
Tobi
Warum funktioniert dies nicht für den verschachtelten PreferenceScreen, sondern nur für den Haupt-PreferenceScreen?
Tobi
@Tobi Ich habe meine Antwort aktualisiert, um das verschachtelte Problem zu lösen, und erklärt, warum. :)
David Passmore
Vielen Dank! Ihr Rat "AppCompat 22.1" hat den Trick für mich getan :) Sehr nützlich!
Martin Pfeffer
1
Warum ist PreferenceActivity so ein Schmerz im Arsch zu benutzen ? Es soll Zeit sparen. Ich könnte genauso gut eine regelmäßige Aktivität durchführen und alle Einstellungen manuell in einem linearen Layout selbst anordnen. Fuuuuck!
Jemand irgendwo
12

Es wurde eine PreferenceFragment-Implementierung basierend auf support-v4 Fragment gefunden:

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

Edit: Ich habe es gerade getestet und es funktioniert super!

Ostkontentitan
quelle
@Konstantin, können Sie ein Codebeispiel hinzufügen? Ich habe es heruntergeladen, den Import von android.preference.PreferenceFragment in android.support.v4.preference.PreferenceFragment geändert und einige Header in der Mitte des Bildschirms hinzugefügt, aber nicht die ActionBar oben
Gavriel
Die Aktionsleiste, die den Job der Aktivität darstellt, wird nicht hinzugefügt. Leider habe ich keinen Beispielcode zur Hand, aber er sollte ähnlich funktionieren wie: developer.android.com/reference/android/preference/…
Ostkontentitan
3

Eine Integration PreferenceActivityin ABC ist zumindest für mich nicht möglich. Ich habe die beiden Möglichkeiten ausprobiert, die ich finden konnte, aber keine hat funktioniert:

Option 1:

ActionBarPreferenceActivityerstreckt sich PreferenceActivity. Wenn Sie dies tun, werden Sie durch eingeschränkt ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). Auch müssen Sie implementierenActionBar.Callbacks die nicht zugegriffen werden kann

Option 2:

ActionBarPreferenceActivityerstreckt sich ActionBarActivity. Dieser Ansatz erfordert Umschreiben eine ganz neue PreferenceActivity, PreferenceManagerund sein kann , PreferenceFragmentdas heißt , Sie Zugriff auf versteckte Klassen wie müssen com.android.internal.util.XmlUtils die Lösung dieses kann nur von Google kommen Devs eine Implementierung , ActionBarWrapperdie auf jede Aktivität hinzugefügt werden können.

Wenn Sie wirklich eine Präferenzaktivität benötigen, ist mein Rat für den Moment ActionBarSherlock.

Es ist mir jedoch gelungen, es hier umzusetzen .

Maxwell Weru
quelle
1
Per4.0 bringt Absturz. Ich habe es gegabelt und verbessert. gist.github.com/0e9fe2119b901921b160
TeeTracker
Überprüfen Sie meine Lösung. Es verwendet keine Problemumgehung stackoverflow.com/a/25603448/1276636
Sufian
Es löst nichts! Ich hoffe, Sie verstehen das Problem
Maxwell Weru
3

Problem Hintergrund:

Das OP möchte wissen, wie wir MenuItems in das ActionBarvon setzen könnenPreferenceActivity für Pre-Honeycomb weil Android Support - Bibliothek hat einen Fehler , der nicht dies geschehen läßt.

Meine Lösung:

Ich habe einen viel saubereren Weg gefunden, als bereits vorgeschlagen, um das Ziel zu erreichen (und habe es in den Android-Dokumenten gefunden ):

android:parentActivityName

Der Klassenname des logischen übergeordneten Elements der Aktivität. Der Name hier muss mit dem Klassennamen übereinstimmen, der dem Attribut android: name des entsprechenden Elements zugewiesen wurde.

Das System liest dieses Attribut, um zu bestimmen, welche Aktivität gestartet werden soll, wenn die Verwendung die Schaltfläche Nach oben in der Aktionsleiste drückt. Das System kann diese Informationen auch verwenden, um einen Backstack von Aktivitäten mit TaskStackBuilder zu synthetisieren.

Um die API-Ebenen 4 bis 16 zu unterstützen, können Sie die übergeordnete Aktivität auch mit einem Element deklarieren, das einen Wert für "android.support.PARENT_ACTIVITY" angibt. Beispielsweise:

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

Tun Sie jetzt, was Sie normalerweise in Ihrem tun würden onOptionsItemSelected(). Da es Teil von Android Docs ist, hat es keine Nebenwirkungen.

Viel Spaß beim Codieren. :) :)

Aktualisieren:

Diese Lösung funktioniert nicht mehr, wenn Sie auf Lollipop abzielen. Wenn Sie AppCompat verwenden, ist diese Antwort genau das, wonach Sie suchen sollten.

Sufian
quelle
Wie hilft dies dabei, die Aktionsleiste in der Voreinstellungsaktivität aufzurufen?
Frozen Crayon
@ArjunU. einfache Lösung - probieren Sie es aus und finden Sie es heraus.
Sufian
@ArjunU. Das Problem war, dass PreferencesActivityes keine Möglichkeit gab, Gegenstände zu platzieren ActionBar, insbesondere den Zurück-Button. Meine Antwort ist eine gute Lösung dafür.
Sufian
Danke für die Ablehnung. Irgendeine Erklärung, wie ich meine Antwort verbessern könnte oder was falsch war?
Sufian
1
@ Sufian Vielen Dank für den Link zu meiner Antwort, froh, dass es allen hilft:)
David Passmore
1

Ich konnte damit android.app.Actionbarumgehen getActionBar(). Zuerst wurde ein Nullwert zurückgegeben ... dann ging ich zum Manifest und änderte das Thema in:

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

Dann konnte ich die Actionbar wieder haben. Ich gehe davon aus, dass dies nur für bestimmte Build-Levels funktioniert. Daher möchten Sie möglicherweise die Build-Nummer überprüfen oder prüfen, ob der zurückgegebene Wert null ist.

Es wird gut für mich sein, weil die App, an der ich arbeite, für ICS/4.0+ ist.

RCB
quelle
Hier ist eine einfachere Lösung, die keine Problemumgehungen
Sufian