¿Qué usar en lugar de "addPreferencesFromResource" en una PreferenceActivity?

360

Acabo de notar el hecho de que el método addPreferencesFromResource(int preferencesResId)está marcado como obsoleto en la documentación de Android ( Entrada de referencia ).

Desafortunadamente, no se proporciona ningún método alternativo en la descripción del método.

¿Qué método se debe utilizar en su lugar para conectar un PreferScreen.xml a la PreferenceActivity correspondiente?

mweisz
fuente
2
WannaGetHigh proporciona un enfoque muy directo en stackoverflow.com/questions/23523806/…
Sabin
La solución allí todavía usa addPreferencesFromResource(int preferencesResId). Me estoy perdiendo de algo ?
Jammo
@Jammo Sí, pero se ha movido de la Actividad al Fragmento para reflejar la nueva forma de hacerlo:> fragmento.
WannaGetHigh

Respuestas:

332

No se proporciona ningún método alternativo en la descripción del método porque el enfoque preferido (a partir del nivel 11 de API) es instanciar objetos PreferenceFragment para cargar sus preferencias desde un archivo de recursos. Vea el código de muestra aquí: PreferenceActivity

hacker glorificado
fuente
Muchas gracias por su respuesta. Estaba siguiendo instrucciones obsoletas de "Video2Brain-Android Development" que me llevaron a usar la versión del método de PreferenceActivity. Por cierto: me encantaría calificar tu respuesta como útil si pudiera.
mweisz
33
Ahora, solo si se incluye el PreferenceFragment en el paquete de compatibilidad, tendría sentido usarlo stackoverflow.com/questions/5501431/…
christoff el
1
Desde que uso Action Bar Sherlock, seguí el siguiente blog para resolver este problema, consulte ... commonsware.com/blog/2012/10/16/…
Someone Somewhere
2
Aún así, debe llamar a addPreferencesFromResource (int PreferencesID) si desea que la aplicación sea compatible con versiones anteriores con un nivel de API anterior a 11 (Android 3.0). Pero supongo que también podría considerar esos dispositivos antiguos desubicados.
Einar Sundgren
55
@EinarSundgren Soy propietario de Nexus One y mi versión máxima disponible es 2.2: ¡no me detendrás! ¡NUNCA seré desaprobado! Por el poder de Grayskull ... ¡Tengo el poder!
TechNyquist
186

Para agregar más información a la respuesta correcta anterior, después de leer un ejemplo de Android-er descubrí que puede convertir fácilmente su actividad de preferencia en un fragmento de preferencia. Si tienes la siguiente actividad:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.my_preference_screen);
    }
}

Los únicos cambios que tiene que hacer es crear una clase de fragmento interno, moverlo addPreferencesFromResources()al fragmento e invocar el fragmento de la actividad, de esta manera:

public class MyPreferenceActivity extends PreferenceActivity
{
    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
    }

    public static class MyPreferenceFragment extends PreferenceFragment
    {
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.my_preference_screen);
        }
    }
}

Puede haber otras sutilezas para hacer preferencias más complejas a partir de fragmentos; si es así, espero que alguien los note aquí.

Garret Wilson
fuente
66
Bien, gracias por esto. Java y este tipo de pensamiento están muy lejos de mi pequeño y agradable mundo .net :)
Tom
44
¿Por qué se depreciarían addPreferencesFromResources()en el intercambio de PreferenceFragment? Parece innecesario desde el punto de vista del principiante.
Howdy_McGee
44
La llamada requiere API nivel 11
mehmet
1
Entonces, ¿qué pasa si la API está en el nivel 9? @mehmet
gumuruh
2
Respuesta impresionante! ¿hay alguna manera de lograr esto sin cambiar el contenido de android.R.id.? me parece poco elegante por alguna razón ... (
corrígeme
37

@Garret Wilson ¡Muchas gracias! Como novato en la codificación de Android, he estado atrapado con el problema de incompatibilidad de preferencias durante tantas horas, y me parece tan decepcionante que desaprobaron el uso de algunos métodos / enfoques para los nuevos que no son compatibles con las API más antiguas. tener que recurrir a todo tipo de soluciones para que su aplicación funcione en una amplia gama de dispositivos. ¡Es realmente frustrante!

Su clase es excelente, ya que le permite seguir trabajando en nuevas API con preferencias de la forma en que solía ser, pero no es compatible con versiones anteriores. Como estoy tratando de llegar a una amplia gama de dispositivos, trabajé un poco para que funcione en dispositivos anteriores a API 11, así como en API más nuevas:

import android.annotation.TargetApi;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;

public class MyPrefsActivity extends PreferenceActivity
{
    private static int prefs=R.xml.myprefs;

    @Override
    protected void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        try {
            getClass().getMethod("getFragmentManager");
            AddResourceApi11AndGreater();
        } catch (NoSuchMethodException e) { //Api < 11
            AddResourceApiLessThan11();
        }
    }

    @SuppressWarnings("deprecation")
    protected void AddResourceApiLessThan11()
    {
        addPreferencesFromResource(prefs);
    }

    @TargetApi(11)
    protected void AddResourceApi11AndGreater()
    {
        getFragmentManager().beginTransaction().replace(android.R.id.content,
                new PF()).commit();
    }

    @TargetApi(11)
    public static class PF extends PreferenceFragment
    {       
        @Override
        public void onCreate(final Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(MyPrefsActivity.prefs); //outer class
            // private members seem to be visible for inner class, and
            // making it static made things so much easier
        }
    }
}

Probado en dos emuladores (2.2 y 4.2) con éxito.

Por qué mi código se ve tan malo:

Soy un novato en la codificación de Android, y no soy el mejor fanático de Java.

Para evitar la advertencia obsoleta y forzar a Eclipse a permitirme compilar, tuve que recurrir a anotaciones, pero estas parecen afectar solo las clases o los métodos, por lo que tuve que mover el código a dos métodos nuevos para aprovechar esto.

No me gustaría tener que escribir mi ID de recurso xml dos veces cada vez que copio y pego la clase para una nueva PreferenceActivity, por lo que creé una nueva variable para almacenar este valor.

Espero que esto sea útil para alguien más.

PD: Perdón por mis opiniones obstinadas, pero cuando vienes nuevo y encuentras tales desventajas, ¡no puedes evitar frustrarte!

ecv
fuente
Oh bueno ... acabo de notar que tendría que cambiar el nombre de la clase externa dos veces cada vez que lo copie y pegue. En realidad, hay una manera de evitar esto, pasando preferencias a la clase interna. No puede crear un constructor de clases interno que acepte prefs como parámetro, ya que aparentemente no se recomienda para clases derivadas de PreferenceFragment. Además, no puede crear un método en la clase interna para obtener preferencias y llamar a addPreferencesFromResource de inmediato, ya que addPreferencesFromResource debe llamarse después de que se haya llamado a super.onCreate, y onCreate no se ha llamado justo después de que la clase derivada de PreferenceFragment haya ...
ecv
... sido instanciado. Por lo tanto, debe crear una nueva variable en la clase interna, crear un método de conjunto público para ella en la clase interna, dejar addPreferencesFromResource donde está después de la llamada a super.onCreate, inside onCreate, instanciar la clase interna, establecer las preferencias , y úselo en la llamada a getFragmentManager () ... como antes.
ecv
2
¡Tu código ha sido un salvavidas! Estaba tratando de apuntar a teléfonos viejos y nuevos, y perdí 3 días ... Y la respuesta es muy simple. Gracias
Devdatta Tengshe
22

Mi enfoque es muy cercano al de Garret Wilson (gracias, te voté;)

Además, ofrece compatibilidad con Android <3.

Acabo de reconocer que mi solución está aún más cerca de la de Kevin Remo . Es solo un poquito más limpio (ya que no se basa en el antipatrón "expection" ).

public class MyPreferenceActivity extends PreferenceActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            onCreatePreferenceActivity();
        } else {
            onCreatePreferenceFragment();
        }
    }

    /**
     * Wraps legacy {@link #onCreate(Bundle)} code for Android < 3 (i.e. API lvl
     * < 11).
     */
    @SuppressWarnings("deprecation")
    private void onCreatePreferenceActivity() {
        addPreferencesFromResource(R.xml.preferences);
    }

    /**
     * Wraps {@link #onCreate(Bundle)} code for Android >= 3 (i.e. API lvl >=
     * 11).
     */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void onCreatePreferenceFragment() {
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new MyPreferenceFragment ())
                .commit();
    }
}

Para un ejemplo "real" (pero más complejo) vea NusicPreferencesActivity y NusicPreferencesFragment .

schnatterer
fuente
@SuppressLint("NewApi")- evite - sea más preciso. ¿Lo has ejecutado por apis bajos? Se tiraráVerifyError
Mr_and_Mrs_D
Compruebo lo anterior en el emulador que ejecuta el nivel de API 10, el APK se creó con la versión 19 del SDK. ¿Qué versión del SDK usaste para compilar? ¿En qué nivel de API del dispositivo lo ejecutó? ¿Lo ejecutaste en el emulador o en un dispositivo físico? ¿Cuándo ocurrió exactamente el error? Si Ur construye con SDK <= 16, vea esta respuesta .
schnatterer
Te estás perdiendo el punto, y agrega @Mr_and_Mrs_D si quieres que me notifiquen. Para el VE, lea aquí: stackoverflow.com/questions/20271593/… . El @SuppressLint("NewApi")estilo es simplemente malo
Mr_and_Mrs_D
@M Bueno, entonces, ¿qué tal una propuesta sobre cómo evitar @SuppressLint("NewApi")en esta situación particular?
schnatterer
@TargetApi(Build.VERSION_CODES.HONEYCOMB)- no todas las advertencias para ninguna API :)
Mr_and_Mrs_D
6

En lugar de excepciones, solo use:

if (Build.VERSION.SDK_INT >= 11)

y use

@SuppressLint("NewApi")

para suprimir las advertencias.

Peter
fuente
1
porque no hace nada más que estrellarse: D
Ishtiaq
1
si es un accidente ... entonces intenta atraparlo: D
gumuruh
0

En lugar de usar un PreferenceActivitypara cargar directamente las preferencias, use un AppCompatActivityo equivalente que cargue un PreferenceFragmentCompatque cargue sus preferencias. Es parte de la biblioteca de soporte (ahora Android Jetpack) y proporciona compatibilidad con API 14.

En su build.gradle, agregue una dependencia para la biblioteca de soporte de preferencias:

dependencies {
    // ...
    implementation "androidx.preference:preference:1.0.0-alpha1"
}

Nota: Vamos a suponer que ya tiene sus preferencias XML creadas.

Para su actividad, cree una nueva clase de actividad. Si está utilizando temas materiales, debe extender un AppCompatActivity, pero puede ser flexible con esto:

public class MyPreferencesActivity extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_preferences_activity)
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, MyPreferencesFragment())
                    .commitNow()
        }
    }
}

Ahora para la parte importante: cree un fragmento que cargue sus preferencias desde XML:

public class MyPreferencesFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.my_preferences_fragment); // Your preferences fragment
    }
}

Para obtener más información, lea los documentos para desarrolladores de Android PreferenceFragmentCompat.

Willie Chalmers III
fuente