(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

답변:


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 :

예


업데이트 (Gingerbread 호환성) :

의견에 따라 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:

예 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가) 그토록 고통스러운 이유는 래퍼 대화 상자를 기반으로하기 때문에 도구 모음을 추가하려면 대화 상자 레이아웃을 캡처해야합니다.


툴바 그림자

디자인 임포트는 Toolbarv21 이전 장치에서 고도 및 그림자를 허용하지 않으므로 고도 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'

안드로이드 6.0

보고 된 중복 문제를 조사했지만 문제를 재현 할 수 없습니다.

위와 같이 사용중인 전체 코드는 다음을 생성합니다.

여기에 이미지 설명 입력

누락 된 항목 이있는 경우이 저장소 를 통해 알려 주시면 조사하겠습니다.


그것은 gingebread에서 nullpointer 예외를 제공하고 루트는 null입니다 .. 어떤 해결책이 있습니까?
andQlimax 2014

귀하의 솔루션은 훌륭하게 작동합니다. 그러나이 접근 방식에는 문제가 있습니다. 사실 (문서에서) <5.0에서 머티리얼 테마를 얻는 데 필수적인 ActionBarActivity를 확장하지 않고 colorAccent (예제를 만들기 위해)는 5.0 미만의 장치의 확인란에 적용되지 않습니다. 이것은 정말 고통스러워 보입니다. 아마도 환경 설정 활동을 제거하고 선형 레이아웃을 사용하여 환경 설정 화면을 시뮬레이션해야 할 수도 있습니다. 그렇지 않으면 API 레벨 8에서 21까지의 장치에서 재료 테마를 사용하는 방법을 볼 수 없습니다. 환경 설정 조각은 "only"> 11 :(
andQlimax

1
@andQlimax 나는 색조 문제에 대한 해결책을 내 대답을 업데이트 한
데이빗 패스 모어에게

3
@DavidPassmore 나를 위해 기본 설정 목록 도구 모음에서 겹치는
Shashank 스리 바스타에게

1
@ShashankSrivastava 이것은 Android 6을 사용하는 경우 반영되며 이에 대한 해결책을 찾고 있습니다. 업데이트 해주셔서 감사합니다.
David Passmore 2015

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 2014 년

2
나는 그가 루트 기본 설정 XML에 포함 된 PreferenceScreen에 대해 이야기하고 있다고 생각합니다.
Lucas S.

5
나는 접근 방식을 좋아하지만 슬프게도하지 않습니다 작업 대상 API는 API (11)보다 작은 경우
midhunhk

12
둘 다 작동하지 않습니다. 실제로는 물질적으로 디자인되고 도구 모음이 있고 중첩 된 기본 설정 화면을 만들 수있는 방법이없는 것 같습니다. 를 사용 ActionBarActivity하여 도구 모음 및 관련 기능을 가져 오는 경우 onBuildHeaders()재정의 할 수 없으며 활동에서 실제 기본 설정 지원이 없습니다. 이전을 사용하는 경우 PreferenceActivity도구 모음과 관련 기능이 없습니다 (예, a Toolbar및 레이아웃을 사용할 수 있지만을 호출 할 수 없습니다 setSupportActionBar(). 따라서 기본 설정 헤더 또는 중첩 된 기본 설정 화면을 사용하면 멈춘 것처럼 보입니다.
Gábor

1
나는 Gabor의 의견에 동의합니다. 이 솔루션은 일반적으로 작동하지 않습니다. 툴바 (ActionBar는 없지만 누가 신경 쓰는지)를 에뮬레이트하는 더 좋은 방법과 AppCompatDelegate가 탑재 된 새로운 지원 라이브러리가 있습니다.
Eugene Wechsler

48

완전히 새로운 업데이트.

몇 가지 실험을 통해 중첩 된 기본 설정 화면에 대해 작동하는 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>

기본 설정 자체는 평소와 같이 정의됩니다 ( 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>

이 시점까지 넷상의 솔루션과 실질적인 차이는 없습니다. 실제로 중첩 된 화면이없고 헤더가없고 단일 화면 만 있어도 사용할 수 있습니다.

헤더 PreferenceFragmentextra매개 변수로 구분되는 모든 더 깊은 페이지에 공통 을 사용합니다 . 각 페이지에는 공통 PreferenceScreen내부 ( xml/settings_page1.xmlet al.) 가있는 별도의 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에 재질 도구 모음을 표시하는 유일한 솔루션입니다. 잘하셨습니다.
String

나는 up-icon에 대해 appcompat 라이브러리의 리소스를 사용합니다.R.drawable.abc_ic_ab_back_mtrl_am_alpha
Ridcully

이 솔루션을 사용하면 툴바가 콘텐츠와 함께 스크롤 될 것이라고 생각합니다. 내부 ListView의 항목 일 뿐이 기 때문입니다.
tasomaniac

이 업데이트 된 새로운 솔루션이 아닙니다. 예상대로 작동합니다.
Gábor 2015 년

이상하게도,이 솔루션은 인식하지 않는 것 PreferenceFragmentCompat대신을 PreferenceFragment. 위로 설정 preference-header으로 xmlns:app="http://schemas.android.com/apk/res-auto" 다음 app:fragment대신하는 android:fragment새로운의 환경 설정 화면을로드하지 않습니다. 그래서 이전 버전과의 호환성에 문제가 있습니까? 제안?
fattire

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
나는 안드로이드의 코드를 확인하고 필요한 것을 확인하는 것이 더 낫다고 생각한다. 뷰 고용을 망쳐 놓는 것보다 (있는 뷰 제거 / 추가). 이 방법이 더 안전하다고 생각합니다. "preference_list_content"파일을 확인하는 것이 좋습니다.
안드로이드 개발자

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

@Tomas 아직 시도하지 않았지만 중첩 된 화면에서도 작동합니다. 당신에게 효과가 있다면, 저희에게 알려주십시오.
MrBrightside

고마워요! Galaxy Nexus (4.3)와 중첩 된 화면 (롤리팝)이있는 에뮬레이터에서 나를 위해 일하고 있습니다.
Tim Autin 2015 년

6

위의 답변은 정교 해 보이지만 API 7 이상을 지원하는 툴바를 사용하는 빠른 수정 솔루션을 원하는 경우 아래이 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

저도 AppCompatPreferenceActivity (SettingsActivity를 추가 할 때 AndroidStudio에서 자동으로 생성됨)에 v7 지원 도구 모음 ( API 25 ) 을 추가하는 솔루션을 찾고있었습니다 . 여러 솔루션을 읽고 각각을 시도한 후 생성 된 PreferenceFragment 예제를 도구 모음과 함께 표시하는 데 어려움을 겪었습니다.

일종의 수정 된 솔루션은 " Gabor " 에서 나왔습니다 .

내가 직면 한 경고 중 하나는 'onBuildHeaders'가 한 번만 발생한다는 것입니다. 장치 (전화와 같은)를 옆으로 돌리면보기가 다시 생성되고 PreferenceActivity가 도구 모음없이 다시 남아 있지만 PreferenceFragments는 해당 항목을 유지합니다.

나는 'onPostCreate'를 사용하여 'setContentView'를 호출하려고 시도했지만 방향이 변경 될 때 툴바를 다시 생성하는 동안 PreferenceFragments는 공백으로 렌더링됩니다.

내가 생각 해낸 것은 거의 모든 팁과이 주제에 대해 읽을 수있는 답변을 활용합니다. 다른 사람들도 유용하다고 생각하기를 바랍니다.

자바 부터 시작하겠습니다.

먼저 (생성 된) 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'는 이제 PreferenceFragment 대신 사용자 정의 ' AppCompatPreferenceFragment '를 확장합니다 .

코드 요약 : 조각이있는 경우 조각은 새 레이아웃을 삽입하고 수정 된 '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>

최종 결과 :

SettingsActivity

GeneralPreferenceFragment


유망 해 보이지만 나를 위해 작동하지 않습니다. imgur.com/lSSVCIo(Pixel C 에뮬레이터).
Thomas Vos

Github의 링크 게으른에 대한
마틴 노래

5

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

그런 다음 내에서 새 레이아웃을 만들고 제공된 레이아웃을 my 안에 배치하도록 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

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 (Jelly Bean 이상)에서 거의 작동합니다.


1

James Cross의 표시된 솔루션을 계속하고 싶습니다. 그 후에는 SettingsActivity도 닫지 않는 방식으로 활성 중첩 화면 (PreferenceFragment) 만 닫는 문제가 있기 때문입니다.

실제로 모든 중첩 된 화면에서 작동합니다. 만 조각이 (참조 변경 <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으로는;

없습니다 필요 (가보의 답변을 참조) 각 preference.xml에 추가하는 가짜의 ToolbarPreference 클래스를 만들 수는.


1

다음은 AOSP 코드를 기반으로 만든 라이브러리입니다.이 라이브러리는 환경 설정과 대화 상자 모두에 색조를 추가하고 작업 표시 줄을 추가하며 API 7의 모든 버전을 지원합니다.

https://github.com/AndroidDeveloperLB/MaterialPreferenceLibrary


코드를 보면 중첩 된 환경 설정에서 작동하지 않습니다 ...?
Tim Rae

@TimRae 나는 당신이 말하는 것을 테스트했는지 잘 모르겠습니다. 무슨 말인지 설명 해주세요. 정확히 사용하려는 시나리오는 무엇입니까?
안드로이드 개발자

당신이있을 때 PreferenceScreen내부의 PreferenceScreen같은
팀 레이

나는 그런 것을 사용한 적이 없습니다. 문서 읽기 : : developer.android.com/reference/android/preference/… , 화면 사이를 이동하는 데 도움이 될 수 있습니다. 나도 추가해야한다고? 제가 확인하겠습니다 . 감사합니다. 또한 다음 번에는 이러한 일 (요청 및 문제)에 대해 Github를 이용하시기 바랍니다.
안드로이드 개발자

예, 단일 화면에 대한 환경 설정이 너무 많을 때 유용합니다 ... 사람들이 중첩 된 화면을 언급하는이 스레드에는 이미 여러 곳이 있으므로 여기에 댓글을 달 적절한 곳이 있다고 생각합니다
Tim Rae

1

글쎄요, 이것은 오늘날 저에게 여전히 문제입니다 (2015 년 11 월 18 일). 이 스레드에서 모든 솔루션을 시도했지만 해결할 수없는 두 가지 주요 사항이 있습니다.

  • 도구 모음없이 중첩 된 기본 설정 화면이 나타남
  • 롤리팝 이전 기기에서 환경 설정에 머티리얼 모양이 없었습니다.

그래서 결국 더 복잡한 솔루션으로 라이브러리를 만들었습니다. 기본적으로 사전 롤리팝 장치를 사용하는 경우 내부적으로 기본 설정에 스타일을 적용해야하고 사용자 지정 프래그먼트를 사용하여 중첩 된 화면도 처리했습니다 (PreferenceScreen 를 활용하여 모든 중첩 된 계층 구조 복원 ).

라이브러리는 https://github.com/ferrannp/material-preferences입니다.

그리고 소스 코드에 관심이 있다면 (여기에 게시하기에는 너무 길다), 이것이 기본적으로 핵심입니다 : https://github.com/ferrannp/material-preferences/blob/master/library/src/main/ java / com / fnp / materialpreferences / PreferenceFragment.java

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.