Membuat Layar Preferensi dengan dukungan (v21) Toolbar

116

Saya mengalami masalah saat menggunakan toolbar Desain Material baru di pustaka dukungan pada layar Preferensi.

Saya memiliki file settings.xml seperti di bawah ini:

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

String ditentukan di tempat lain.

James Cross
sumber
stackoverflow.com/a/27455363/2247612 Jawaban ini memiliki solusi sempurna untuk Pustaka Dukungan
harishannam

Jawaban:

110

Silakan temukan Repo GitHub: Di Sini


Agak terlambat ke pesta, tapi ini solusi saya yang saya gunakan sebagai solusi untuk terus menggunakan 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 :

contoh


UPDATE (Kompatibilitas Gingerbread):

Sesuai komentar, Perangkat Gingerbread mengembalikan NullPointerException di baris ini:

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

MEMPERBAIKI:

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

Setiap masalah di atas beri tahu saya!


UPDATE 2: SOLUSI PENCETAKAN

Seperti yang ditunjukkan di banyak catatan pengembang PreferenceActivitytidak mendukung pewarnaan elemen, namun dengan memanfaatkan beberapa kelas internal Anda DAPAT mencapai ini. Itu sampai kelas-kelas ini dihapus. (Bekerja menggunakan appCompat support-v7 v21.0.3).

Tambahkan impor berikut:

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;

Kemudian ganti onCreateViewmetode:

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

contoh 2


AppCompat 22.1

AppCompat 22.1 memperkenalkan elemen berwarna baru, yang berarti bahwa tidak perlu lagi menggunakan kelas internal untuk mencapai efek yang sama seperti pembaruan terakhir. Sebagai gantinya ikuti ini (masih menimpa 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;
}

LAYAR PREFERENSI NESTED

Banyak orang mengalami masalah dengan memasukkan Toolbar secara bersarang <PreferenceScreen />, tetapi saya telah menemukan solusinya !! - Setelah banyak trial and error!

Tambahkan yang berikut ini ke Anda 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();
        }
    });
}

Alasan yang PreferenceScreensangat merepotkan adalah karena mereka berbasis sebagai dialog pembungkus, jadi kita perlu menangkap tata letak dialog untuk menambahkan toolbar ke dalamnya.


Bayangan Toolbar

Dengan desain mengimpor Toolbartidak memungkinkan untuk elevasi dan shadowing di perangkat pra-v21, jadi jika Anda ingin memiliki elevasi pada Anda, ToolbarAnda perlu membungkusnya dalam 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>

Tidak lupa untuk menambahkan pustaka Dukungan Desain sebagai dependensi dalam 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

Saya telah menyelidiki masalah tumpang tindih yang dilaporkan dan saya tidak dapat mereproduksi masalah tersebut.

Kode lengkap yang digunakan seperti di atas menghasilkan yang berikut ini:

masukkan deskripsi gambar di sini

Jika saya melewatkan sesuatu, beri tahu saya melalui repo ini dan saya akan menyelidikinya.

David Passmore
sumber
itu memberikan pengecualian nullpointer di roti jahe, root adalah null .. ada solusi?
danQlimax
solusi Anda bekerja dengan baik. tetapi ada masalah dengan pendekatan ini, bahkan tanpa memperluas ActionBarActivity yang wajib (dari dokumentasi) untuk mendapatkan tema material di <5.0, colorAccent (hanya untuk membuat contoh) tidak diterapkan ke kotak centang di perangkat <5.0. Ini tampaknya sangat menyakitkan..mungkin saya harus menghapus aktivitas preferensi dan menggunakan tata letak linier untuk mensimulasikan layar preferensi, jika tidak, saya tidak melihat cara apa pun untuk menggunakan tema material di perangkat dari api level 8 hingga 21. Fragmen preferensi adalah "hanya"> 11 :(
danQlimax
1
@andQlimax Saya telah memperbarui jawaban saya dengan solusi untuk masalah pewarnaan
David Passmore
3
@DavidPassmore bagi saya daftar preferensi tumpang tindih pada bilah alat
Shashank Srivastava
1
@ShashankSrivastava Ini tercermin jika Anda menggunakan Android 6, saya sedang mengerjakan solusi untuk ini. Terima kasih atas pembaruannya.
David Passmore
107

Anda dapat menggunakan PreferenceFragment, sebagai alternatif PreferenceActivity. Jadi, inilah Activitycontoh pembungkusnya :

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

Dan inilah file tata letaknya (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>

Dan akhirnya PreferenceFragment:

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

Saya harap ini membantu seseorang.

James Cross
sumber
39
Saya telah mencoba pendekatan ini. Masalahnya adalah itu tidak menampilkan toolbar di layar preferensi anak.
Madhur Ahuja
2
Saya pikir dia sedang berbicara tentang PreferenceScreen yang disematkan di XML preferensi root.
Lucas S.
5
Saya menyukai pendekatannya, tetapi sayangnya tidak akan berfungsi jika api target kurang dari API 11
midhunhk
12
Ini tidak akan berhasil dengan keduanya. Secara praktis, tampaknya tidak ada cara untuk membuat layar preferensi bersarang yang dirancang secara material, toolbared. Jika Anda menggunakan ActionBarActivityuntuk mendapatkan toolbar dan fungsi terkait, tidak akan ada onBuildHeaders()penggantian dan tidak ada dukungan preferensi aktual dalam aktivitas. Jika Anda menggunakan yang lama PreferenceActivity, Anda tidak memiliki bilah alat dan fungsi terkait (ya, Anda dapat memiliki Toolbardan tata letak tetapi Anda tidak dapat memanggil setSupportActionBar(). Jadi, baik dengan header preferensi atau layar preferensi bersarang, kami tampaknya macet.
Gábor
1
Saya setuju dengan komentar Gabor. Solusi ini tidak berfungsi secara umum. Ada yang lebih baik di bawah ini dengan meniru Toolbar (tidak ada ActionBar, tapi siapa peduli) dan juga lib dukungan baru yang dirilis dengan AppCompatDelegate terpasang.
Eugene Wechsler
48

Pembaruan yang benar-benar baru.

Dengan beberapa eksperimen, saya tampaknya telah menemukan solusi AppCompat 22.1+ yang berfungsi untuk layar preferensi bersarang.

Pertama, seperti yang disebutkan dalam banyak jawaban (termasuk satu di sini), Anda harus menggunakan yang baru AppCompatDelegate. Gunakan AppCompatPreferenceActivity.javafile dari demo dukungan ( https://android.googlesource.com/platform/development/+/58bf5b99e6132332afb8b44b4c8cedf5756ad464/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java ) dan cukup expand darinya, atau salin fungsi yang relevan ke milik Anda PreferenceActivity. Saya akan menunjukkan pendekatan pertama di sini:

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

Tata letak yang menyertainya agak sederhana dan biasa ( 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>

Preferensi itu sendiri didefinisikan seperti biasa ( 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>

Tidak ada perbedaan nyata untuk solusi di internet sampai saat ini. Sebenarnya, Anda dapat menggunakan ini meskipun Anda tidak memiliki layar bersarang, tidak ada header, hanya satu layar.

Kami menggunakan kesamaan PreferenceFragmentuntuk semua halaman yang lebih dalam, dibedakan oleh extraparameter di header. Setiap halaman akan memiliki XML terpisah dengan bagian PreferenceScreendalamnya yang sama ( xml/settings_page1.xmlet al.). Fragmen tersebut menggunakan tata letak yang sama dengan aktivitas, termasuk toolbar.

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

Terakhir, ringkasan singkat tentang cara kerjanya. Yang baru AppCompatDelegatememungkinkan kita menggunakan aktivitas apa pun dengan fitur AppCompat, tidak hanya yang diperluas dari aktivitas yang sebenarnya di AppCompat. Ini berarti kita dapat mengubah yang lama yang bagus PreferenceActivitymenjadi yang baru dan menambahkan toolbar seperti biasa. Sejak saat itu, kami dapat tetap menggunakan solusi lama terkait layar dan header preferensi, tanpa penyimpangan dari dokumentasi yang ada. Hanya ada satu poin penting: jangan gunakan onCreate()dalam aktivitas karena akan menyebabkan error. MenggunakanonBuildHeaders() untuk semua operasi seperti menambahkan toolbar.

Satu-satunya perbedaan nyata adalah, dan itulah yang membuatnya berfungsi dengan layar bersarang adalah Anda dapat menggunakan pendekatan yang sama dengan fragmen. Anda dapat menggunakan onCreateView()cara yang sama, memekarkan tata letak Anda sendiri daripada tata letak sistem, menambahkan bilah alat dengan cara yang sama seperti dalam aktivitas.

Gábor
sumber
2
Solusi kecil yang bagus! Ini adalah satu-satunya solusi yang saya temukan yang akan menampilkan toolbar material pada PreferenceScreen turunan. Kerja bagus Pak.
String
Saya menggunakan sumber daya dari pustaka appcompat untuk up-icon:R.drawable.abc_ic_ab_back_mtrl_am_alpha
Ridcully
Dengan solusi ini, saya pikir Toolbar akan menggulir dengan konten, bukan? Karena ini hanya item di ListView internal.
tasomaniac
Tidak dengan solusi baru yang diperbarui ini. Itu bekerja seperti yang diharapkan.
Gábor
Anehnya, solusi ini tampaknya tidak mengenali PreferenceFragmentCompatalih-alih PreferenceFragment. Menyiapkan preference-headerdengan xmlns:app="http://schemas.android.com/apk/res-auto" dan kemudian app:fragmentbukannya android:fragmentmemuat layar prefs baru. Jadi mengalami masalah dengan kompatibilitas mundur ... saran?
menggemukkan
18

Jika Anda ingin menggunakan PreferenceHeaders, Anda dapat menggunakan pendekatan berikut:

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>

Anda dapat menggunakan tata letak apa pun yang Anda inginkan di sini, pastikan Anda menyesuaikannya dalam kode Java juga.

Dan terakhir, file Anda dengan header (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>
Sven Dubbeld
sumber
root.addView (toolbar); Mengapa? root.addView (toolbarContainer);
Crossle Song
Ups, melewatkan variabel saat mengganti nama, perbaiki.
Sven Dubbeld
1
Jawaban yang sangat bagus. Kuncinya di sini adalah android.R.id.content, mengingat kita biasa meneruskan ListViewdengan android.R.id.listuntuk daftar preferensi itu sendiri (dan masih melakukannya jika menggunakan cara tanpa fragmen, tanpa header,).
davidcsb
2
Saya pikir lebih baik untuk memeriksa kode Android, untuk melihat apa yang dibutuhkannya, daripada mengotak-atik pandangannya hirerchy (hapus / tambahkan tampilan yang dimilikinya). Saya pikir lebih aman dengan cara ini. Saya sarankan untuk memeriksa file "preference_list_content".
Pengembang android
2
Ini adalah jawaban terbaik di utas ini. Penulis artikel ini mengembangkannya menjadi implementasi referensi penuh, yang saya gunakan. Faktanya, ini adalah satu-satunya solusi yang berfungsi untuk mendukung preferensi canggih di aplikasi Anda.
Eugene Wechsler
17

Dengan rilis Android Support Library 22.1.0 dan AppCompatDelegate yang baru, di sini Anda dapat menemukan contoh yang bagus dari implementasi PreferenceActivity dengan dukungan material dengan kompatibilitas mundur.

Perbarui Ini berfungsi pada layar bersarang juga.

https://android.googlesource.com/platform/development/+/marshmallow-mr3-release/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java

MrBrightside
sumber
1
Oh, itu berita bagus! Jadi, tampaknya solusi yang didasarkan pada "perluasan PreferenceActivity" lebih baik daripada solusi "memperpanjang ActionBarActivity" dalam perspektif baru ini.
Eugene Wechsler
1
@EugeneWechsler Ya, memang, ActionBarActivity sudah tidak digunakan lagi sekarang.
MrBrightside
Kerjakan solusi ini juga di layar bersarang? Apakah ada contoh yang lebih baik?
Tomas
@Tomas Saya belum mencobanya, tetapi seharusnya berfungsi pada layar bersarang juga. Jika berhasil untuk Anda, beri tahu kami.
MrBrightside
Terima kasih banyak ! Bekerja untuk saya di Galaxy Nexus (4.3) dan di emulator dengan layar bersarang (lollipop).
Tim Autin
6

Meskipun jawaban di atas tampak rumit, jika Anda menginginkan solusi perbaikan cepat untuk menggunakan Toolbar dengan dukungan API 7 dan lebih tinggi sambil terus mengembangkan PreferenceActivity, saya mendapat bantuan dari proyek di bawah ini.

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;
}
}
midhunhk.dll
sumber
6

Saya juga telah mencari solusi untuk menambahkan toolbar dukungan v7 ( API 25 ) ke AppCompatPreferenceActivity (yang secara otomatis dibuat oleh AndroidStudio saat menambahkan SettingsActivity). Setelah membaca beberapa solusi dan mencoba masing-masing, saya berjuang untuk mendapatkan contoh PreferenceFragment yang dihasilkan untuk ditampilkan dengan toolbar juga.

Solusi modifikasi yang berhasil dilakukan berasal dari " Gabor ".

Salah satu peringatan yang saya hadapi adalah 'onBuildHeaders' hanya aktif sekali. Jika Anda memutar perangkat (seperti ponsel) ke samping, tampilan dibuat ulang dan PreferenceActivity dibiarkan tanpa toolbar lagi, namun PreferenceFragments akan mempertahankannya.

Saya mencoba menggunakan 'onPostCreate' untuk memanggil 'setContentView', sementara ini berfungsi untuk membuat ulang toolbar saat orientasi berubah, PreferenceFragments kemudian akan ditampilkan kosong.

Apa yang saya dapatkan dengan memanfaatkan hampir setiap tip dan jawaban yang dapat saya baca tentang subjek ini. Saya berharap orang lain juga merasakan manfaatnya.

Kami akan mulai dengan Java

Pertama di AppCompatPreferenceActivity.java (yang dihasilkan), saya memodifikasi 'setSupportActionBar' seperti ini:

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

Kedua , saya membuat kelas baru bernama AppCompatPreferenceFragment.java (saat ini nama yang tidak digunakan, meskipun mungkin tidak tetap seperti itu!):

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

Ini adalah bagian dari jawaban Gabor yang berhasil.

Terakhir , Untuk mendapatkan konsistensi kita perlu melakukan beberapa perubahan pada SettingsActivity.java :

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

Beberapa kode telah ditinggalkan dari aktivitas agar singkatnya. Komponen utama di sini adalah ' onAttachedFragment ', ' onPostCreate ', dan 'GeneralPreferenceFragment' sekarang memperluas ' AppCompatPreferenceFragment ' kustom alih-alih PreferenceFragment.

Ringkasan Kode : Jika ada fragmen, fragmen akan memasukkan tata letak baru dan memanggil fungsi 'setSupportActionBar' yang dimodifikasi. Jika fragmen tidak ada, SettingsActivity memasukkan tata letak baru ke 'onPostCreate'

Sekarang ke XML (sangat sederhana):

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>

Hasil Akhir :

SettingsActivity

GeneralPreferenceFragment

SilverX
sumber
Terlihat menjanjikan tetapi tidak berhasil untuk saya. imgur.com/lSSVCIo (emulator Pixel C).
Thomas Vos
Github Link for the lazy
Martin Sing
5

Saya memiliki solusi baru (mungkin lebih rapi), yang menggunakan AppCompatPreferenceActivitysampel dari Dukungan v7. Dengan kode ini di tangan saya membuat tata letak saya sendiri yang menyertakan toolbar:

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

Kemudian, di my AppCompatPreferenceActivity, saya mengubah setContentViewuntuk membuat tata letak baru saya, dan menempatkan tata letak yang disediakan di dalam my 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);
}

Lalu saya hanya memperpanjang AppCompatPreferenceActivity, mengizinkan saya untuk memanggil setSupportActionBar((Toolbar) findViewById(R.id.toolbar)), dan memekarkan item menu di toolbar juga. Semua sambil menjaga manfaat a PreferenceActivity.

Bryan
sumber
5

Mari tetap sederhana & bersihkan di sini, tanpa merusak tata letak bawaan apa pun

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();
        }
    });
}
Samuel
sumber
Tidak berhasil untukku, root.getChildAt(0);kembali null.
Eido95
4

Saya menemukan solusi sederhana ini saat mengerjakan ini. Pertama kita perlu membuat layout untuk aktivitas pengaturan.

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>

Pastikan Anda menambahkan tampilan daftar dengan android:id="@android:id/list", jika tidak maka akan terlemparNullPointerException

Langkah selanjutnya adalah menambahkan onCreatemetode (Ganti) di aktivitas pengaturan Anda

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

Pastikan Anda mengimpor android.suppoer.v7.widget.Toolbar. Ini harus bekerja cukup banyak pada semua API di atas 16 (Jelly Bean dan lebih tinggi)

Apurva
sumber
1

Saya ingin melanjutkan solusi James Cross yang ditandai, karena setelah itu ada masalah menutup hanya layar bersarang yang aktif (PreferenceFragment) dengan cara tidak menutup SettingsActivity juga.

Sebenarnya itu berfungsi pada semua layar bersarang (jadi saya tidak mengerti solusi dari Gábor yang saya coba tanpa hasil, baik itu bekerja sampai titik tertentu tetapi itu berantakan dari beberapa bilah alat), karena ketika pengguna mengklik layar sub preferensi , hanya fragmen yang diubah (lihat <FrameLayout android:id="@+id/content_frame" .../>) bukan Toolbar yang selalu aktif dan terlihat, tetapi perilaku khusus harus diterapkan untuk menutup setiap fragmen sebagaimana mestinya.

Di kelas utama SettingsActivityyang memperluas ActionBarActivitymetode berikut harus dilaksanakan. Perhatikan bahwa pribadi setupActionBar()dipanggil darionCreate()

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

Untuk judul layar bersarang yang dipilih, Anda harus mendapatkan referensi dari Toolbar Anda dan menyetel judul yang sesuai dengan toolbar.setTitle(R.string.pref_title_general);(misalnya).

Tidak perlu mengimplementasikan getSupportActionBar()di semua PreferenceFragment karena hanya tampilan fragmen yang diubah di setiap commit, bukan Toolbar;

Tidak perlu membuat kelas ToolbarPreference palsu untuk ditambahkan di setiap preferensi.xml (lihat jawaban Gábor).

Davideas
sumber
1

Berikut adalah pustaka yang saya buat berdasarkan kode AOSP, yang menambahkan pewarnaan ke preferensi dan dialog, menambahkan bilah tindakan, dan mendukung semua versi dari API 7:

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary

pengembang android
sumber
Dari melihat kode, itu tidak berfungsi untuk preferensi bersarang ...?
Tim Rae
@TimRae Saya tidak yakin saya telah menguji apa yang Anda bicarakan. Tolong jelaskan apa yang Anda maksud. Apa skenario yang Anda coba gunakan?
Pengembang android
Ketika Anda memiliki bagian PreferenceScreendalam PreferenceScreenseperti ini
Tim Rae
Saya tidak pernah menggunakan hal seperti itu. membaca dokumen:: developer.android.com/reference/android/preference/… , saya melihatnya dapat membantu berpindah antar layar. Anda bilang saya harus menambahkannya juga? Saya akan memeriksanya. Terima kasih. Juga, silakan gunakan Github lain kali untuk hal seperti itu (permintaan dan masalah).
Pengembang android
Ya itu berguna ketika Anda memiliki terlalu banyak preferensi untuk satu layar ... Ada beberapa tempat di utas ini di mana orang menyebutkan layar bersarang, jadi saya pikir di sini adalah tempat yang tepat untuk berkomentar
Tim Rae
1

Nah, ini masih menjadi masalah bagi saya hingga hari ini (18 Nov 2015). Saya telah mencoba semua solusi dari utas ini tetapi ada dua hal utama yang tidak dapat saya selesaikan:

  • Layar preferensi bersarang muncul tanpa toolbar
  • Preferensi tidak memiliki tampilan Material pada perangkat pra-Lollipop

Jadi saya akhirnya membuat perpustakaan dengan solusi yang lebih rumit. Pada dasarnya, saya harus menerapkan gaya secara internal ke preferensi jika kami menggunakan perangkat pra-Lollipop dan saya juga menangani layar bersarang menggunakan fragmen khusus (memulihkan semua hierarki bersarang dengan memanfaatkan kunci PreferenceScreen ).

Perpustakaannya adalah yang ini: https://github.com/ferrannp/material-preferences

Dan jika Anda tertarik dengan kode sumber (terlalu panjang untuk mempostingnya di sini), ini pada dasarnya adalah intinya: https://github.com/ferrannp/material-preferences/blob/master/library/src/main/ java / com / fnp / materialpreferences / PreferenceFragment.java

Ferran Negre
sumber