wake-up-neo.com

Erstellen eines Einstellungsbildschirms mit der Support-Symbolleiste (v21)

Ich hatte Probleme bei der Verwendung der neuen Materialdesign-Symbolleiste in der Unterstützungsbibliothek auf einem Einstellungsbildschirm.

Ich habe eine settings.xml-Datei wie folgt:

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

Die Zeichenfolgen werden an anderer Stelle definiert.

114
James Cross

Sie können ein PreferenceFragment als Alternative zu PreferenceActivity verwenden. Hier ist also das Beispiel für das Umschließen von Activity:

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

Und hier ist die Layoutdatei (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>

Und zum Schluss das PreferenceFragment:

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

Ich hoffe das hilft jemandem.

106
James Cross

Finden Sie bitte das GitHub Repo: hier


Ein bisschen zu spät zur Party, aber dies ist meine Lösung, die ich als Workaround verwende, um weiterhin PreferenceActivity zu verwenden:

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 (Lebkuchen-Kompatibilität):

Gemäß den Kommentaren geben Gingerbread Devices NullPointerException in dieser Zeile 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();
            }
        });
    }
}

Probleme mit den oben genannten lassen Sie es mich wissen!


UPDATE 2: ABHILFE ZUM TÖNEN

Wie in vielen Dev Notes erwähnt, unterstützt PreferenceActivity das Abtönen von Elementen nicht. Durch die Verwendung einiger interner Klassen können Sie dies jedoch erreichen. Bis diese Klassen entfernt sind. (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 Methode 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

In AppCompat 22.1 wurden neue getönte Elemente eingeführt, sodass die internen Klassen nicht mehr verwendet werden müssen, um den gleichen Effekt wie beim letzten Update zu erzielen. Folgen Sie stattdessen diesen Anweisungen (überschreiben Sie immer noch 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 PRÄFERENZBILDSCHIRME

Viele Leute haben Probleme damit, die Symbolleiste in ein verschachteltes <PreferenceScreen /> aber ich habe eine lösung gefunden !! - Nach vielem Ausprobieren!

Fügen Sie Ihrem SettingsActivity Folgendes hinzu:

@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, warum PreferenceScreen so schmerzhaft ist, liegt darin, dass sie auf einem Wrapper-Dialogfeld basieren. Daher müssen wir das Dialogfeldlayout erfassen, um die Symbolleiste hinzuzufügen.


Symbolleiste Schatten

Durch das Importieren von Toolbar ist in Geräten vor Version 21 keine Erhöhung und Schattenbildung möglich. Wenn Sie also eine Erhöhung in Ihrem Toolbar haben möchten, müssen Sie diese in ein AppBarLayout einwickeln ] _:

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, das Hinzufügen der Design Support-Bibliothek als Abhängigkeit in build.gradle Datei:

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 es nicht reproduzieren.

Der wie oben verwendete vollständige Code erzeugt Folgendes:

enter image description here

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

108
David Passmore

Völlig neues Update.

Nach einigem Experimentieren habe ich die funktionierende AppCompat 22.1+ -Lösung für verschachtelte Einstellungsbildschirme gefunden.

Erstens müssen Sie, wie in vielen Antworten erwähnt (einschließlich einer hier), das neue AppCompatDelegate verwenden. Verwenden Sie entweder die Datei AppCompatPreferenceActivity.Java Aus den Support-Demos ( https://Android.googlesource.com/platform/development/+/58bf5b99e6132332afb8b44b4c8cedf5756ad464/samples/Support7Demos/src/com/example/Avroid/ /app/AppCompatPreferenceActivity.Java ) und erweitern Sie es einfach oder kopieren Sie die relevanten Funktionen in Ihr eigenes PreferenceActivity. Ich werde hier den ersten Ansatz zeigen:

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

Das dazugehörige Layout ist recht einfach und üblich (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>

Die Einstellungen selbst sind wie gewohnt definiert (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>

Kein wirklicher Unterschied zu Lösungen im Netz bis zu diesem Punkt. Eigentlich können Sie dies auch dann verwenden, wenn Sie keine verschachtelten Bildschirme, keine Überschriften, sondern nur einen einzigen Bildschirm haben.

Wir verwenden ein gemeinsames PreferenceFragment für alle tieferen Seiten, differenziert durch die extra -Parameter in den Headern. Jede Seite enthält eine separate XML-Datei mit einem gemeinsamen PreferenceScreen (xml/settings_page1.xml Et al.). Das Fragment verwendet dasselbe Layout wie die Aktivität, einschließlich der Symbolleiste.

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

Abschließend eine kurze Zusammenfassung, wie dies tatsächlich funktioniert. Mit dem neuen AppCompatDelegate können wir jede Aktivität mit AppCompat-Funktionen verwenden, nicht nur die, die sich von den Aktivitäten in AppCompat erstrecken. Dies bedeutet, dass wir das gute alte PreferenceActivity in ein neues verwandeln und die Symbolleiste wie gewohnt hinzufügen können. Von diesem Punkt an können wir uns an die alten Lösungen für Voreinstellungsbildschirme und Kopfzeilen halten, ohne von der vorhandenen Dokumentation abzuweichen. Es gibt nur einen wichtigen Punkt: Verwenden Sie in der Aktivität nicht onCreate(), da dies zu Fehlern führt. Verwenden Sie onBuildHeaders() für alle Operationen wie das Hinzufügen der Symbolleiste.

Der einzige wirkliche Unterschied besteht darin, dass Sie bei verschachtelten Bildschirmen den gleichen Ansatz für die Fragmente verwenden können. Sie können ihre onCreateView() auf dieselbe Weise verwenden, indem Sie Ihr eigenes Layout anstelle des Systemlayouts aufblasen und die Symbolleiste auf dieselbe Weise wie in der Aktivität hinzufügen.

48
Gábor

Wenn Sie PreferenceHeaders verwenden möchten, können Sie den folgenden Ansatz verwenden:

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>

Sie können hier jedes Layout verwenden, das Sie bevorzugen. Stellen Sie jedoch sicher, dass Sie es auch im Code Java) anpassen.

Und schließlich Ihre Datei mit Headern (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

Mit der Veröffentlichung der Android Support Library 22.1.0 und des neuen AppCompatDelegate finden Sie hier ein schönes Beispiel für eine Implementierung der PreferenceActivity mit Materialunterstützung mit Abwärtskompatibilität.

Update Funktioniert auch auf verschachtelten Bildschirmen.

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

17
MrBrightside

Auch ich habe nach einer Lösung gesucht, um die v7 Support-Symbolleiste ( API 25 ) zur AppCompatPreferenceActivity (die automatisch erstellt wird) hinzuzufügen von AndroidStudio beim Hinzufügen einer SettingsActivity). Nachdem ich mehrere Lösungen gelesen und ausprobiert hatte, bemühte ich mich, die generierten PreferenceFragment-Beispiele auch mit einer Symbolleiste anzuzeigen.

Eine modifizierte Lösung, die irgendwie funktioniert hat, war von " Gabor ".

Einer der Vorbehalte, mit denen ich konfrontiert war, war, dass 'onBuildHeaders' nur einmal ausgelöst wurde. Wenn Sie ein Gerät (z. B. ein Telefon) zur Seite drehen, wird die Ansicht neu erstellt und die PreferenceActivity wird wieder ohne Symbolleiste angezeigt. Die PreferenceFragments behalten jedoch ihre.

Ich habe versucht, 'onPostCreate' zu verwenden, um 'setContentView' aufzurufen, während dies dazu beitrug, die Symbolleiste neu zu erstellen, wenn sich die Ausrichtung änderte. PreferenceFragments wurde dann leer gemacht.

Was ich mir bei fast jedem Tipp und jeder Antwort ausgedacht habe, konnte ich zu diesem Thema nachlesen. Ich hoffe, andere finden es auch nützlich.

Beginnen wir mit Java

Zuerst in (der generierten) AppCompatPreferenceActivity.Java habe ich 'setSupportActionBar' folgendermaßen geändert:

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

Zweitens habe ich eine neue Klasse mit dem Namen AppCompatPreferenceFragment.Java erstellt (es ist derzeit ein nicht verwendeter Name, auch wenn dies möglicherweise nicht der Fall ist !):

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

Dies ist der Teil von Gabors Antwort, der funktioniert hat.

Last , Um die Konsistenz zu gewährleisten, müssen wir einige Änderungen an SettingsActivity.Java vornehmen:

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

Einige Codes wurden aus Gründen der Kürze aus der Aktivität herausgenommen. Die Schlüsselkomponenten hier sind ' onAttachedFragment ', ' onPostCreate ' und das 'GeneralPreferenceFragment' erweitert jetzt das custom ' AppCompatPreferenceFragment 'anstelle von PreferenceFragment.

Code Summary : Wenn ein Fragment vorhanden ist, fügt das Fragment das neue Layout ein und ruft die geänderte Funktion 'setSupportActionBar' auf. Wenn das Fragment nicht vorhanden ist, fügt SettingsActivity das neue Layout in 'onPostCreate' ein.

Nun zu [~ # ~] xml [~ # ~] (sehr einfach):

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>

Endergebnis :

SettingsActivity

GeneralPreferenceFragment

6
SilverX

Die obigen Antworten scheinen zwar ausführlich zu sein, aber wenn Sie eine schnelle Lösung für die Verwendung der Symbolleiste mit der Unterstützungs-API 7 und höher wünschen, während Sie PreferenceActivity erweitern, habe ich von diesem Projekt unten Hilfe erhalten.

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

Ich habe eine neue (möglicherweise ordentlichere) Lösung, die AppCompatPreferenceActivity aus den Support v7-Beispielen verwendet. Mit diesem Code habe ich mein eigenes Layout erstellt, das eine Symbolleiste enthält:

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

Dann änderte ich in meinem AppCompatPreferenceActivitysetContentView, um ein neues Layout zu erstellen, und platziere das bereitgestellte Layout in meinem 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);
}

Dann erweitere ich einfach AppCompatPreferenceActivity, um setSupportActionBar((Toolbar) findViewById(R.id.toolbar)) aufzurufen und die Menüelemente in der Symbolleiste aufzublasen. Dabei bleiben die Vorteile einer PreferenceActivity erhalten.

5
Bryan

Lassen Sie es uns hier einfach und übersichtlich halten, ohne das eingebaute Layout zu beschädigen

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

Ich habe diese einfache Lösung gefunden, als ich daran gearbeitet habe. Zuerst müssen wir ein Layout für die Einstellungsaktivität erstellen.

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>

Stelle sicher, dass du eine Listenansicht mit Android:id="@Android:id/list" Hinzufügst, sonst wirft es NullPointerException

Der nächste Schritt ist das Hinzufügen der Methode (Überschreiben) onCreate in Ihrer Einstellungsaktivität

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

Stellen Sie sicher, dass Sie Android.suppoer.v7.widget.Toolbar Importieren. Dies sollte auf allen APIs über 16 (Jelly Bean und höher) ziemlich gut funktionieren.

4
Apurva

Ich möchte die markierte Lösung von James Cross fortsetzen, da es danach ein Problem gibt, nur den aktiven verschachtelten Bildschirm (PreferenceFragment) zu schließen, um nicht auch die SettingsActivity zu schließen.

Tatsächlich funktioniert es auf allen verschachtelten Bildschirmen (daher verstehe ich die Lösung von Gábor, die ich erfolglos ausprobiert habe, nicht. Es funktioniert bis zu einem gewissen Punkt, aber es ist ein Durcheinander mehrerer Symbolleisten), weil der Benutzer auf einen Bildschirm mit untergeordneten Einstellungen klickt , nur das Fragment wird geändert (siehe <FrameLayout Android:id="@+id/content_frame" .../>), nicht die Symbolleiste, die immer aktiv und sichtbar bleibt, sondern ein benutzerdefiniertes Verhalten sollte implementiert werden jedes Fragment entsprechend zu schließen.

In der Hauptklasse SettingsActivity, die ActionBarActivity erweitert, sollten die folgenden Methoden implementiert werden. Beachten Sie, dass private setupActionBar() von onCreate() aufgerufen wird

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

Für das title des ausgewählten verschachtelten Bildschirms sollten Sie die Referenz Ihrer Symbolleiste abrufen und den entsprechenden Titel mit toolbar.setTitle(R.string.pref_title_general); (zum Beispiel) festlegen.

Es gibt keine Notwendigkeit, die Funktion getSupportActionBar() in allen PreferenceFragment zu implementieren, da bei jedem Commit nur die Ansicht des Fragments geändert wird, nicht die Symbolleiste.

Es gibt keine Notwendigkeit, eine gefälschte ToolbarPreference-Klasse zu erstellen, die in jede preference.xml eingefügt wird (siehe Gábors Antwort).

1
Davideas

Hier ist eine Bibliothek, die ich erstellt habe und die auf AOSP-Code basiert, der sowohl die Voreinstellungen als auch die Dialogfelder einfärbt, eine Aktionsleiste hinzufügt und alle Versionen von API 7 unterstützt:

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary

1

Nun, das ist heute noch ein Problem für mich (18. November 2015). Ich habe alle Lösungen aus diesem Thread ausprobiert, aber es gab zwei wichtige Dinge, die ich nicht lösen konnte:

  • Verschachtelte Einstellungsbildschirme wurden ohne Symbolleiste angezeigt
  • Preferences hatte auf Geräten vor Lollipop nicht das Aussehen von Material

Also habe ich eine Bibliothek mit einer komplizierteren Lösung erstellt. Grundsätzlich musste ich Stile intern auf die Voreinstellungen anwenden, wenn wir ein Gerät vor Lollipop verwendeten. Außerdem behandelte ich die verschachtelten Bildschirme mithilfe eines benutzerdefinierten Fragments (Wiederherstellung der gesamten verschachtelten Hierarchie unter Ausnutzung des PreferenceScreen Taste ).

Die Bibliothek ist diese: https://github.com/ferrannp/material-preferences

Und wenn Sie sich für den Quellcode interessieren (zu lange, um ihn hier zu veröffentlichen), ist dies im Grunde der Kern davon: https://github.com/ferrannp/material-preferences/blob/master/library/ src/main/Java/com/fnp/materialpreferences/PreferenceFragment.Java

1
Ferran Negre