如何将支持库中的操作栏添加到PreferenceActivity中?


128

Action Bar兼容性已添加到支持库的修订版18中。它现在具有ActionBarActivity用于在旧版本的Android上使用Action Bar创建活动的类。

有什么方法可以将Action Bar从支持库添加到PreferenceActivity吗?

以前,我使用ActionBarSherlock并具有SherlockPreferenceActivity



1
我刚刚编辑了答案,发现了一个基于support-v4的工作偏好片段实现,可与ActionBarActivity一起使用。我自己使用。
Ostkontentitan

如果您愿意,可以使用我的解决方案:github.com/AndroidDeveloperLB/MaterialStuffLibrary
android开发人员

Answers:


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的代码。


请在答案中输入您的基本代码段。以避免潜在的断开链接问题。
Yohanes Khosiawan许先汉

谢谢,这对我(ViewGroup) getWindow().getDecorView().getRootView()
有用

2
@petrsyn它实际上有效。看看这里这里找到了你应该怎么做。
卢博米尔库塞拉

1
@swooby您可能会遇到错误。
卢博米尔库塞拉

1
好的,那么我应该将工具栏放在xml的什么位置?我收到了NullPointerException
Martin Erlic '16

78

AppCompat当前无法实现。我在内部打开了一个错误。


6
谢谢@克里斯。拥有此功能会很好。
Pablo

12
@Chris,您是否知道何时可以期望PreferenceActivity加入ActionBarCompat
inbryk

3
我认为该功能不太可能实现。目前,运行旧版Android(<4.0)的设备数量少于30%,并且这个数字每个月都在下降。
罗马

1
这是大约一年前输入的,没有解决方案??
某处某人2014年

1
wtf还在等待吗?
Broak

24

我设法创建了一种类似于Google Play商店使用的解决方法。链接到原始答案

请在以下位置找到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 :

例


UPDATE(姜饼兼容性):

正如指出的在这里,姜饼设备是在这条线返回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
大!像魅力一样工作:)
Tobi 2015年

为什么这不适用于嵌套的PreferenceScreen,而仅适用于主PreferenceScreen?
Tobi

@Tobi我已经更新了答案以解决嵌套问题,并解释了原因。:)
David Passmore 2015年

谢谢!您的建议“ AppCompat 22.1”对我有用:)非常有用!
Martin Pfeffer 2015年

1
为什么PreferenceActivity 在屁股上这么疼???应该节省时间。我不妨进行常规活动,然后自己亲自以线性布局布置所有设置。Fuuuuck!
某处某人2015年

12

找到了基于support-v4 Fragment的PreferenceFragment实现:

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

编辑:我刚刚测试了它,并且效果很好!


@Konstantin,您可以添加一些代码示例吗?我下载了它,将导入从android.preference.PreferenceFragment更改为android.support.v4.preference.PreferenceFragment,我看到它在屏幕中间添加了一些标题,但顶部没有添加ActionBar
Gavriel

它不会添加动作栏即活动的工作。Unfortunally我手头没有samplecode但它应该工作类似:developer.android.com/reference/android/preference/...
Ostkontentitan

3

整合 PreferenceActivity至少对我而言,无法与ABC。我尝试了两种可能找到的可能性,但没有一种可行:

选项1:

ActionBarPreferenceActivity延伸PreferenceActivity。执行此操作时,您会受到的限制ActionBarActivityDelegate.createDelegate(ActionBarActivity activity)。另外,您需要实现ActionBar.Callbacks无法访问的内容

选项2:

ActionBarPreferenceActivity延伸ActionBarActivity。这种方法需要重写一个全新的PreferenceActivityPreferenceManager并且可能PreferenceFragment意味着您需要访问诸如这样com.android.internal.util.XmlUtils 的隐藏类。解决方案只能来自Google开发人员,他们实施了ActionBarWrapper可以添加到任何活动中的。

如果您真的需要进行偏好活动,那么我的建议是ActionBarSherlock

但是,我设法在这里实现它。


1
Per4.0导致崩溃。我已经分叉并改进了。 gist.github.com/0e9fe2119b901921b160
TeeTracker 2014年

检查我的解决方案。它不使用任何解决方法stackoverflow.com/a/25603448/1276636
Sufian 2014年

它什么也解决不了!希望您了解当前的问题
Maxwell Weru 2014年

3

问题背景:

在OP想知道我们怎样才能把MenuItemS IN的ActionBarPreferenceActivity预蜂窝,因为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文档的一部分,因此没有任何副作用。

快乐的编码。:)

更新:

如果您以棒棒糖为目标,此解决方案将不再起作用。如果您使用的是AppCompat,那么答案就是您所需要的。


如何帮助获得“首选项”活动中的操作栏?
冷冻蜡笔

@ArjunU。简单的解决方案-尝试并解决。
苏菲安2014年

@ArjunU。问题在于,PreferencesActivity无法将任何物品放入ActionBar,特别是后退按钮。我的答案是一个很好的解决方案。
苏菲安2014年

谢谢你的反对。有什么说明可以改善答案或出了什么问题吗?
苏菲安2014年

1
@Sufian感谢您链接到我的答案,很高兴能为所有人提供帮助:)
David Passmore

1

我能够android.app.Actionbar通过使用获得getActionBar()。它首先返回一个空值...然后我进入清单并将主题更改为:

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

然后,我又有了操作栏。我假设这仅适用于某些构建级别。因此,您可能需要检查内部版本号或检查返回的值是否为null。

对我来说很好,因为我正在使用的应用程序是ICS/4.0+。


这是一种更简单的解决方案,不使用任何解决方法stackoverflow.com/a/25603448/1276636
Sufian 2014年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.