Creating a Preference Screen with support (v21) Toolbar

我在使用首选项屏幕上的支持库中的新材质设计工具栏时遇到了麻烦。

我有一个 setings.xml 文件,如下所示:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/AddingItems"
android:key="pref_key_storage_settings">


<ListPreference
android:key="pref_key_new_items"
android:title="@string/LocationOfNewItems"
android:summary="@string/LocationOfNewItemsSummary"
android:entries="@array/new_items_entry"
android:entryValues="@array/new_item_entry_value"
android:defaultValue="1"/>


</PreferenceCategory>
</PreferenceScreen>

字符串在其他地方定义。

71731 次浏览

You can use a PreferenceFragment, as an alternative to PreferenceActivity. So, here is the wrapping Activity example:

public class MyPreferenceActivity extends ActionBarActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pref_with_actionbar);


android.support.v7.widget.Toolbar toolbar = (android.support.v7.widget.Toolbar) findViewById(uk.japplications.jcommon.R.id.toolbar);
setSupportActionBar(toolbar);


getFragmentManager().beginTransaction().replace(R.id.content_frame, new MyPreferenceFragment()).commit();
}
}

And here is the layout file (pref_with_actionbar):

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">


<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_height="@dimen/action_bar_height"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:theme="@style/ToolbarTheme.Base"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>


<FrameLayout
android:id="@+id/content_frame"
android:layout_below="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />


</RelativeLayout>

最后是 PreferenceFragment:

public static class MyPreferenceFragment extends PreferenceFragment{
@Override
public void onCreate(final Bundle savedInstanceState){
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
}
}

I hope this helps someone.

如果您想使用 PreferenceHeader,可以使用以下方法:

import android.support.v7.widget.Toolbar;


public class MyPreferenceActivity extends PreferenceActivity


Toolbar mToolbar;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
LinearLayout content = (LinearLayout) root.getChildAt(0);
LinearLayout toolbarContainer = (LinearLayout) View.inflate(this, R.layout.activity_settings, null);


root.removeAllViews();
toolbarContainer.addView(content);
root.addView(toolbarContainer);


mToolbar = (Toolbar) toolbarContainer.findViewById(R.id.toolbar);
}


@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.pref_headers, target);
}


// Other methods


}

布局/活动 _ 设置. xml

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">


<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:theme="@style/AppTheme"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>


</LinearLayout>

您可以在这里使用任何您喜欢的布局,只需要确保在 Java 代码中也对其进行了调整。

最后,您的头文件(xml/pref _ headers.xml)

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">


<header
android:fragment="com.example.FirstFragment"
android:title="@string/pref_header_first" />
<header
android:fragment="com.example.SecondFragment"
android:title="@string/pref_header_second" />


</preference-headers>

全新的进展。

通过一些实验,我似乎找到了可用于嵌套首选项屏幕的 AppCompat 22.1 + 解决方案。

首先,正如许多答案中提到的(包括这里的一个) ,您需要使用新的 AppCompatDelegate 使用支持演示中的 AppCompatPreferenceActivity.java文件 (https://android.googlesource.com/platform/development/+/58bf5b99e6132332afb8b44b4c8cedf5756ad464/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java)并简单地从它扩展,或者将相关函数复制到您自己的 PreferenceActivity中。我将在这里展示第一种方法:

public class SettingsActivity extends AppCompatPreferenceActivity {


@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.settings, target);


setContentView(R.layout.settings_page);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);


ActionBar bar = getSupportActionBar();
bar.setHomeButtonEnabled(true);
bar.setDisplayHomeAsUpEnabled(true);
bar.setDisplayShowTitleEnabled(true);
bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
bar.setTitle(...);
}


@Override
protected boolean isValidFragment(String fragmentName) {
return SettingsFragment.class.getName().equals(fragmentName);
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
}
return super.onOptionsItemSelected(item);
}
}

附带的布局相当简单和平常(layout/settings_page.xml) :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="0dp"
android:orientation="vertical"
android:padding="0dp">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:theme="@style/..."/>
<ListView
android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

The preferences themselves are defined as usual (xml/settings.xml):

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="com.example.SettingsFragment"
android:summary="@string/..."
android:title="@string/...">
<extra
android:name="page"
android:value="page1"/>
</header>
<header
android:fragment="com.example.SettingsFragment"
android:summary="@string/..."
android:title="@string/...">
<extra
android:name="page"
android:value="page2"/>
</header>
...
</preference-headers>

No real difference to solutions on the net until this point. Actually, you can use this even if you don't have nested screens, no headers, just a single screen.

We use a common PreferenceFragment for all deeper pages, differentiated by the extra parameters in the headers. Each page will have a separate XML with a common PreferenceScreen inside (xml/settings_page1.xml et al.). The fragment uses the same layout as the activity, including the toolbar.

public class SettingsFragment extends PreferenceFragment {


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTheme(R.style...);


if (getArguments() != null) {
String page = getArguments().getString("page");
if (page != null)
switch (page) {
case "page1":
addPreferencesFromResource(R.xml.settings_page1);
break;
case "page2":
addPreferencesFromResource(R.xml.settings_page2);
break;
...
}
}
}


@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.settings_page, container, false);
if (layout != null) {
AppCompatPreferenceActivity activity = (AppCompatPreferenceActivity) getActivity();
Toolbar toolbar = (Toolbar) layout.findViewById(R.id.toolbar);
activity.setSupportActionBar(toolbar);


ActionBar bar = activity.getSupportActionBar();
bar.setHomeButtonEnabled(true);
bar.setDisplayHomeAsUpEnabled(true);
bar.setDisplayShowTitleEnabled(true);
bar.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
bar.setTitle(getPreferenceScreen().getTitle());
}
return layout;
}


@Override
public void onResume() {
super.onResume();


if (getView() != null) {
View frame = (View) getView().getParent();
if (frame != null)
frame.setPadding(0, 0, 0, 0);
}
}
}

最后,简要介绍一下这种方法的实际工作原理。新的 AppCompatDelegate允许我们使用任何具有 AppCompat 特性的活动,而不仅仅是那些从 AppCompat 中实际扩展的活动。这意味着我们可以将旧的 PreferenceActivity转换成新的,并像往常一样添加工具栏。从那时起,我们就可以坚持使用关于首选项屏幕和标题的旧解决方案,而不会偏离现有的文档。只有一点很重要: 不要在活动中使用 onCreate(),因为它会导致错误。对所有操作(如添加工具栏)使用 onBuildHeaders()

唯一真正的区别是,使它能够在嵌套屏幕上工作的原因是,您可以对片段使用相同的方法。您可以以同样的方式使用它们的 onCreateView(),使用您自己的布局而不是系统的布局,并以与活动中相同的方式添加工具栏。

请找到 GitHub Repo: < a href = “ https://github.com/davcpas1234/Materials Settings”rel = “ noReferrer”> Here


有点晚了,但这是我的解决方案,我正在继续使用 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


更新(姜饼兼容性) :

根据评论,姜饼设备在这一行返回 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:

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

The reason that PreferenceScreen's are such a pain is because they are based as a wrapper dialog, so we need to capture the dialog layout to add the toolbar to it.


工具栏阴影

通过设计导入的 Toolbar不允许在前 v21设备的高度和阴影,所以如果你想在你的 Toolbar的高度你需要把它包装在一个 AppBarLayout:

返回文章页面

<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文件中添加 Design Support 库作为依赖项:

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'

安卓6.0

我已经调查了所报告的重叠问题,但我无法复制这个问题。

正在使用的上述完整代码产生以下内容:

enter image description here

如果我错过了什么,请通过 这个回购让我知道,我会调查。

虽然上面的答案看起来很详细,但是如果您想要一个快速修复解决方案来使用支持 API 7的工具栏,并且一直扩展 PreferenceActivity,我从下面的项目中得到了帮助。

Https://github.com/androiddeveloperlb/actionbarpreferenceactivity

Activity _ sets. xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >


<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/app_theme_light"
app:popupTheme="@style/Theme.AppCompat.Light"
app:theme="@style/Theme.AppCompat" />


<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/padding_medium" >


<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

设置 Activity.java

public class SettingsActivity extends PreferenceActivity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


setContentView(R.layout.activity_settings);


Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);


addPreferencesFromResource(R.xml.preferences);


toolbar.setClickable(true);
toolbar.setNavigationIcon(getResIdFromAttribute(this, R.attr.homeAsUpIndicator));
toolbar.setTitle(R.string.menu_settings);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {


@Override
public void onClick(View v) {
finish();
}
});


}


private static int getResIdFromAttribute(final Activity activity, final int attr) {
if (attr == 0) {
return 0;
}
final TypedValue typedvalueattr = new TypedValue();
activity.getTheme().resolveAttribute(attr, typedvalueattr, true);
return typedvalueattr.resourceId;
}
}

我想继续使用 James Cross 的标记解决方案,因为之后会出现一个问题,即只关闭活动嵌套屏幕(PreferenceFragment)而不关闭 SettingsActivity。

事实上,它可以在所有嵌套的屏幕上工作(所以我不理解 Gábor 的解决方案,我尝试过但是没有成功,好吧,它可以工作到一定程度,但是它是一个混乱的多个工具栏) ,因为当用户点击一个子首选项屏幕时,只有片段被改变(参见 <FrameLayout android:id="@+id/content_frame" .../>) ,而不是工具栏保持总是活跃和可见,but一个自定义的行为应该被实现,以相应地关闭每个片段。

In the main class SettingsActivity that extends ActionBarActivity the following methods should be implemented. Note that private setupActionBar() is called from onCreate()

private void setupActionBar() {
Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
//Toolbar will now take on default Action Bar characteristics
setSupportActionBar(toolbar);
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);


}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}


@Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStackImmediate();
//If the last fragment was removed then reset the title of main
// fragment (if so the previous popBackStack made entries = 0).
if (getFragmentManager().getBackStackEntryCount() == 0) {
getSupportActionBar()
.setTitle(R.string.action_settings_title);
}
} else {
super.onBackPressed();
}
}

对于所选嵌套屏幕的 书名,您应该获得工具栏的引用,并使用 toolbar.setTitle(R.string.pref_title_general);设置适当的标题(例如)。

由于在每次提交时只改变片段的视图,而不改变工具栏,因此在所有 PreferenceFragment 中都有 不用了来实现 getSupportActionBar();

不用了可以创建一个假的 ToolbarPreferences 类来添加到每个 preference.xml 中(参见 Gábor 的答案)。

随着 Android Support Library 22.1.0的发布和新的 AppCompatDepate 的发布,您可以在这里找到 PreferenceActivity 实现的一个很好的例子,它提供了具有向后兼容性的物质支持。

更新 它也可以在嵌套屏幕上工作。

Https://android.googlesource.com/platform/development/+/marshmallow-mr3-release/samples/support7demos/src/com/example/android/supportv7/app/appcompatpreferenceactivity.java

Here's a library I've made that is based on AOSP code, which adds tinting to both the preferences and the dialogs, adds an action bar, and supports all versions from API 7 :

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary

I found this simple solution while working on this. 首先,我们需要为设置活动创建一个布局。

activity_settings.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.my.package">


<android.support.v7.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="@dimen/appbar_elevation"
app:navigationIcon="?attr/homeAsUpIndicator"
app:navigationContentDescription="@string/abc_action_bar_up_description"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />


<ListView
android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tool_bar" />


</RelativeLayout>

确保使用 android:id="@android:id/list"添加列表视图,否则它将抛出 NullPointerException

下一步是在设置活动中添加(覆盖) onCreate方法

Settings.java

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);
toolbar.setTitle(R.string.action_settings);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}

确保你导入了 android.suppoer.v7.widget.Toolbar。这应该可以在16以上的所有 API 上运行(Jelly Bean 和更高版本)

今天(2015年11月18日)我仍然关注这个问题。我已经尝试了这个帖子中的所有解决方案,但有两个主要问题我无法解决:

  • 嵌套首选项屏幕出现时没有工具栏
  • 首选项没有前棒棒糖设备的材质外观

所以我最终创建了一个具有更复杂解决方案的库。基本上,如果我们使用的是前棒棒糖设备,我必须在内部对首选项应用样式,而且我还使用自定义片段处理嵌套屏幕(利用 PreferenceScreen 钥匙恢复所有嵌套层次结构)。

The library is this one: https://github.com/ferrannp/material-preferences

如果您对源代码感兴趣(在这里发布太长了) ,这基本上就是它的核心: https://github.com/ferrannp/material-preferences/blob/master/library/src/main/java/com/fnp/materialpreferences/PreferenceFragment.java

我有一个新的(可能更简洁的)解决方案,它使用了 Support v7示例中的 AppCompatPreferenceActivity。有了这段代码,我创建了自己的布局,其中包括一个工具栏:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" tools:context="edu.adelphi.Adelphi.ui.activity.MainActivity">


<android.support.design.widget.AppBarLayout android:id="@+id/appbar"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">


<android.support.v7.widget.Toolbar android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay"/>


</android.support.design.widget.AppBarLayout>


<FrameLayout android:id="@+id/content"
android:layout_width="match_parent" android:layout_height="match_parent"/>


</android.support.design.widget.CoordinatorLayout>

然后,在我的 AppCompatPreferenceActivity中,我修改了 setContentView来创建一个新的布局,并将提供的布局放在我的 FrameLayout中:

@Override
public void setContentView(@LayoutRes int layoutResID) {
View view = getLayoutInflater().inflate(R.layout.toolbar, null);
FrameLayout content = (FrameLayout) view.findViewById(R.id.content);
getLayoutInflater().inflate(layoutResID, content, true);
setContentView(view);
}

然后我只是扩展 AppCompatPreferenceActivity,允许我调用 setSupportActionBar((Toolbar) findViewById(R.id.toolbar)),并在工具栏中充气菜单项。同时保持 PreferenceActivity的好处。

让我们保持这里的简单和干净,没有打破任何内置的布局

import android.support.design.widget.AppBarLayout;
import android.support.v4.app.NavUtils;
import android.support.v7.widget.Toolbar;


private void setupActionBar() {
Toolbar toolbar = new Toolbar(this);


AppBarLayout appBarLayout = new AppBarLayout(this);
appBarLayout.addView(toolbar);


final ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
final ViewGroup window = (ViewGroup) root.getChildAt(0);
window.addView(appBarLayout, 0);


setSupportActionBar(toolbar);


// Show the Up button in the action bar.
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
}

我也一直在寻找将 V7支持工具栏(API 25)添加到 AppCompatPreferenceActivity (在添加 SettingsActivity 时由 AndroidStudio 自动创建)的解决方案。在阅读了几个解决方案并尝试了每个解决方案之后,我努力使生成的 PreferenceFragment 示例也能在工具栏中显示出来。

某种程度上起作用的改良溶液来自“ 嘉宝”。

我面临的一个警告是“ onBuildHeader”只触发一次。如果你把一个设备(比如手机)横向转动,视图会重新创建,PreferenceActivity 会再次没有工具栏,但是 PreferenceFragments 会保留它们的工具栏。

我尝试使用‘ onPostCreate’调用‘ setContentView’,当方向改变时,这种方法可以重新创建工具栏,然后 PreferenceFragments 将变为空白。

关于这个主题,我所能读到的几乎每一个技巧和答案,我都想到了杠杆作用。我希望其他人也能从中受益。

我们从 爪哇咖啡开始

First in (the generated) Java I modified 'setSupportActionBar' like so:

public void setSupportActionBar(@Nullable Toolbar toolbar) {
getDelegate().setSupportActionBar(toolbar);
ActionBar bar = getDelegate().getSupportActionBar();
bar.setHomeButtonEnabled(true);
bar.setDisplayHomeAsUpEnabled(true);
}

第二个 ,我创建了一个名为 Java的新类(它当前是一个未使用的名称,尽管它可能不会一直保持这个名称!):

abstract class AppCompatPreferenceFragment extends PreferenceFragment {


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_settings, container, false);
if (view != null) {
Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar_settings);
((AppCompatPreferenceActivity) getActivity()).setSupportActionBar(toolbar);
}
return view;
}


@Override
public void onResume() {
super.onResume();
View frame = (View) getView().getParent();
if (frame != null) frame.setPadding(0, 0, 0, 0);
}
}

This is the portion of Gabor's answer that worked.

最后 ,为了保持一致性,我们需要对 设置 Activity.java做一些修改:

public class SettingsActivity extends AppCompatPreferenceActivity {


boolean mAttachedFragment;


@Override
protected void onCreate(Bundle savedInstanceState) {
mAttachedFragment = false;
super.onCreate(savedInstanceState);
}


@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.pref_headers, target);
}


@Override
public void onAttachFragment(Fragment fragment) {
mAttachedFragment = true;
super.onAttachFragment(fragment);
}


@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);


//if we didn't attach a fragment, go ahead and apply the layout
if (!mAttachedFragment) {
setContentView(R.layout.activity_settings);
setSupportActionBar((Toolbar)findViewById(R.id.toolbar_settings));
}
}


/**
* This fragment shows general preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class GeneralPreferenceFragment extends AppCompatPreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


addPreferencesFromResource(R.xml.pref_general);
setHasOptionsMenu(true);


bindPreferenceSummaryToValue(findPreference("example_text"));
bindPreferenceSummaryToValue(findPreference("example_list"));
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
startActivity(new Intent(getActivity(), SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
}

为了简洁起见,一些代码被排除在活动之外。这里的关键组件是‘ onAttachedFragment’、‘ OnPostCreate’,并且‘ GeneralPreferenceFragment’现在扩展了自定义的‘ AppCompatPreferences 片段’,而不是 PreferenceFragment。

Code Summary: If a fragment is present, the fragment injects the new layout and calls the modified 'setSupportActionBar' function. If the fragment is not present, SettingsActivity injects the new layout on 'onPostCreate'

现在来看看 XML(非常简单) :

activity_settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">


<include
layout="@layout/app_bar_settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />


</LinearLayout>

App _ bar _ setings.xml :

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".SettingsActivity">


<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.NoActionBar.AppBarOverlay">


<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_settings"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.NoActionBar.PopupOverlay" />


</android.support.design.widget.AppBarLayout>


<include layout="@layout/content_settings" />


</android.support.design.widget.CoordinatorLayout>

Content _ setings.xml :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".SettingsActivity"
tools:showIn="@layout/app_bar_settings">


<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />


</RelativeLayout>

最终结果 :

SettingsActivity

GeneralPreferenceFragment