¿Cómo agregar Action Bar de la biblioteca de soporte a PreferenceActivity?

128

La compatibilidad de Action Bar se ha agregado a la biblioteca de soporte, revisión 18. Ahora tiene ActionBarActivityclase para crear actividades con Action Bar en versiones anteriores de Android.

¿Hay alguna forma de agregar Action Bar desde la biblioteca de soporte PreferenceActivity?

Anteriormente utilicé ActionBarSherlock y lo ha hecho SherlockPreferenceActivity.

romano
fuente
quizás aún relevante: ActionBar in PreferenceActivity
Matthias Robbers
1
Acabo de editar mi respuesta, encontré una implementación de fragmento de preferencia de trabajo basada en support-v4 que funciona muy bien con ActionBarActivity. Lo estoy usando solo.
Ostkontentitan
Si lo desea, puede usar mi solución: github.com/AndroidDeveloperLB/MaterialStuffLibrary
desarrollador de Android

Respuestas:

128

EDITAR: En appcompat-v7 22.1.0, Google agregó la clase abstracta AppCompatDelegate como delegado que puede usar para extender el soporte de AppCompat a cualquier actividad.

Úselo así:

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

No más pirateo. Código tomado de AppCompatPreferenceActivity.java .

Ľubomír Kučera
fuente
por favor ponga sus fragmentos de código esenciales en su respuesta. para evitar un posible problema de enlace roto.
Yohanes Khosiawan 许先汉
gracias, esto funciona para mí: cambiaría el nuevo LinearLayout en la primera llamada de (ViewGroup) getWindow().getDecorView().getRootView()
inflado
2
@petrsyn Realmente funciona. Mire aquí y aquí para saber cómo debe hacerlo.
Ľubomír Kučera
1
@swooby Puede sufrir este error.
Ľubomír Kučera
1
Bien, ¿dónde pongo la barra de herramientas en el xml? Recibo una NullPointerException
Martin Erlic
78

Actualmente no hay forma de lograrlo con AppCompat. He abierto un error internamente.

Chris Banes
fuente
66
Gracias @Chris. Será bueno tener esta característica.
Pablo
12
@ Chris, ¿tienes alguna idea de cuándo podríamos esperar PreferenceActivityque nos agreguen ActionBarCompat?
imbryk
3
Creo que es muy poco probable que esta característica se implemente. El número de dispositivos que ejecutan versiones anteriores de Android (<4.0) es inferior al 30% en este momento, y este número disminuye cada mes.
Romano
1
¿Esto se ingresó hace casi un año y no hay resolución?
Alguien en algún lugar el
1
wtf sigue esperando?
Broak
24

He logrado crear una solución similar a la que usa Google Play Store. Enlace a la respuesta original

Encuentre el repositorio de GitHub: aquí


Muy similar a su propio código, pero agregó xml para permitir establecer el título:

Continuando con el uso 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 de 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.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();
            }
        });
    }
}

Cualquier problema con lo anterior, hágamelo saber!


ACTUALIZACIÓN 2: SOLUCIÓN DE TINTADO

Como se señala en muchas notas de desarrollo PreferenceActivity, no admite el tinte de elementos, sin embargo, al utilizar algunas clases internas, PUEDE lograr esto. 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 cambio, 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.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;
}

PANTALLAS DE PREFERENCIA ANIDADA

<PreferenceScreen />Sin embargo, muchas personas están experimentando problemas al incluir la barra de herramientas en correos anidados , ¡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 cual PreferenceScreenes tan difícil es porque se basan en un diálogo de contenedor, por lo que debemos capturar el diseño del diálogo para agregarle la barra de herramientas.


Barra de herramientas de sombra

Por diseño, la importación Toolbarno permite la elevación y el sombreado en dispositivos anteriores a 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
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

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

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

Sin olvidar agregar el agregar la biblioteca de soporte de diseño 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

He investigado el problema de superposición informado y no puedo reproducirlo.

El código completo en uso como el anterior 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é.

David Passmore
fuente
1
¡Excelente! Funciona como un encanto :)
Tobi
¿Por qué esto no funciona para la PreferenceScreen anidada, sino solo para la PreferenceScreen principal?
Tobi
@Tobi He actualizado mi respuesta para resolver el problema anidado y he explicado por qué. :)
David Passmore
¡Gracias! Su consejo "AppCompat 22.1" fue el truco para mí :) ¡Muy útil!
Martin Pfeffer
1
¿Por qué es PreferenceActivity tan difícil de usar ? Se supone que ahorra tiempo. También podría hacer una actividad regular y diseñar manualmente todas las configuraciones en un diseño lineal. ¡Fuuuuck!
Alguien en algún lugar
12

Se encontró una implementación de PreferenceFragment basada en el fragmento support-v4

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

Editar: ¡Acabo de probarlo y funciona muy bien!

Ostkontentitan
fuente
@ Konstantin, ¿puedes agregar algún ejemplo de código? Lo descargué, cambié la importación de android.preference.PreferenceFragment a android.support.v4.preference.PreferenceFragment, y veo que agregó algunos encabezados en el medio de la pantalla, pero no el ActionBar en la parte superior
Gavriel
No agrega la barra de acción que es el trabajo de la actividad. Desafortunadamente, no tengo un código de muestra a mano, pero debería funcionar de manera similar a: developer.android.com/reference/android/preference/…
Ostkontentitan
3

La integración PreferenceActivitycon ABC no es posible, al menos para mí. Probé las dos posibilidades que pude encontrar pero ninguna funcionó:

Opción 1:

ActionBarPreferenceActivityse extiende PreferenceActivity. Cuando haces esto te restringen ActionBarActivityDelegate.createDelegate(ActionBarActivity activity). También necesitas implementarActionBar.Callbacks que no es accesible

Opcion 2:

ActionBarPreferenceActivityse extiende ActionBarActivity. Este enfoque requiere la reescritura de una versión completamente nueva PreferenceActivity, PreferenceManagery puede ser lo PreferenceFragmentque significa que necesita acceso a clases ocultas como com.android.internal.util.XmlUtils La solución a esto solo puede provenir de los desarrolladores de Google que implementen una ActionBarWrapperque se pueda agregar a cualquier actividad.

Si realmente necesita una actividad preferencial, mi consejo por ahora es ActionBarSherlock.

Sin embargo, logré implementarlo aquí .

Maxwell Weru
fuente
1
Per4.0 trae accidente. Lo bifurqué y mejoré. gist.github.com/0e9fe2119b901921b160
TeeTracker
Comprueba mi solución. No utiliza ninguna solución alternativa stackoverflow.com/a/25603448/1276636
Sufian
¡No resuelve nada! Espero que entiendan el problema en cuestión
Maxwell Weru
3

Antecedentes del problema:

El PO quiere saber cómo podemos poner MenuItems en el ActionBarde PreferenceActivitypre-nido de abeja debido biblioteca de soporte de Android tiene un error que no permite que esto suceda.

Mi solución:

He encontrado una forma mucho más limpia, de lo que ya se propuso, para lograr el objetivo (y lo encontré en los Documentos de Android ):

android:parentActivityName

El nombre de clase del padre lógico de la actividad. El nombre aquí debe coincidir con el nombre de clase dado al atributo android: name del elemento correspondiente.

El sistema lee este atributo para determinar qué actividad debe iniciarse cuando el uso presiona el botón Arriba en la barra de acción. El sistema también puede usar esta información para sintetizar una pila de actividades con TaskStackBuilder.

Para admitir los niveles API 4 - 16, también puede declarar la actividad principal con un elemento que especifica un valor para "android.support.PARENT_ACTIVITY". Por ejemplo:

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

Ahora haz lo que normalmente harías en tu onOptionsItemSelected(). Como es parte de Android Docs, no tiene efectos secundarios.

Feliz codificación :)

Actualizar:

Esta solución ya no funciona si estás apuntando a Lollipop. Si está utilizando AppCompat, esta respuesta es lo que debería estar buscando.

Sufí
fuente
¿Cómo ayuda esto a obtener la barra de acción en la actividad de preferencias?
Crayón congelado el
@ArjunU. solución fácil: pruébalo y descúbrelo.
Sufian
@ArjunU. El problema era que PreferencesActivityno habría forma de colocar elementos ActionBar, especialmente el botón de retroceso. Mi respuesta es una buena solución para eso.
Sufian
Gracias por el voto negativo. ¿Alguna explicación de cómo podría mejorar mi respuesta o qué estaba mal?
Sufian
1
@Sufian Gracias por vincular a mi respuesta, me alegro de que esté ayudando a todos:)
David Passmore
1

Pude llegar android.app.Actionbarusando getActionBar(). Al principio devolvió un valor nulo ... luego fui al manifiesto y cambié el tema a:

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

Luego pude volver a tener la barra de acción. Supongo que esto solo funcionará para ciertos niveles de compilación. Por lo tanto, es posible que desee verificar el número de compilación o verificar si el valor devuelto es nulo.

Estaré bien para mí porque la aplicación en la que estoy trabajando es para ICS/4.0+.

RCB
fuente
Aquí hay una solución más simple, una que no utiliza ninguna solución alternativa stackoverflow.com/a/25603448/1276636
Sufian