【问题标题】:How to add Action Bar from support library into PreferenceActivity?如何将支持库中的操作栏添加到 PreferenceActivity?
【发布时间】:2013-07-24 19:48:12
【问题描述】:

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

有没有办法将支持库中的操作栏添加到PreferenceActivity

之前我用的是ActionBarSherlock,它有SherlockPreferenceActivity

【问题讨论】:

标签: android android-actionbar preferenceactivity android-support-library


【解决方案1】:

编辑:在 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

【讨论】:

  • 请将您的基本代码 sn-ps 放入您的答案中。以避免潜在的断开链接问题。
  • 谢谢,这对我有用 - 我会在第一次 inflate 调用中将新的 LinearLayout 更改为:(ViewGroup) getWindow().getDecorView().getRootView()
  • @petrsyn 它确实有效。看看herehere,看看你应该怎么做。
  • @swooby 你可能会遇到this 错误。
  • 好的,那么我应该将工具栏放在 xml 中的哪个位置?我得到一个 NullPointerException
【解决方案2】:

目前没有办法用 AppCompat 来实现。我在内部打开了一个错误。

【讨论】:

  • 谢谢@Chris。有这个功能会很高兴。
  • @Chris,您知道我们什么时候可以将PreferenceActivity 添加到ActionBarCompat 中吗?
  • 我认为这个功能不太可能实现。目前运行旧版 Android (
  • 这是近一年前输入的,没有解决方案??
  • w.t.f 还在等吗??
【解决方案3】:

我设法创建了一个类似于 Google Play 商店使用的解决方法。 Link to Original Answer

请找到 GitHub 存储库:Here


与您自己的代码非常相似,但添加了 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 :


更新(姜饼兼容性):

正如here 指出的那样,Gingerbread Devices 在这一行返回 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:


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

嵌套首选项屏幕

很多人在将工具栏包含在嵌套的&lt;PreferenceScreen /&gt;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

我已调查报告的重叠问题,但无法重现该问题。

上面使用的完整代码产生以下内容:

如果我遗漏了什么,请通过this repo 告诉我,我会进行调查。

【讨论】:

  • 太棒了!像魅力一样工作:)
  • 为什么这不适用于嵌套的 PreferenceScreen,而仅适用于主 PreferenceScreen?
  • @Tobi 我已经更新了解决嵌套问题的答案,并解释了原因。 :)
  • 谢谢!您的建议“AppCompat 22.1”对我有用 :) 非常有用!
  • 为什么PreferenceActivity 使用起来这么痛苦 ???它应该节省时间。我还不如自己做一个常规活动并手动将所有设置布置成线性布局。呸!
【解决方案4】:

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

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

编辑:我刚刚对其进行了测试,效果很好!

【讨论】:

  • @Konstantin,你能添加一些代码示例吗?我下载了它,将导入从 android.preference.PreferenceFragment 更改为 android.support.v4.preference.PreferenceFragment,我看到它在屏幕中间添加了一些标题,但不是顶部的 ActionBar
  • 它不添加活动栏的工作。不幸的是,我手头没有示例代码,但它应该类似于:developer.android.com/reference/android/preference/…
【解决方案5】:

PreferenceActivity 与 ABC 集成是不可能的,至少对我来说是这样。我尝试了我能找到的两种可能性,但都没有奏效:

选项 1:

ActionBarPreferenceActivity 扩展 PreferenceActivity。当你这样做时,你会受到ActionBarActivityDelegate.createDelegate(ActionBarActivity activity) 的限制。您还需要实现无法访问的ActionBar.Callbacks

选项 2:

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

如果你真的需要一个偏好活动,我现在的建议是ActionBarSherlock

但是,我设法实现了它here

【讨论】:

【解决方案6】:

问题背景:

OP 想知道我们如何将 MenuItems 放在 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 Docs 的一部分,因此没有副作用。

愉快的编码。 :)

更新:

如果您以 Lollipop 为目标,此解决方案将不再有效。如果您正在使用 AppCompat,this 答案就是您应该寻找的。​​p>

【讨论】:

  • 这如何帮助在 Preference 活动中获取操作栏?
  • @ArjunU。简单的解决方案 - 尝试并弄清楚。
  • @ArjunU。问题是PreferencesActivity 无法将项目放入ActionBar,尤其是后退按钮。我的回答是一个很好的解决方案。
  • 感谢您的反对。有什么解释我可以如何改进我的答案或出了什么问题?
  • @Sufian 感谢您链接到我的答案,很高兴它对每个人都有帮助 :)
【解决方案7】:

我可以通过使用getActionBar() 获得android.app.Actionbar。一开始它返回了一个空值......然后我去了清单并将主题更改为:

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

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

这对我来说没问题,因为我正在开发的应用程序是为 ICS/4.0+ 开发的。

【讨论】:

【解决方案8】:

现在这个问题的官方答案已经发布了。它是v7/v14 Preference Support 库。

有关如何使用它的讨论,请参阅How to use the v7/v14 Preference Support library?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 2014-11-26
    • 2016-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多