지원 라이브러리에서 PreferenceActivity로 작업 표시 줄을 추가하는 방법은 무엇입니까?


128

작업 표시 줄 호환성이 지원 라이브러리, 개정판 18에 추가되었습니다. 이제 ActionBarActivity이전 버전의 Android에서 작업 표시 줄을 사용하여 활동을 작성하는 클래스가 있습니다.

지원 라이브러리에서 작업 표시 줄을 추가하는 방법이 PreferenceActivity있습니까?

이전에는 ActionBarSherlock을 사용 했으며 SherlockPreferenceActivity.



1
방금 내 대답을 편집하고 ActionBarActivity와 잘 작동하는 support-v4를 기반으로 작업 환경 설정 조각 구현을 찾았습니다. 혼자서 사용하고 있습니다.
Ostkontentitan

원하는 경우 내 솔루션을 사용할 수 있습니다 : github.com/AndroidDeveloperLB/MaterialStuffLibrary
Android 개발자

답변:


128

편집 : appcompat-v7 22.1.0에서 Google은 AppCompatDelegate 추상 클래스를 대리자로 추가하여 AppCompat의 지원을 모든 활동으로 확장 할 수 있습니다.

다음과 같이 사용하십시오.

...
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
...

public class SettingsActivity extends PreferenceActivity {

    private AppCompatDelegate mDelegate;

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

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

    public ActionBar getSupportActionBar() {
        return getDelegate().getSupportActionBar();
    }

    public void setSupportActionBar(@Nullable Toolbar toolbar) {
        getDelegate().setSupportActionBar(toolbar);
    }

    @Override
    public MenuInflater getMenuInflater() {
        return getDelegate().getMenuInflater();
    }

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

    @Override
    public void setContentView(View view) {
        getDelegate().setContentView(view);
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().setContentView(view, params);
    }

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        getDelegate().addContentView(view, params);
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        getDelegate().onPostResume();
    }

    @Override
    protected void onTitleChanged(CharSequence title, int color) {
        super.onTitleChanged(title, color);
        getDelegate().setTitle(title);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getDelegate().onConfigurationChanged(newConfig);
    }

    @Override
    protected void onStop() {
        super.onStop();
        getDelegate().onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getDelegate().onDestroy();
    }

    public void invalidateOptionsMenu() {
        getDelegate().invalidateOptionsMenu();
    }

    private AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, null);
        }
        return mDelegate;
    }
}

더 이상 해킹하지 않습니다. AppCompatPreferenceActivity.java 에서 가져온 코드 입니다.


필수 코드 스 니펫을 답변에 넣으십시오. 잠재적으로 끊어진 링크 문제를 피하기 위해.
Yohanes Khosiawan 许先汉

- 감사, 나를 위해이 작품은 내가에 처음 부풀려 호출에서 새의 LinearLayout을 변경할 것입니다 :(ViewGroup) getWindow().getDecorView().getRootView()
ahmedre

2
@petrsyn 실제로 작동합니다. 어떻게해야하는지 알아 보려면 여기여기 를 보십시오 .
Ľubomír Kučera에서

1
@swooby 버그로 고통받을 수도 있습니다 .
Ľubomír Kučera

1
좋아, 툴바를 XML에 어디에 넣을 까? 나는 NullPointerException이 받고 있어요
마틴 Erlic

78

현재 AppCompat로는 달성 할 방법이 없습니다. 내부적으로 버그를 열었습니다.


6
감사합니다 @Chris. 이 기능이 있으면 좋을 것입니다.
Pablo

12
@Chris, 우리가 PreferenceActivity추가 될 것으로 예상 할 때 어떤 아이디어 가 ActionBarCompat있습니까?
imbryk

3
이 기능이 구현 될 가능성은 매우 낮습니다. 이 시점에서 이전 버전의 Android (<4.0)를 실행하는 기기 수는 30 % 미만이며이 수치는 매달 감소합니다.
로마

1
이것은 거의 1 년 전에 입력되었으며 해결책은 없습니다 ??
누군가 어딘가에

1
wtf 아직도 기다리고 ??
Broak

24

Google Play 스토어에서 사용하는 것과 비슷한 해결 방법을 만들었습니다. 원래 답변으로 연결

GitHub Repo를 찾으십시오 : 여기


자신의 코드와 매우 유사하지만 제목을 설정할 수 있도록 xml을 추가했습니다.

계속 사용 PreferenceActivity:

settings_toolbar.xml :

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?attr/actionBarSize"
    app:navigationContentDescription="@string/abc_action_bar_up_description"
    android:background="?attr/colorPrimary"
    app:navigationIcon="?attr/homeAsUpIndicator"
    app:title="@string/action_settings"
    />

SettingsActivity.java :

public class SettingsActivity extends PreferenceActivity {

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

        LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
        Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
        root.addView(bar, 0); // insert at top
        bar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

}

Result :

예


업데이트 (진저 브레드 호환성) :

here 지적했듯이 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

보고 된 겹치는 문제를 조사한 결과 문제를 재현 할 수 없습니다.

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

여기에 이미지 설명을 입력하십시오

누락 된 내용이 있으면 이 리포지토리 를 통해 알려 주시면 조사하겠습니다.


1
큰! 매력처럼 작동합니다 :)
Tobi

왜 중첩 된 PreferenceScreen에 대해서만 작동하지 않고 기본 PreferenceScreen에 대해서만 작동합니까?
Tobi

@Tobi 중첩 된 문제를 해결하기 위해 답변을 업데이트하고 이유를 설명했습니다. :)
David Passmore 2016 년

감사! 당신의 조언 "AppCompat 22.1"은 나를 위해 속임수를했습니다 :) 매우 유용합니다!
Martin Pfeffer

1
PreferenceActivity 엉덩이에 그런 고통이 사용 됩니까 ??? 시간을 절약해야합니다. 정기적 인 활동을하고 수동으로 모든 설정을 선형 레이아웃으로 배치 할 수도 있습니다. uuuu!
누군가 어딘가에

12

support-v4 Fragment를 기반으로 PreferenceFragment 구현을 찾았습니다.

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

편집 : 방금 테스트했으며 훌륭하게 작동했습니다!


@ Konstantin, 코드 예제를 추가 할 수 있습니까? 나는 그것을 다운로드하고 android.preference.PreferenceFragment에서 android.support.v4.preference.PreferenceFragment로 가져 오기를 변경했는데 화면 중간에 헤더가 추가되었지만 상단의 ActionBar가 아닌 것을 보았습니다.
Gavriel

활동의 작업을 수행하는 조치 표시 줄을 추가하지 않습니다. 불행히도 나는 손에
샘플 코드가 없지만

3

PreferenceActivity적어도 나에게는 ABC와의 통합 이 불가능합니다. 나는 찾을 수있는 두 가지 가능성을 시도했지만 아무도 효과가 없었습니다.

옵션 1:

ActionBarPreferenceActivity확장 PreferenceActivity합니다. 이렇게하면에 의해 제한을받습니다 ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). 또한 ActionBar.Callbacks액세스 할 수없는 것을 구현해야합니다

옵션 2 :

ActionBarPreferenceActivity확장 ActionBarActivity합니다. 이 방법은 완전히 새로운 재 작성 필요 PreferenceActivity, PreferenceManager그리고 수 있습니다 PreferenceFragment당신은 같은 숨겨진 클래스에 액세스해야하는 수단 com.android.internal.util.XmlUtils 구현 DEVS 단지 구글에서 올 수있는이에 대한 해결책을 ActionBarWrapper모든 활동에 추가 할 수 있습니다.

선호 활동이 정말로 필요한 경우 지금 내 조언은 ActionBarSherlock입니다.

그러나 여기서 구현했습니다 .


1
Per4.0은 충돌을 일으 킵니다. 나는 그것을 포크하고 향상시켰다. gist.github.com/0e9fe2119b901921b160
TeeTracker

내 솔루션을 확인하십시오. 해결 방법을 사용하지 않습니다 stackoverflow.com/a/25603448/1276636
Sufian

아무것도 해결되지 않습니다! 당신이 손에 문제가 이해 희망
맥스웰 Weru

3

문제 배경 :

영업 이익은 우리가 넣을 수있는 방법을 알고 싶어 MenuItem에들 ActionBarPreferenceActivity안드로이드 지원 라이브러리는 이런 일이 허용하지 않는 버그가 있기 때문에 사전에 벌집을 위해.

내 솔루션 :

목표를 달성하기 위해 이미 제안 된 것보다 훨씬 깨끗한 방법을 찾았습니다 ( Android Docs 에서 찾았습니다 ).

android:parentActivityName

활동의 논리 상위 클래스 이름입니다. 여기서 이름은 해당 요소의 android : name 속성에 지정된 클래스 이름과 일치해야합니다.

시스템은이 속성을 읽고 사용하여 조치 표시 줄에서 위로 단추를 누를 때 시작해야하는 활동을 판별합니다. 시스템은이 정보를 사용하여 TaskStackBuilder를 사용하여 활동의 백 스택을 합성 할 수도 있습니다.

API 레벨 4-16을 지원하기 위해 "android.support.PARENT_ACTIVITY"값을 지정하는 요소를 사용하여 상위 활동을 선언 할 수도 있습니다. 예를 들면 다음과 같습니다.

<activity
    android:name="com.example.app.ChildActivity"
    android:label="@string/title_child_activity"
    android:parentActivityName="com.example.myfirstapp.MainActivity" >
    <!-- Parent activity meta-data to support API level 4+ -->
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.example.app.MainActivity" />
</activity>

이제 일반적으로 할 일을하십시오 onOptionsItemSelected(). Android Docs의 일부이므로 부작용이 없습니다.

행복한 코딩. :)

최신 정보:

Lollipop을 타겟팅하는 경우이 솔루션은 더 이상 작동하지 않습니다. AppCompat를 사용하는 경우이 답변을 찾으십시오.


이것은 환경 설정 활동에서 조치 표시 줄을 얻는 데 어떻게 도움이됩니까?
냉동 크레용

@ArjunU. 쉬운 해결책-시도하고 알아 내십시오.
Sufian

@ArjunU. 문제는 PreferencesActivity항목 ActionBar, 특히 뒤로 버튼 을 넣을 방법이 없다는 것 입니다. 내 대답은 그에 대한 좋은 해결책입니다.
수피

downvote 주셔서 감사합니다. 어떻게 대답을 향상시킬 수 있는지 또는 무엇이 잘못 되었습니까?
Sufian

1
@Sufian 내 답변에 연결해 주셔서 감사합니다. 모든 사람을 도와주게되어 기쁩니다:)
David Passmore

1

android.app.Actionbar을 사용하여 얻을 수있었습니다 getActionBar(). 처음에는 null 값을 반환 한 다음 매니페스트로 이동하여 테마를 다음과 같이 변경했습니다.

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

그런 다음 작업 표시 줄을 다시 가질 수있었습니다. 나는 이것이 특정 빌드 레벨에서만 작동한다고 가정합니다. 따라서 빌드 번호를 확인하거나 반환 된 값이 null인지 확인할 수 있습니다.

내가 작업중 인 앱이 ICS/4.0+ 용이므로 괜찮습니다 .


다음은 해결 방법을 사용하지 않는보다 간단한 솔루션입니다. stackoverflow.com/a/25603448/1276636
Sufian

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