创建带有支持(v21)工具栏的首选项屏幕


116

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

我有一个settings.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>

字符串在其他地方定义。


stackoverflow.com/a/27455363/2247612这个答案为支持库提供了一个完美的解决方案
harishannam 2015年

Answers:


110

请在以下位置找到GitHub Repo:


参加聚会有点晚,但这是我正在使用的解决方案,以继续使用它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 />但是,我找到了解决方案!-经过大量的反复试验!

将以下内容添加到您的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

我已经调查了所报告的重叠问题,因此无法重现该问题。

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

在此处输入图片说明

如果我遗漏了一些东西,请通过此仓库让我知道,我将进行调查。


它在gingebread中给出了nullpointer异常,root为null ..任何解决方案?
andQlimax 2014年

您的解决方案效果很好。但是这种方法存在问题,实际上没有扩展ActionBarActivity(强制性(从文档中获取)以使材料主题小于5.0)是必需的,colorAccent(仅作为示例)并不应用于设备中小于5.0的复选框。这似乎很痛苦。.也许我必须删除首选项活动并使用线性布局来模拟首选项屏幕,否则我看不到在api级别8到21的设备中使用材质主题的任何方法。 “仅”> 11 :(
andQlimax

1
@andQlimax我已为色问题解决方案更新了答案
David Passmore

3
@DavidPassmore对我来说,首选项列表在工具栏上重叠
Shashank Srivastava

1
@ShashankSrivastava如果您使用的是Android 6,则会反映出来,我正在为此寻求解决方案。感谢更新。
David Passmore

107

您可以使用PreferenceFragment代替PreferenceActivity。因此,这是包装Activity示例:

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

这是布局文件(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);
    }
}

我希望这可以帮助别人。


39
我已经尝试过这种方法。问题在于它没有在子首选项屏幕中显示工具栏。
Madhur Ahuja

2
我认为他是在谈论嵌入在根首选项XML中的PreferenceScreen。
Lucas S.

5
我喜欢的方式,但可悲的是不会工作,如果目标API小于11 API
midhunhk

12
两者都不起作用。实际上,似乎没有任何方法可以创建经过精心设计的,工具栏化的,嵌套的首选项屏幕。如果使用ActionBarActivity来获取工具栏和相关功能,则活动中将没有onBuildHeaders()覆盖和实际的首选项支持。如果您使用旧的PreferenceActivity,你没有工具栏和相关的功能(是的,你可以有一个Toolbar和布局,但你不能叫setSupportActionBar()因此,无论是与偏好标题或嵌套的优先选择屏幕中,我们似乎被卡住。
的Gabor

1
我同意Gabor的评论。该解决方案通常不起作用。有一个更好的模仿工具栏(没有ActionBar,但谁在乎),还有随AppCompatDelegate发布的新支持库。
尤金·韦克斯勒

48

全新更新。

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

首先,正如许多答案(包括此处的答案)中提到的那样,您需要使用new 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>

首选项本身按常规定义(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>

到目前为止,网络上的解决方案没有真正的区别。实际上,即使您没有嵌套屏幕,没有标题,也只有一个屏幕,也可以使用此功能。

我们PreferenceFragment对所有较深的页面使用通用名称extra,以页眉中的参数来区分。每个页面都会有一个单独的XML,并且PreferenceScreen内部有一个通用的XML (xml/settings_page1.xml等)。该片段使用与活动相同的布局,包括工具栏。

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()相同的方法,以自己的布局代替系统布局,以与活动中相同的方式添加工具栏。


2
多么好的小解决方法!这是我发现的唯一解决方案,它将在后代PreferenceScreen上显示材质工具栏。干得好先生。
字符串

我将来自appcompat库的资源用于up-icon:R.drawable.abc_ic_ab_back_mtrl_am_alpha
Ridcully

通过这种解决方案,我认为工具栏将随内容一起滚动吗?因为它只是内部ListView中的一项。
tasomaniac

不适用于此更新的新解决方案。正如预期的那样。
加博尔2015年

奇怪的是,此解决方案似乎无法识别PreferenceFragmentCompat而不是PreferenceFragment。设置preference-headerwith xmlns:app="http://schemas.android.com/apk/res-auto" app:fragment不是android:fragment不会加载任何新的偏好设置屏幕。那么在向后兼容性方面有问题...建议吗?
发胖

18

如果要使用PreferenceHeaders,可以使用以下方法:

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

}

layout / activity_settings.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>

root.addView(工具栏); 为什么?root.addView(toolbarContainer);
Crossle Song 2014年

糟糕,重命名时错过了一个变量,因此对其进行了修复。
Sven Dubbeld,2014年

1
极好的答案。这里的关键是android.R.id.content,考虑到我们用来传递ListViewandroid.R.id.list的优先级列表本身(现在仍然如此,如果使用碎片少,无头,道)来代替。
davidcsb 2014年

2
我认为最好检查一下Android代码,看看它需要什么,而不是弄乱它的视图混乱(删除/添加它具有的视图)。我认为这样更安全。我建议签出文件“ preference_list_content”。
android开发人员

2
这是该线程中的最佳答案。本文的作者将其扩展为我使用的完整参考实现。实际上,这是在应用程序中支持复杂首选项的唯一可行的解​​决方案。
Eugene Wechsler,2015年

17

随着Android支持库22.1.0的发布和新的AppCompatDelegate的发布,您可以在此处找到PreferenceActivity实现的一个不错的示例,该示例实现了具有向后兼容性的实质性支持。

更新 它也适用于嵌套屏幕。

https://android.googlesource.com/platform/development/+/marshmallow-mr3-release/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatPreferenceActivity.java


1
哦,真是个好消息!因此,在这种新观点看来,基于“ extend PreferenceActivity”的解决方案要比基于“ extend ActionBarActivity”的解决方案更好。
Eugene Wechsler

1
@EugeneWechsler是的,确实,ActionBarActivity现在已被弃用。
MrBrightside 2015年

在嵌套屏幕上也可以使用此解决方案吗?有没有更好的例子?
托马斯

@Tomas我还没有尝试过,但是它也应该在嵌套屏幕上工作。如果适合您,请告诉我们。
MrBrightside

非常感谢 !在Galaxy Nexus(4.3)和带嵌套屏幕的仿真器(棒棒糖)上为我工作。
蒂姆·奥丁

6

尽管上面的答案似乎很详尽,但是如果您想使用支持支持API 7的Toolbar以及在扩展过程中一直使用的快速解决方案PreferenceActivity,我将从下面的此项目中获得帮助。

https://github.com/AndroidDeveloperLB/ActionBarPreferenceActivity

activity_settings.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>

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

6

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

一种经过改进的解决方案是“ Gabor ”提供的。

我面临的警告之一是“ onBuildHeaders”仅触发一次。如果将设备(如电话)侧向旋转,则将重新创建视图,并且PreferenceActivity不再具有工具栏,但是PreferenceFragments将保留它们。

我尝试使用'onPostCreate'来调用'setContentView',尽管当方向改变时,这可以重新创建工具栏,但是PreferenceFragments会变成空白。

我想出的一切几乎都可以利用我的技巧和答案,我可以阅读有关该主题的信息。我希望其他人也觉得它有用。

我们将从Java开始

首先在(生成的)AppCompatPreferenceActivity.java中,我修改了“ setSupportActionBar”,如下所示:

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

其次,我创建了一个名为AppCompatPreferenceFragment.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);
    }
}

这是Gabor答案的一部分。

最后,要获得一致性,我们需要对SettingsActivity.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”现在扩展了自定义“ AppCompatPreferenceFragment ”而不是PreferenceFragment。

代码摘要:如果存在片段,则该片段将注入新的布局并调用修改后的“ setSupportActionBar”函数。如果该片段不存在,SettingsActivity将在“ 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_settings.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_settings.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>

最终结果

设置活动

GeneralPreferenceFragment


看起来很有希望,但对我不起作用。imgur.com/lSSVCIo(像素C仿真器)。
Thomas Vos


5

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

<?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


5

让我们在这里保持简洁整洁,而不会破坏任何内置布局

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

root.getChildAt(0);回来对我没用null
Eido95'9

4

我在进行这项工作时发现了这个简单的解决方案。首先,我们需要为设置活动创建一个布局。

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(果冻豆及以上)上,这应该都可以正常工作


1

我想继续James Cross的标记解决方案,因为在那之后存在一个问题,即以不关闭SettingsActivity的方式仅关闭活动的嵌套屏幕(PreferenceFragment)。

实际上,它确实适用于所有嵌套屏幕(因此,我不理解我尝试不成功的Gábor解决方案,它可以一直工作到一定程度,但杂乱了多个工具栏),因为当用户单击子首选项屏幕时,只有片段被更改(请参阅<FrameLayout android:id="@+id/content_frame" .../>),而不是始终保持活动状态和可见状态的工具栏,而是应实现自定义行为以相应地关闭每个片段。

SettingsActivity扩展的主类中,ActionBarActivity应实现以下方法。注意,private setupActionBar()是从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);(例如)。

没有必要实行getSupportActionBar()在所有PreferenceFragment因为只有片段的观点是在每一个改变提交,而不是工具栏;

没有必要创建一个假的ToolbarPreference类中的每个preference.xml增加(见的Gabor的答案)。


1

这是我制作的基于AOSP代码的库,该库为首选项和对话框添加了色彩,添加了操作栏,并支持API 7中的所有版本:

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary


通过查看代码,它不适用于嵌套首选项...?
Tim Rae 2015年

@TimRae我不确定我已经测试过您在说什么。请解释你的意思。您到底想使用哪种方案?
Android开发人员

当你有一个PreferenceScreenPreferenceScreen这样
蒂姆·雷伊

我从来没有用过这样的东西。阅读docs::developer.android.com/reference/android/preference/…,我认为它可以在屏幕之间切换。您说我也应该添加吗?我会看看 。谢谢。另外,请下次使用Github处理此类事情(请求和问题)。
Android开发人员

是的,当您对单个屏幕的偏好过多时,这很有用。此线程中已经有多个地方提到嵌套屏幕,因此我认为这里是一个合适的地方
Tim Rae

1

好吧,今天(2015年11月18日)对我来说仍然是一个问题。我已经尝试了该线程的所有解决方案,但是有两件事我无法解决:

  • 嵌套的首选项屏幕没有工具栏出现
  • 偏好设置在棒棒糖之前的设备上没有Material外观

因此,我最终创建了一个具有更复杂解决方案的库。基本上,如果我们使用的是Lollipop之前的设备,则必须在内部将样式应用于首选项,并且我还使用自定义片段处理了嵌套屏幕(利用PreferenceScreen 还原了所有嵌套层次结构)。

该库就是这个库:https : //github.com/ferrannp/material-preferences

而且,如果您对源代码感兴趣(时间太长,无法在此处发布),则基本上是其核心:https : //github.com/ferrannp/material-preferences/blob/master/library/src/main/ java / com / fnp / materialpreferences / PreferenceFragment.java

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.