如何将支持库中的Action Bar添加到PreferenceActivity中?

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

有没有什么办法可以将支持库中的Action Bar添加到PreferenceActivity

以前我用过ActionBarSherlock ,它有SherlockPreferenceActivity

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

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

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

请findGitHub回购: 在这里


非常类似于你自己的代码,但添加了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:debugging替代方法

正如许多开发人员指出的, 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 />中,但是我find了一个解决scheme! – 经过大量的试验和错误!

将以下内容添加到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的原因是因为它们基于一个包装对话框,所以我们需要捕获对话框的布局来添加工具栏。


工具栏阴影

通过devise导入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> 

不要忘记添加devise支持库作为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

我调查了报道的重叠问题,我不能重现这个问题。

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

在这里输入图像说明

如果我失去了一些东西,请通过这个回购让我知道,我会调查。

find一个基于support-v4的PreferenceFragment实现片段:

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

编辑:我只是testing它,它的工作很好!

PreferenceActivity与ABC集成是不可能的,至less对我而言是这样。 我尝试了两种可能的方法,但都没有成功:

选项1:

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

选项2:

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

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

不过,我设法在这里实现它。

问题背景:

OP希望知道如何将MenuItem放在MenuItemActionBar ,因为Android的支持库有一个不允许这种情况发生的bug。

我的解决scheme

我已经find了一个比已经提出的更简洁的方式来实现目标(并在Android文档中find它):

android:parentActivityName

活动的逻辑父项的类名称。 这里的名字必须和赋给相应元素的android:name属性的类名匹配。

系统读取此属性以确定在使用按下操作栏中的“上”button时应启动哪个活动。 系统也可以使用这些信息来与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的一部分,因此没有副作用。

快乐的编码。 🙂

更新:

如果您针对棒棒糖,此解决scheme不再有效。 如果你使用AppCompat, 这个答案是你应该寻找的。

我能够通过使用getActionBar()获取android.app.Actionbar 。 它首先返回一个空值…然后我去了清单,并改变主题为:

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

然后我能够再次拥有该操作栏。 我假设这只适用于某些构build级别。 所以你可能想要检查内部版本号或检查返回的值是否为空。

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

现在这个问题的正式答案已经公布了。 这是v7 / v14首选项支持库。

请参阅如何使用v7 / v14首选项支持库? 讨论如何使用它。