如何将支持库中的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:
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
。 这种方法需要重写一个全新的PreferenceActivity
, PreferenceManager
并可能是PreferenceFragment
,这意味着你需要访问像com.android.internal.util.XmlUtils
这样的隐藏类。解决scheme只能来自Google开发者实现的ActionBarWrapper
,可以添加到任何活动。
如果你真的需要一个偏好活动,我现在的build议是ActionBarSherlock
。
不过,我设法在这里实现它。
问题背景:
OP希望知道如何将MenuItem
放在MenuItem
的ActionBar
,因为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首选项支持库? 讨论如何使用它。