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

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

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

Раньше я использовал ActionBarSherlock и имел SherlockPreferenceActivity.

Ответ 1

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.

Ответ 2

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

Ответ 3

Мне удалось создать временное решение, подобное тому, что использует 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 :

example


UPDATE (совместимость с пряничными):

Как указано здесь, 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-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:

example 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

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

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

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

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

Ответ 5

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

Вариант 1:

ActionBarPreferenceActivity extends PreferenceActivity. Когда вы это сделаете, вы получите ограничение на ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). Также вам нужно реализовать ActionBar.Callbacks, который недоступен

Вариант 2:

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

Если вам действительно нужна активность предпочтений, теперь мой совет ActionBarSherlock.

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

Ответ 6

Проблема:

OP хочет знать, как мы можем поставить MenuItem в ActionBar of 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, этот ответ - это то, что вы должны искать.

Ответ 7

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

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

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

Это будет хорошо для меня, потому что приложение, над которым я работаю, для ICS/4.0 +.