Sin ActionBar en PreferenceActivity después de la actualización a Support Library v21


83

Después de actualizar a la biblioteca de soporte v21, mi ActionBar en mi PreferenceActivityse ha ido.

¿Perdí algunos atributos en mi tema para activarlo nuevamente? Tuve un problema similar con una ActionBar negra .

También intenté agregarlo un poco al agregar un Toolbaral diseño raíz, pero no funcionó como se esperaba.



1
@JaredBurrows, aunque no puedes usar PreferenceFragment pre 3.0
tyczj

1
Yo también los estoy usando. AFIK Necesito vincularme a una PreferenceActivity que usa PreferenceFragments. Sin embargo, como señala tycyj, los necesito también para soporte heredado.
rekire

1
La barra de herramientas es solo una vista. Puede agregarlo en cualquier lugar.
Jared Burrows

Respuestas:


170

Encuentre el repositorio de GitHub: aquí


Muy similar a su propio código pero agregado xml para permitir el título establecido:

Continuar usando 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 :

ejemplo


ACTUALIZACIÓN (compatibilidad con pan de jengibre):

Como se señaló aquí , los dispositivos Gingerbread están devolviendo NullPointerException en esta línea:

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

REPARAR:

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

Cualquier problema con lo anterior, hágamelo saber.


ACTUALIZACIÓN 2: SOLUCIÓN DE TINTADO

Como se señaló en muchas notas de desarrollo PreferenceActivity, no admite el teñido de elementos, sin embargo, al utilizar algunas clases internas, PUEDE lograrlo. Eso es hasta que se eliminen estas clases. (Funciona con appCompat support-v7 v21.0.3).

Agregue las siguientes importaciones:

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;

Luego anule el onCreateViewmétodo:

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

ejemplo 2


AppCompat 22.1

AppCompat 22.1 introdujo nuevos elementos teñidos, lo que significa que ya no es necesario utilizar las clases internas para lograr el mismo efecto que la última actualización. En su lugar, siga esto (aún anulando 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;
}

PANTALLAS DE PREFERENCIAS ANIDADAS

Mucha gente está experimentando problemas al incluir la barra de herramientas en <PreferenceScreen /> s , sin embargo, ¡he encontrado una solución! - ¡Después de mucho ensayo y error!

Agregue lo siguiente a su 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();
        }
    });
}

La razón por la que PreferenceScreenson tan molestos es porque se basan como un diálogo contenedor, por lo que necesitamos capturar el diseño del diálogo para agregarle la barra de herramientas.


Sombra de la barra de herramientas

Por diseño, la importación Toolbarno permite la elevación y el sombreado en dispositivos anteriores a la v21, por lo que si desea tener elevación en su Toolbar, debe envolverlo en un 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>

Sin olvidar agregar la biblioteca Add the Design Support como una dependencia en el build.gradlearchivo:

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

Investigué el problema de superposición informado y no puedo reproducirlo.

El código completo en uso como arriba produce lo siguiente:

ingrese la descripción de la imagen aquí

Si me falta algo, hágamelo saber a través de este repositorio e investigaré.


Buena: he usado el primer enfoque (Gingerbread no está en mi objetivo). ¡Gracias!
JJ86

Parece que el botón arriba está roto en Gingerbread usando este ejemplo.
idunnololz

1
Gracias por tu gran respuesta, te di hace unos días el estado aceptado. Acabo de copiar la parte de tinte, para asegurarme de que se vea genial :)
rekire

1
Tengo que corregirme. La android:themesolución alternativa solo corrige el color del texto negro en la barra de herramientas para Android 5.0+. Parece que algo cambió con la nueva biblioteca de soporte (estoy usando 22.2.0).
Tony

4
El padre de android.R.id.list es FrameLayout en Android N
zys

45

Utilice AppCompatActivity & PreferenceFragment para resolver el problema:

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 está en desuso.
rekire

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

4
Simplemente extienda AppCompatActivity en lugar de ActionBarActivity.
SammyT

1
Funcionó para mí, y mucho más simple que lo anterior.
Aaron Blenkush

1
Para pantallas de preferencias simples, esta es, con mucho, la solución más fácil de implementar.
Chris Cirefice

7

Terminé agregando la barra de herramientas yo mismo con este código simple:

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

Aquí está mi preferencias_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" />

Supongo que lo puse en onCreate ()
rekire

6

Una mejor solución que "enrollar su propia barra de acción" es usar la clase AppCompatDelegate , que le permite traer una barra de acción real de la biblioteca de soporte. Aquí hay un código de ejemplo para usarlo, tomado de la respuesta de Ľubomír Kučera a esta pregunta .

...
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
también conocido como AppCompatPreferenceActivity de muestras oficiales v7.
Avinash R

3

Hola, no lo soy si todavía tienes este problema. Pero supongo que publicaré lo que hice para resolver esto y espero que ayude a alguien.

1) En primer lugar, es posible que haya notado que PreferenceActivity extiende ListActivity que a su vez se extiende desde Activity.

Según los comentarios en el blog de desarrolladores ( http://android-developers.blogspot.com/2014/10/appcompat-v21-material-design-for-pre.html ), para usar v21 todas sus actividades deben heredar de ActionBarActivity . Entonces ahí está tu problema.

2) Los pasos que utilicé para resolver son:

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.

Esto debería resolverlo.

¡Salud!


4
"Haga que su clase PreferenceActivity amplíe ActionBarActivity", pero ¿CÓMO?
vbence

Mira este enlace aquí: code.hootsuite.com/…
AfrikAndroid

1
Gracias, creo que esto se acerca lo más posible. El verdadero problema es que PreferenceActivity tiene navegación incorporada a través de loadHeadersFromResource , con fragmentos que tienes que construir tú mismo.
vbence

0

Hice algo similar a la pregunta aceptada, pero también estoy usando mi diseño en otras actividades (MainActivity , por lo que no puedo simplemente codificar una flecha en él.

Lo que funcionó para mí:

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

Tuve el mismo problema. simplemente intente cambiar el tema de la actividad por uno que tenga cualquier barra de acción. agregar la siguiente línea en la etiqueta de actividad de SettingsActivity funcionó para mí:

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


-1

Una forma fácil que descubrí es anular:

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

A tu estilo:

<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>
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.