Создание экрана предпочтений с поддержкой (v21) Панели инструментов

У меня возникла проблема с использованием новой панели инструментов Material Design в библиотеке поддержки на экране настроек.

У меня есть файл settings.xml, как показано ниже:

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

Строки определяются в другом месте.

Вы можете использовать PreferenceFragment в качестве альтернативы PreferenceActivity . Итак, вот пример операции переноса:

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

И вот файл макета (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> 

И, наконец, PreferenceFragment :

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

Я надеюсь, что это помогает кому-то.

Пожалуйста, найдите GitHub Repo: Здесь


Немного поздно для вечеринки, но это мое решение, которое я использую для продолжения использования 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 :

пример


ОБНОВЛЕНИЕ (совместимость с пряниками):

Согласно комментариям, Gingerbread Devices возвращают NullPointerException в этой строке:

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

Любые проблемы с вышеуказанным дайте мне знать!


ОБНОВЛЕНИЕ 2: ПОРЯДОК РАЗВИТИЯ

Как указано во многих заметках Dev, PreferenceActivity не поддерживает тонирование элементов, однако, используя несколько внутренних классов, вы можете достичь этого. Это до тех пор, пока эти классы не будут удалены. (Работает с использованием appCompat support-v7 v21.0.3).

Добавьте следующие импорты:

 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; 

Затем переопределите метод 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:

Пример 2


AppCompat 22.1

В AppCompat 22.1 были введены новые тонированные элементы, что означает, что больше нет необходимости использовать внутренние классы для достижения того же эффекта, что и последнее обновление. Вместо этого следуйте этому (все еще переопределяя 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; } 

НЕСТАЦИОНАРНЫЕ ЭКРАНЫ

Многие люди испытывают проблемы с включением панели инструментов в вложенном <PreferenceScreen /> однако я нашел решение! – После долгих проб и ошибок!

Добавьте в свой 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(); } }); } 

Причина, по которой PreferenceScreen является такой болью, состоит в том, что они основаны как диалоговое окно обертки, поэтому нам нужно зафиксировать макет диалогового окна, чтобы добавить к нему панель инструментов.


Тень инструментальной панели

По дизайну импорт Toolbar не допускает возвышения и затенения в устройствах pre-v21, поэтому, если вы хотите иметь возвышение на Toolbar вам необходимо обернуть его в 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> 

Не забывая добавить в библиотеку build.gradle библиотеку поддержки поддержки как зависимость:

 compile 'com.android.support:support-v4:22.2.0' compile 'com.android.support:appcompat-v7:22.2.0' compile 'com.android.support:design:22.2.0' 

Android 6.0

Я исследовал сообщаемую проблему перекрытия, и я не могу воспроизвести проблему.

Полный код, используемый выше, дает следующее:

Введите описание изображения здесь

Если я что-то упустил, дайте мне знать через это репо, и я буду исследовать.

Полностью новое обновление.

С некоторыми экспериментами я, кажется, нашел рабочее решение AppCompat 22.1+ для вложенных экранов предпочтений.

Во-первых, как упоминалось во многих ответах (в том числе и здесь), вам нужно будет использовать новый AppCompatDelegate . Либо используйте файл AppCompatPreferenceActivity.java из демонстраций поддержки ( https://android.googlesource.com/platform/development/+/58bf5b99e6132332afb8b44b4c8cedf5756ad464/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java ) И просто простираться от него или копировать соответствующие функции в свою собственную PreferenceActivity . Здесь я покажу первый подход:

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

Сопровождающий макет довольно простой и обычный ( 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> 

Сами предпочтения определяются как обычно ( 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> 

Никакой реальной разницы в решениях в сети до этого момента. Фактически, вы можете использовать это, даже если у вас нет вложенных экранов, нет заголовков, всего один экран.

Мы используем общий PreferenceFragment для всех более глубоких страниц, отличающийся extra параметрами в заголовках. Каждая страница будет иметь отдельный XML с общим внутренним xml/settings_page1.xml PreferenceScreen ( xml/settings_page1.xml и др.). Фрагмент использует тот же макет, что и действие, включая панель инструментов.

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

Наконец, краткое изложение того, как это работает. Новый AppCompatDelegate позволяет нам использовать любые действия с функциями AppCompat, а не только те, которые распространяются на действия, фактически действующие в AppCompat. Это означает, что мы можем превратить старый добрый PreferenceActivity в новый и добавить панель инструментов, как обычно. С этого момента мы можем придерживаться старых решений, касающихся экранов и заголовков предпочтений, без каких-либо отклонений от существующей документации. Существует только один важный момент: не используйте onCreate() в onCreate() потому что это приведет к ошибкам. Используйте onBuildHeaders() для всех операций, таких как добавление панели инструментов.

Единственное реальное различие заключается в том, что делает его работу с вложенными экранами, так это то, что вы можете использовать тот же подход с фрагментами. Вы можете использовать их onCreateView() одинаково, раздувая свой собственный макет вместо системного, добавляя панель инструментов так же, как и в действие.

Если вы хотите использовать PreferenceHeaders, вы можете использовать следующий подход:

 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 } 

макет / 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> 

Вы можете использовать любой макет, который вы предпочитаете здесь, просто убедитесь, что вы его отредактируете и в коде Java.

И, наконец, ваш файл с заголовками (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> 

С выпуском Android Support Library 22.1.0 и нового AppCompatDelegate здесь вы можете найти хороший пример реализации PreferenceActivity с материальной поддержкой с обратной совместимостью.

Обновление Он также работает на вложенных экранах.

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

Хотя приведенные выше ответы кажутся сложными, если вы хотите быстро исправить решение использовать панель инструментов с поддержкой API 7 и все время расширения PreferenceActivity , я получил помощь в этом проекте ниже.

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

Я также искал решение добавить панель инструментов поддержки v7 ( API 25 ) в AppCompatPreferenceActivity (которая автоматически создается AndroidStudio при добавлении параметра SettingsActivity). Прочитав несколько решений и попробовав каждый из них, я изо всех сил пытался отобразить созданные примеры PreferenceFragment с помощью панели инструментов.

Модифицированное решение, отработанное от « Габора ».

Одно из предостережений, с которыми я столкнулся, – это только «onBuildHeaders». Если вы поворачиваете устройство (например, телефон) сбоку, представление воссоздается, а функция PreferenceActivity остается без панели инструментов, однако PreferenceFragments сохранит их.

Я попытался использовать 'onPostCreate' для вызова 'setContentView', в то время как это сработало, чтобы воссоздать панель инструментов при изменении ориентации, PreferenceFragments затем будет отображаться пустым.

То, что я придумал, использует почти каждый совет и ответ, который я мог бы прочитать по этому вопросу. Я надеюсь, что другие считают это полезным.

Мы начнем с Java

Сначала в (сгенерированном) AppCompatPreferenceActivity.java я изменил 'setSupportActionBar' следующим образом:

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

Во-вторых , я создал новый класс с именем AppCompatPreferenceFragment.java (это текущее неиспользуемое имя, хотя оно может и не оставаться таким!):

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

Это часть ответа Габора, который сработал.

Наконец , Чтобы добиться согласованности, нам нужно внести некоторые изменения в 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); } } } 

Некоторый код для краткости был оставлен без внимания. Ключевыми компонентами здесь являются « onAttachedFragment », « onPostCreate » и что «GeneralPreferenceFragment» теперь расширяет пользовательский « AppCompatPreferenceFragment » вместо PreferenceFragment.

Сводка кода . Если фрагмент присутствует, фрагмент вводит новый макет и вызывает измененную функцию setSupportActionBar. Если фрагмент отсутствует, SettingsActivity вводит новый макет в 'onPostCreate'

Теперь о XML (очень просто):

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> 

Конечный результат :

SettingsActivity

GeneralPreferenceFragment

Давайте оставим его простым и чистым, не нарушая встроенный макет

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

Я нашел это простое решение, работая над этим. Сначала нам нужно создать макет для активности настроек.

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> 

Убедитесь, что вы добавили представление списка с помощью android:id="@android:id/list" , иначе он выкинет NullPointerException

Следующий шаг – добавить (переопределить) метод onCreate в ваших настройках.

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

Убедитесь, что вы импортируете android.suppoer.v7.widget.Toolbar . Это должно работать в значительной степени на всех API выше 16 (Jelly Bean и выше)

У меня есть новое (возможно, более аккуратное) решение, которое использует AppCompatPreferenceActivity из образцов поддержки v7. С помощью этого кода я создал свой собственный макет, который включает панель инструментов:

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

Затем, в моем AppCompatPreferenceActivity , я изменил setContentView для создания моего нового макета и FrameLayout предоставленный макет внутри моего 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); } 

Затем я просто расширяю AppCompatPreferenceActivity , позволяя мне вызвать setSupportActionBar((Toolbar) findViewById(R.id.toolbar)) и надуть элементы меню на панели инструментов. Все, сохраняя преимущества PreferenceActivity .

Я хотел бы продолжить заметное решение Джеймса Кросса, поскольку после этого возникает проблема закрытия только активного вложенного экрана (PreferenceFragment), чтобы не закрывать параметр SettingsActivity.

Фактически, он работает на всех вложенных экранах (поэтому я не понимаю решение Gábor, которое я пробовал без успеха, но он работает до определенного момента, но это путаница нескольких панелей инструментов), потому что когда пользователь нажимает экран субстандартных настроек , <FrameLayout android:id="@+id/content_frame" .../> только фрагмент (см. <FrameLayout android:id="@+id/content_frame" .../> ), а не панель инструментов, которая всегда остается активной и видимой, но для закрытия каждого фрагмента необходимо выполнить индивидуальное поведение.

В основном классе SettingsActivity который расширяет ActionBarActivity должны быть реализованы следующие методы. Обратите внимание, что private setupActionBar() вызывается из onCreate()

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

Для названия выбранного вложенного экрана вы должны получить ссылку на свою панель инструментов и установить соответствующий заголовок с помощью toolbar.setTitle(R.string.pref_title_general); (например).

Нет необходимости реализовывать getSupportActionBar() во всем PreferenceFragment, поскольку только изменение фрагмента изменяется при каждой фиксации, а не в Панели инструментов;

Нет необходимости создавать поддельный класс ToolbarPreference для добавления в каждый файл preference.xml (см. Ответ Габора).

Вот библиотека, которую я создал, основанный на коде AOSP, который добавляет оттенок как для предпочтений, так и для диалогов, добавляет панель действий и поддерживает все версии из API 7:

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary

Ну, это все еще проблема для меня сегодня (18 ноября 2015 года). Я пробовал все решения из этой темы, но я не мог решить две основные вещи:

  • Вложенные экраны предпочтений появились без панели инструментов
  • В настройках не было просмотра материала на устройствах с предварительным охлаждением

Поэтому я создал библиотеку с более сложным решением. В принципе, мне приходилось применять стили к предпочтениям, если мы используем устройство pre-Lollipop, а также обработали вложенные экраны с помощью пользовательского фрагмента (восстанавливая всю вложенную иерархию, используя ключ PreferenceScreen).

Эта библиотека: https://github.com/ferrannp/material-preferences

И если вас интересует исходный код (слишком длинный, чтобы опубликовать его здесь), это в основном его суть: https://github.com/ferrannp/material-preferences/blob/master/library/src/main/ Java / COM / ФНП / materialpreferences / PreferenceFragment.java