Как добавить панель действий из библиотеки поддержки в PreferenceActivity?

128

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

Есть ли способ добавить панель действий из библиотеки поддержки PreferenceActivity?

Раньше я использовал ActionBarSherlock, и у него есть SherlockPreferenceActivity.

Римский
источник
возможно, все еще актуально: ActionBar в PreferenceActivity
Маттиас Робберс
1
Я только что отредактировал свой ответ, нашел рабочую реализацию предпочтительного фрагмента на основе support-v4, которая отлично работает с ActionBarActivity. Сам пользуюсь.
Ostkontentitan
Если хотите, можете использовать мое решение: github.com/AndroidDeveloperLB/MaterialStuffLibrary
разработчик Android

Ответы:

128

РЕДАКТИРОВАТЬ: в 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()
inflate
2
@petrsyn Это действительно работает. Посмотрите здесь и здесь, чтобы узнать, как это сделать.
ubomír Kučera
1
@swooby Вы можете страдать от этой ошибки.
Любомир Кучера
1
Хорошо, так где мне поместить панель инструментов в xml? Я получаю
исключение
78

В настоящее время нет возможности достичь с помощью AppCompat. Я обнаружил внутреннюю ошибку.

Крис Бэйнс
источник
6
Спасибо @Chris. Было бы неплохо иметь эту функцию.
Пабло
12
@ Крис, ты хоть представляешь, когда нас могут PreferenceActivityдобавить ActionBarCompat?
imbryk
3
Я считаю маловероятным, что эта функция будет реализована. Количество устройств под управлением более старых версий Android (<4.0) на данный момент составляет менее 30%, и это число уменьшается каждый месяц.
Роман
1
Это было введено почти год назад и без разрешения ??
Someone Somewhere
1
wtf все еще ждет ??
Broak 01
24

Мне удалось создать обходной путь, аналогичный тому, который используется в Google Play Store. Ссылка на исходный ответ

Пожалуйста, найдите репозиторий GitHub: здесь


Очень похоже на ваш собственный код, но добавлен 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 :

пример


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

Как указано здесь , 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: ВОЗМОЖНОЕ РЕШЕНИЕ ПО ОКРАШИВАНИЮ

Как указано во многих заметках разработчика 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не допускает возвышения и затенения на устройствах до версии 21, поэтому, если вы хотите, чтобы у вас было повышение, 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 такая заноза в жопе использовать ??? это должно экономить время. С таким же успехом я мог бы заняться обычным делом и вручную выложить все настройки в линейный макет. Фуууук!
Someone Somewhere
12

Найдена реализация PreferenceFragment на основе фрагмента support-v4:

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

Изменить: я только что протестировал его, и он отлично работает!

Ostkontentitan
источник
@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,
Проверьте мое решение. Он не использует никакого обходного пути stackoverflow.com/a/25603448/1276636
Суфиан
Ничего не решает! Надеюсь, вы понимаете
суть
3

Предпосылки проблемы:

OP хочет знать, как мы можем поместить MenuItems в ActionBarof PreferenceActivityдля pre-Honeycomb, потому что в библиотеке поддержки Android есть ошибка, которая не позволяет этому случиться.

Мое решение:

Я нашел гораздо более чистый способ, чем уже предлагалось, для достижения цели (и нашел его в Android Docs ):

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, вам следует искать этот ответ.

Суфьян
источник
Как это помогает получить панель действий в действии предпочтений?
Frozen Crayon
@ArjunU. простое решение - попробуй и разберись.
Sufian
@ArjunU. Проблема заключалась в том, что у него PreferencesActivityне было возможности поместить предметы вActionBar , особенно кнопку возврата. Мой ответ - хорошее решение этой проблемы.
Sufian
Спасибо за отрицательный голос. Любое объяснение, как я могу улучшить свой ответ или что было не так?
Sufian
1
@Sufian Спасибо за ссылку на мой ответ, рад, что он всем помогает:)
Дэвид Пассмор
1

Я смог android.app.Actionbarиспользовать getActionBar(). Сначала он вернул нулевое значение ... затем я перешел к манифесту и изменил тему на:

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

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

Для меня это будет нормально, потому что приложение, над которым я работаю, предназначено для ICS/4.0+.

RCB
источник
Вот более простое решение, в котором не используются обходные пути stackoverflow.com/a/25603448/1276636
Суфиан