Як додати панель дій з бібліотеки підтримки в PreferenceActivity?


128

Сумісність панелі дій додана в бібліотеку підтримки, версія 18. Тепер вона має ActionBarActivityклас для створення заходів із панеллю дій на старих версіях Android.

Чи є спосіб додати панель дій із бібліотеки підтримки PreferenceActivity?

Раніше я використовував ActionBarSherlock і він має SherlockPreferenceActivity.


можливо все ще актуально: ActionBar в PreferenceActivity
Маттіас Робберс

1
Я щойно відредагував свою відповідь, знайшов робочу реалізацію параметрів налаштування на основі support-v4, яка чудово працює з ActionBarActivity. Я використовую його сам.
Ostkontentitan

Якщо ви хочете, ви можете використовувати моє рішення: github.com/AndroidDeveloperLB/MaterialStuffLibrary
андроїд розробник

Відповіді:


128

EDIT: У appcompat-v7 22.1.0 Google додав абстрактний клас AppCompatDelegate як делегат, який можна використовувати для розширення підтримки AppCompat на будь-яку діяльність.

Використовуйте його так:

...
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
...

public class SettingsActivity extends PreferenceActivity {

    private AppCompatDelegate mDelegate;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getDelegate().installViewFactory();
        getDelegate().onCreate(savedInstanceState);
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        getDelegate().onPostCreate(savedInstanceState);
    }

    public ActionBar getSupportActionBar() {
        return getDelegate().getSupportActionBar();
    }

    public void setSupportActionBar(@Nullable Toolbar toolbar) {
        getDelegate().setSupportActionBar(toolbar);
    }

    @Override
    public MenuInflater getMenuInflater() {
        return getDelegate().getMenuInflater();
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

    @Override
    public void setContentView(View view) {
        getDelegate().setContentView(view);
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().setContentView(view, params);
    }

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().addContentView(view, params);
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        getDelegate().onPostResume();
    }

    @Override
    protected void onTitleChanged(CharSequence title, int color) {
        super.onTitleChanged(title, color);
        getDelegate().setTitle(title);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getDelegate().onConfigurationChanged(newConfig);
    }

    @Override
    protected void onStop() {
        super.onStop();
        getDelegate().onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getDelegate().onDestroy();
    }

    public void invalidateOptionsMenu() {
        getDelegate().invalidateOptionsMenu();
    }

    private AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, null);
        }
        return mDelegate;
    }
}

Більше не злому. Код, взятий з AppCompatPreferenceActivity.java .


будь-ласка, введіть у відповідь свої ключові фрагменти коду. щоб уникнути потенційної проблеми з посиланням.
Йоханес Хосяван 许先汉

дякую, це працює для мене - я змінив би новий LinearLayout у першому заклику надути на:(ViewGroup) getWindow().getDecorView().getRootView()
ахмед

2
@petrsyn Це насправді працює. Подивіться тут і тут, щоб дізнатися, як ви повинні це зробити.
Ľubomír Kučera

1
@swooby Ви можете постраждати від цієї помилки.
Ľubomír Kučera

1
Гаразд, де я можу помістити панель інструментів у xml? Я отримую NullPointerException
Мартін Ерлік

78

В даний час немає можливості досягти програми AppCompat. Я відкрив помилку внутрішньо.


6
Дякую @Chris Буде приємно мати цю особливість.
Пабло

12
@Chris, чи маєш ти ідею, коли ми можемо очікувати, що ми PreferenceActivityбудемо додані ActionBarCompat?
імбрик

3
Я думаю, що це дуже малоймовірно, що ця функція буде реалізована. Кількість пристроїв, на яких працює старша версія Android (<4.0), на цей момент становить менше 30%, і це число зменшується щомісяця.
Роман

1
Це було введено майже рік тому, і без резолюції ??
Хтось десь

1
WTF ще чекає ??
Брок

24

Мені вдалося створити обхід, подібний до того, який використовується в Google Play Store. Посилання на оригінальний відповідь

Знайдіть GitHub Repo: Ось


Дуже схожий на ваш власний код, але додав xml, щоб дозволити встановити назву:

Продовження використання 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 />s, однак я знайшов рішення !! - Після безлічі спроб і помилок!

Додайте до свого 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

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

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

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

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


1
Чудово! Працює як чарівність :)
Тобі

Чому це не працює для вкладеного PreferenceScreen, а лише для основного PreferenceScreen?
Тобі

@Tobi Я оновив свою відповідь, щоб вирішити вкладене питання, і пояснив, чому. :)
Девід Пасмор

Дякую! Ваша порада "AppCompat 22.1" зробила для мене фокус :) Дуже корисно!
Мартін Пфеффер

1
чому PreferenceActivity такий біль у попці використовувати ??? це має зекономити час. Я б також міг зробити звичайну діяльність і сам вручну викласти всі параметри в лінійному макеті. Fuuuuck!
Хтось десь

12

Знайдено реалізацію PreferenceFragment на основі фрагмента support-v4:

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

Редагувати: Я щойно перевірив це та чудово працює!


@Konstantin, чи можете ви додати приклад коду? Я завантажив його, змінив імпорт з android.preference.PreferenceFragment на android.support.v4.preference.PreferenceFragment, і я бачу, що він додав заголовки в середині екрана, але не ActionBar вгорі
Gavriel

Він не додає панель дій, яка відповідає діяльності. Нажаль, у мене немає примірного коду під рукою, але він повинен працювати аналогічно: developer.android.com/reference/android/preference/…
Ostkontentitan

3

Інтеграція PreferenceActivityз ABC неможлива, принаймні для мене. Я спробував дві можливості, які я міг знайти, але жодна не спрацювала:

Варіант 1:

ActionBarPreferenceActivityрозширюється PreferenceActivity. Коли ви це зробите, вас обмежують ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). Також потрібно реалізуватиActionBar.Callbacks яка недоступна

Варіант 2:

ActionBarPreferenceActivityрозширюється ActionBarActivity. Такий підхід вимагає перезапису зовсім нового PreferenceActivity, PreferenceManagerі може означати, PreferenceFragmentщо вам потрібен доступ до прихованих класів, як-от com.android.internal.util.XmlUtils Рішення цього може надходити лише від розробників Google, що реалізують додаток, ActionBarWrapperякий можна додати до будь-якої діяльності.

Якщо вам справді потрібна вподобання, моя порада наразі є ActionBarSherlock.

Однак мені вдалося це реалізувати тут .


1
Per4.0 приносить збій. Я роздрібнився і покращився. gist.github.com/0e9fe2119b901921b160
TeeTracker

Перевірте моє рішення. Він не використовує жодного способу
суфійський

Це нічого не вирішує! Сподіваюсь, ви зрозуміли проблему
Максвелл Веру

3

Проблема:

ОП хоче знати , як можна покласти MenuItemз в ActionBarпроPreferenceActivity для попередньої Honeycomb , тому що бібліотека підтримки Android, є помилка , яка не дозволяє цьому статися.

Моє рішення:

Я знайшов набагато більш чистий спосіб, ніж було запропоновано, досягти мети (і знайшов її в Документах Android ):

android:parentActivityName

Ім'я класу логічного батька діяльності. Ім'я тут має відповідати імені класу, заданому атрибуту відповідного елемента android: name.

Система зчитує цей атрибут, щоб визначити, яку діяльність слід починати, коли використання натискає кнопку Вгору на панелі дій. Система також може використовувати цю інформацію для синтезу зворотного стеку дій з TaskStackBuilder.

Для підтримки API рівнів 4-16, ви також можете оголосити батьківську діяльність за допомогою елемента, який визначає значення для "android.support.PARENT_ACTIVITY". Наприклад:

<activity
    android:name="com.example.app.ChildActivity"
    android:label="@string/title_child_activity"
    android:parentActivityName="com.example.myfirstapp.MainActivity" >
    <!-- Parent activity meta-data to support API level 4+ -->
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.example.app.MainActivity" />
</activity>

Тепер робіть те, що ви зазвичай робили у своєму onOptionsItemSelected() . Оскільки він є частиною Android Docs, він не має побічних ефектів.

Щасливе кодування. :)

Оновлення:

Це рішення більше не працює, якщо ви націлені на Lollipop. Якщо ви використовуєте AppCompat, ця відповідь - це те, що вам слід шукати.


Як це допомагає отримати панель дій у налаштуваннях Налаштування?
Заморожений олівець

@ArjunU. просте рішення - спробуйте і розібратися.
Суфіан

@ArjunU. Проблема полягала в тому, що PreferencesActivityне було б ніякого способу розміщення елементів ActionBar, особливо кнопки "назад". Моя відповідь - це гарне виправлення.
Суфіан

Дякуємо за голосування. Будь-яке пояснення, як я міг би покращити свою відповідь чи що не так?
Суфіан

1
@Sufian Дякую за посилання на мою відповідь, радий, що допомагає всім:)
Девід Пасмор

1

Мені вдалося отримати android.app.Actionbar, використовуючи getActionBar(). Спочатку воно повернуло нульове значення ... потім я перейшов до маніфесту та змінив тему на:

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

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

Для мене це буде добре, оскільки додаток, над яким я працюю, призначений для ICS/4.0+.


Ось більш просте рішення, яке не використовує жодних обхідних шляхів stackoverflow.com/a/25603448/1276636
Суфіан

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