Створення екрана налаштувань за допомогою панелі інструментів підтримки (v21)


116

У мене виникли проблеми з використанням нової панелі інструментів «Дизайн матеріалу» в бібліотеці підтримки на екрані налаштувань.

У мене є файл 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>

Рядки визначені в іншому місці.


stackoverflow.com/a/27455363/2247612 Ця відповідь має ідеальне рішення для бібліотеки підтримки
harishannam

Відповіді:


110

Знайдіть 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 :

приклад


ОНОВЛЕННЯ (сумісність пряників):

Відповідно до коментарів, Пряники пристрої повертають NullPointerException по цій лінії:

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

Виправлення:

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: РОБОЧА РОБОТА

Як зазначається у багатьох нотах розробників 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не дозволяє піднятись і затінитись на пристроях, що передують 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

Я дослідив проблему, що перекривається, і не можу відтворити проблему.

Повний код, що використовується, як описано вище, дає наступне:

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

Якщо я щось пропускаю, будь ласка, повідомте мене через це репо, і я проведу розслідування.


це дає nullpointer виключення в gingebread, корінь є null..any рішення?
andQlimax

ваше рішення чудово працює. але існує проблема з цим підходом, infact без розширення ActionBarActivity, який є обов'язковим (з документації) для отримання теми матеріалу на <5,0, colorAccent (просто для прикладу) не застосовується до прапорців на пристроях <5.0. Це здається справжнім болем. Можливо, я повинен видалити активність уподобань і використовувати лінійний макет для імітації екрана налаштувань, інакше я не бачу жодного способу використання матеріальної теми на пристроях від рівня 8 до 21 api. "тільки"> 11 :(
andQlimax

1
@andQlimax Я оновив свою відповідь на вирішення проблеми з відтінком
Девід Пасмор

3
@DavidPassmore для мене список переваг накладається на панелі інструментів
Shashank Srivastava

1
@ShashankSrivastava Це відображається, якщо ви використовуєте Android 6, я працюю над рішенням для цього. Дякуємо за оновлення.
Девід Пасмор

107

Ви можете використовувати PreferenceFragment, як альтернативу PreferenceActivity. Отже, ось 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();
    }
}

А ось файл макета (pref_with washingbar):

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

Я сподіваюся, що це комусь допоможе.


39
Я спробував такий підхід. Проблема полягає в тому, що вона не відображає панель інструментів на екранах уподобань для дітей.
Мадхур Ахуджа

2
Я думаю, він говорить про PreferenceScreen, вбудований у кореневу перевагу XML.
Лукас С.

5
Мені сподобався підхід, але, на жаль, не вийде, якщо ціль api менша, ніж API 11
midhunhk

12
Він буде працювати ні з одним, ні з одним. Насправді, схоже, немає жодного способу створення матеріально розроблених, інструментальних, вкладених екранів налаштувань. Якщо ви використовуєте ActionBarActivityдля отримання панелі інструментів та пов'язаних з ними функцій, не буде onBuildHeaders()перекрито і не буде фактичної підтримки переваг у діяльності. Якщо ви використовуєте старі PreferenceActivity, у вас немає панелі інструментів і пов'язаних з ними функцій (так, ви можете мати Toolbarі макет, але ви не можете зателефонувати setSupportActionBar(). Отже, або з заголовками уподобань, або з вкладеними екранами налаштувань, ми здаємося застряглими.
Gábor

1
Я згоден з коментарем Габора. Це рішення взагалі не працює. Краще є наступний опис із емуляцією Панелі інструментів (немає ActionBar, але кого це цікавить), а також нова версія підтримки, випущена разом із AppCompatDelegate на борту.
Євген Векслер

48

Зовсім нове оновлення.

З деяким експериментом я, здається, знайшов працююче рішення 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 із загальним 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()в діяльності, оскільки це призведе до помилок. ВикористовуйтеonBuildHeaders() для всіх операцій, таких як додавання панелі інструментів.

Єдина реальна відмінність, і саме це змушує працювати з вкладеними екранами в тому, що ви можете використовувати той самий підхід з фрагментами. Ви можете використовувати їх onCreateView()однаково, надуваючи власний макет замість системного, додаючи панель інструментів так само, як і в діяльності.


2
Яке чудове маленьке вирішення! Це єдине знайдене нами рішення, яке покаже панель інструментів матеріалів на спадковому екрані PreferenceScreen. Молодці, сер.
Рядок

Я використовую ресурс з бібліотеки appcompat для up-icon:R.drawable.abc_ic_ab_back_mtrl_am_alpha
Ridcully

За допомогою цього рішення, я думаю, що Панель інструментів прокручуватиметься із вмістом, правильно? Тому що це лише предмет у внутрішньому ListView.
tasomaniac

Не з цим оновленим новим рішенням. Це працює так, як очікувалося.
Габор

Як не дивно, це рішення, схоже, не розпізнає PreferenceFragmentCompatзамість цього PreferenceFragment. Налаштування preference-headerз, xmlns:app="http://schemas.android.com/apk/res-auto" а потім app:fragmentзамість android:fragmentцього не завантажує жоден новий екран налаштування. Отже, виникають проблеми з сумісністю назад ... пропозиції?
жировик

18

Якщо ви хочете використовувати 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

}

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>

Тут ви можете використовувати будь-який макет, який вам більше подобається, просто переконайтеся, що ви налаштували його також у коді 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>

root.addView (панель інструментів); чому? root.addView (панель інструментівContainer);
Пісня Crossle

Уопс, пропустивши змінну під час перейменування, виправив її.
Свен Дабблд

1
Відмінна відповідь. Ключовим моментом тут є android.R.id.content, з огляду на , що ми використовували , щоб пропускати ListViewз android.R.id.listдля самого списку переваг (і до цих пір при використанні фрагмента менш, headerless, шлях) замість цього.
davidcsb

2
Я думаю, що краще перевірити код Android, щоб побачити, що йому потрібно, замість того, щоб возитися з його переглядами hirerchy (видалити / додати перегляди, які він має). Я думаю, що так безпечніше. Я пропоную перевірити файл "preference_list_content".
андроїд розробник

2
Це найкраща відповідь у цій темі. Автор цієї статті розширив її до повної реалізації посилань, яку я використав. Насправді це єдине робоче рішення для підтримки складних налаштувань у вашій програмі.
Євген Векслер

17

З випуском бібліотеки підтримки Android 22.1.0 та нового AppCompatDelegate, тут ви можете знайти хороший зразок реалізації програми PreferenceActivity з матеріальною підтримкою із зворотною сумісністю.

Оновлення Він працює і на вкладених екранах.

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


1
О, це чудова новина! Таким чином, виявляється, що рішення, засновані на "розширити PreferenceActivity", кращі, ніж рішення на "розширити ActionBarActivity" в цій новій перспективі.
Євген Векслер

1
@EugeneWechsler Так, дійсно, ActionBarActivity застаріла.
MrBrightside

Працювати це рішення також на вкладених екранах? Чи є кращий приклад?
Томаш

@Tomas Я ще не пробував, але він також повинен працювати на вкладених екранах. Якщо працює для вас, скажіть, будь ласка.
MrBrightside

Дуже дякую ! Працює для мене на Galaxy Nexus (4.3) та на емуляторі з вкладеними екранами (льодяник).
Тім Атін

6

Хоча наведені вище відповіді здаються складними, якщо ви хочете швидко вирішити питання, щоб використовувати Панель інструментів з підтримкою 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;
}
}

6

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

Змінене рішення, яке працювало, було від " Gabor ".

Одне з застережень, з якими я зіткнувся, - це пожежі лише на "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>

Кінцевий результат :

НалаштуванняАктивність

GeneralPreferenceFragment


Це виглядає перспективно, але не працює для мене. imgur.com/lSSVCIo (емулятор Pixel C).
Томас Вос

Github Посилання для ледачих
Martin Sing

5

У мене є нове (можливо більш акуратне) рішення, яке використовує AppCompatPreferenceActivityзразки Support 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:

@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)), а також надуваю елементи меню на панелі інструментів. Все, зберігаючи переваги a PreferenceActivity.


5

Давайте тут будемо просто і чисто, не порушуючи вбудованої верстки

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

Не працює для мене, root.getChildAt(0);повертається null.
Eido95

4

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

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методу (Override) у вашій діяльності з налаштуваннями

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 і вище)


1

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

Насправді це працює на всіх вкладених екранах (тому я не розумію рішення Габора, що я намагався без успіху, добре працює до певного моменту, але це безлад декількох панелей інструментів), тому що, коли користувач натискає екран додаткового налаштування , змінюється лише фрагмент (див. <FrameLayout android:id="@+id/content_frame" .../>) не Панель інструментів, яка залишається завжди активною і видимою, але слід реалізувати власну поведінку, щоб відповідно закрити кожен фрагмент.

У головному класі, SettingsActivityякий розширює ActionBarActivityтакі методи, слід реалізувати. Зауважте, що приватне 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, оскільки лише перегляд фрагмента змінюється під час кожної комісії, а не Панель інструментів;

Не потрібно створювати підроблений клас ToolPreference для додавання до кожного preference.xml (див. Відповідь Габора).


1

Ось створена нами бібліотека, що базується на коді AOSP, який додає тонування як до налаштувань, так і до діалогів, додає панель дій та підтримує всі версії API 7:

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary


Якщо дивитися на код, він не працює для вкладених налаштувань ...?
Тім Рай

@TimRae Я не впевнений, що перевірив те, про що ти говориш. Поясніть, будь ласка, що ви маєте на увазі. Який сценарій ви точно намагаєтесь використовувати?
андроїд розробник

Коли у вас є PreferenceScreenвсередині PreferenceScreenподібне до цього
Тім Рай

Я ніколи такого не використовував. читаючи документи:: developer.android.com/reference/android/preference/… , я бачу, що це може допомогти в переході між екранами. Ви кажете, що я також повинен додати його? Я перевірю це. Дякую. Крім того, будь ласка, використовуйте Github наступного разу для такої речі (запити та проблеми).
андроїд розробник

Так, це корисно, коли у вас занадто багато уподобань для одного екрана ... У цій темі вже є кілька місць, де люди згадують вкладені екрани, тому я думаю, що тут є відповідне місце для коментарів
Tim Rae

1

Ну, це все ще проблема для мене сьогодні (18 листопада 2015 р.). Я спробував усі рішення з цієї теми, але було дві основні речі, які я не міг вирішити:

  • Вкладені екрани налаштувань з’явилися без панелі інструментів
  • На вподобаннях не було вигляду "Матеріал" на пристроях до Lollipop

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

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

І якщо вас цікавить вихідний код (занадто довго, щоб публікувати його тут), це в основному його суть: https://github.com/ferrannp/material-preferences/blob/master/library/src/main/ java / com / fnp / materialpreferences / PreferenceFragment.java

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.