Brak ActionBar w PreferenceActivity po aktualizacji do Support Library v21


83

Po uaktualnieniu do Support Library v21 mój ActionBar PreferenceActivityzniknął.

Czy przegapiłem jakieś atrybuty w moim motywie, aby go ponownie aktywować? Miałem podobny problem z czarnym ActionBarem .

Próbowałem też dodać trochę hackingu, dodając Toolbardo układu głównego, ale to nie zadziałało zgodnie z oczekiwaniami.


Powinieneś używać fragmentu preferencji: developer.android.com/reference/android/preference/…
Jared Burrows

1
@JaredBurrows nie możesz jednak używać PreferenceFragment przed 3.0
tyczj

1
Ja też ich używam. AFIK Muszę połączyć się z PreferenceActivity, który używa PreferenceFragments. Jednak, jak podkreśla tycyjny, potrzebuję ich również do obsługi starszych wersji.
rekire

1
Pasek narzędzi to tylko widok. Możesz go dodać w dowolnym miejscu.
Jared Burrows

1
Możesz sprawdzić przykład, który stworzyłem tutaj: github.com/AndroidDeveloperLB/MaterialPreferenceLibrary
programista Androida

Odpowiedzi:


170

Znajdź repozytorium GitHub: tutaj


Bardzo podobny do twojego własnego kodu, ale dodano XML, aby umożliwić ustawienie tytułu:

Kontynuacja użytkowania 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 :

przykład


UPDATE (zgodność z piernikami):

Jak wskazano tutaj , Gingerbread Devices zwracają NullPointerException w tej linii:

LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();

NAPRAWIĆ:

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

Wszelkie problemy z powyższym daj mi znać!


AKTUALIZACJA 2: OBEJŚCIE BARWIENIA

Jak wskazano w wielu notatkach deweloperów, PreferenceActivitynie obsługuje barwienia elementów, jednak używając kilku wewnętrznych klas MOŻESZ to osiągnąć. Dzieje się tak do momentu usunięcia tych klas. (Działa przy użyciu appCompat support-v7 v21.0.3).

Dodaj następujące importy:

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;

Następnie nadpisz onCreateViewmetodę:

@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:

przykład 2


AppCompat 22.1

AppCompat 22.1 wprowadził nowe przyciemniane elementy, co oznacza, że ​​nie ma już potrzeby wykorzystywania klas wewnętrznych, aby osiągnąć ten sam efekt, co podczas ostatniej aktualizacji. Zamiast tego wykonaj następujące czynności (nadal nadrzędne 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.JELLY_BEAN) {
        // 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;
}

EKRANY PREFERENCYJNE ZAGNIEŻDŻONE

Wiele osób ma problemy z włączaniem paska narzędzi do zagnieżdżonych <PreferenceScreen />s, jednak znalazłem rozwiązanie! - Po wielu próbach i błędach!

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

Powodem tego PreferenceScreenjest to, że są one oparte na oknach opakowujących, więc musimy uchwycić układ okna dialogowego, aby dodać do niego pasek narzędzi.


Cień paska narzędzi

Zgodnie z projektem importowanie Toolbarnie pozwala na elewację i cieniowanie w urządzeniach starszych niż 21, więc jeśli chcesz mieć elewację na swoim Toolbar, musisz owinąć ją w AppBarLayout:

`settings_toolbar.xml:

<android.support.design.widget.AppBarLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

   <android.support.v7.widget.Toolbar
       .../>

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

Nie zapominając o dodaniu biblioteki Design Support jako zależności w build.gradlepliku:

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

Zbadałem zgłoszony problem nakładania się i nie mogę go odtworzyć.

Pełny kod używany, jak powyżej, daje następujące wyniki:

wprowadź opis obrazu tutaj

Jeśli czegoś mi brakuje, daj mi znać za pośrednictwem tego repozytorium, a zbadam sprawę.


Fajne: zastosowałem pierwsze podejście (Piernik nie jest moim celem). Dziękuję Ci!
JJ86

Wygląda na to, że przycisk w górę jest uszkodzony na Gingerbread na tym przykładzie.
idunnololz

1
Dziękuję za wspaniałą odpowiedź, kilka dni temu podałem Ci stan akceptacji. Właśnie skopiowałem część
barwiącą

1
Muszę się poprawić. To android:themeobejście naprawia tylko czarny kolor tekstu na pasku narzędzi w systemie Android 5.0+. Wygląda na to, że coś się zmieniło w nowej bibliotece wsparcia (używam 22.2.0).
tony

4
Rodzicem android.R.id.list jest FrameLayout w Androidzie N
zyski

45

Użyj AppCompatActivity & PreferenceFragment, aby rozwiązać problem:

AppCompatActivity:

public class SettingsActivity extends AppCompatActivity {

@Override
protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
}}

PreferenceFragment:

public class SettingsFragment extends PreferenceFragment {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    addPreferencesFromResource(R.xml.settings_preferences);
}}

1
ActionBarActivity jest przestarzałe.
rekire

@rekire its android.support.v7.app.ActionBarActivity
Abdullah

4
Po prostu rozszerz AppCompatActivity zamiast ActionBarActivity.
SammyT

1
Pracował dla mnie i znacznie prostszy niż powyższe.
Aaron Blenkush

1
W przypadku prostych ekranów preferencji jest to zdecydowanie najłatwiejsze do wdrożenia rozwiązanie.
Chris Cirefice

7

Skończyło się na samodzielnym dodaniu paska narzędzi za pomocą tego prostego kodu:

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

Oto moje preferencje_toolbar.xml:

<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:minHeight="?attr/actionBarSize"
    app:navigationContentDescription="@string/abc_action_bar_up_description"
    android:background="?attr/colorPrimary"
    app:navigationIcon="?attr/homeAsUpIndicator"
    app:theme="@style/Theme.Toolbar" />

Chyba umieściłem to naCreate ()
rekire

6

Lepszym rozwiązaniem niż „toczenie własnego” paska akcji jest użycie klasy AppCompatDelegate , która umożliwia wprowadzenie rzeczywistego paska działań z biblioteki pomocy technicznej. Oto przykładowy kod, aby go użyć, zaczerpnięty z odpowiedzi Ľubomíra Kučery na to pytanie .

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

4
aka AppCompatPreferenceActivity z oficjalnych próbek w wersji 7.
Avinash R

3

Cześć, nie jestem, jeśli nadal masz ten problem. Ale myślę, że opublikuję, co zrobiłem, aby rozwiązać ten problem i mam nadzieję, że to komuś pomoże.

1) Po pierwsze, mogłeś zauważyć, że PreferenceActivity rozszerza ListActivity, które z kolei rozszerza się z Activity.

Zgodnie z komentarzami na blogu deweloperów ( http://android-developers.blogspot.com/2014/10/appcompat-v21-material-design-for-pre.html ), aby używać wersji 21, wszystkie Twoje działania muszą dziedziczyć po ActionBarActivity . Więc jest twój problem.

2) Kroki, które rozwiązałem, to:

a) Make sure that you set the Theme of your PreferenceActivity to inherits one ot the Theme.AppCompat Themes.

b) Make your class PreferenceActivity extends ActionBarActivity.

c) Use the PreferenceFragment as your container for all your preferences.

To powinno rozwiązać problem.

Twoje zdrowie!


4
„Uczyń swoją klasę PreferenceActivity rozszerzeniem ActionBarActivity” - ale JAK?
vbence

Sprawdź ten link tutaj: code.hootsuite.com/…
AfrikAndroid

1
Dzięki, myślę, że to jest tak blisko, jak to tylko możliwe. Prawdziwy ból polega na tym, że PreferenceActivity ma wbudowaną nawigację przez loadHeadersFromResource , z fragmentami, które musisz samodzielnie zbudować.
vbence

0

Zrobiłem coś podobnego do zaakceptowanego pytania, ale używam swojego układu również w innych działaniach MainActivity, więc nie mogę po prostu zakodować w nim strzałki.

Co mi pomogło:

private void setupActionBar() {
    LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
    Toolbar toolbar = (Toolbar)LayoutInflater.from(this).inflate(R.layout.app_bar, root, false);
    root.addView(toolbar, 0);
    setSupportActionBar(toolbar);
    ActionBar ab = getSupportActionBar();
    ab.setDisplayHomeAsUpEnabled(true);
}

0

Miałem ten sam problem. po prostu spróbuj zmienić Motyw działania na taki, który zawiera dowolny ActionBar. Dodanie następującego wiersza w tagu aktywności SettingsActivity wyszło dla mnie:

android:theme="Theme.AppCompat.DayNight.DarkActionBar"


-1

Prostym sposobem, który odkryłem, jest zastąpienie:

@android:style/Theme.Material.Light.DarkActionBar

W Twoim stylu:

<style name="SettingsTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
    <item name="android:colorPrimary">@color/sunshine_blue</item>
    <item name="android:colorPrimaryDark">@color/sunshine_dark_blue</item>
</style>
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.