¿PreferenceFragment fue excluido intencionalmente del paquete de compatibilidad?

153

Estoy buscando escribir preferencias que se puedan aplicar a dispositivos 3.0 y pre-3.0. Descubriendo que PreferenceActivitycontiene métodos obsoletos (aunque estos se usan en el código de muestra adjunto), miré PreferenceFragementy el paquete de compatibilidad para resolver mis problemas.

Sin embargo, parece que PreferenceFragmentno está en el paquete de compatibilidad. ¿Alguien puede decirme si esto fue intencional? Si es así, ¿puedo apuntar fácilmente a un rango de dispositivos (es decir, <3.0 y> = 3.0) o tendré que saltar a través de aros? Si no se excluyó intencionalmente, ¿podemos esperar una nueva versión del paquete de compatibilidad? ¿O hay otra solución alternativa que sea segura de usar?

Salud

James

James
fuente
1
Este es mi enfoque para resolver el problema: stackoverflow.com/questions/14076073/…
ecv
Alguien hizo un tercero PreferenceFragmentque olvidará incluso está allí. Mira mi respuesta .
theblang
Chris Banes aborda esto en un comentario en su blog . Dijo que la razón es"Because most of Preferences' implementation is hidden, therefore impossible to backport without lots of hackery."
theblang
Ver mi respuesta actualizada . PreferenceFragmentCompatfue agregado recientemente a la biblioteca de soporte.
theblang

Respuestas:

90

Descubrir que PreferenceActivity contiene métodos obsoletos (aunque estos se usan en el código de muestra adjunto)

Los métodos en desuso están en desuso a partir de Android 3.0. Están perfectamente bien en todas las versiones de Android, pero la dirección es usarlo PreferenceFragmenten Android 3.0 y superior.

¿Alguien puede decirme si esto fue intencional?

Supongo que es una cuestión de tiempo de ingeniería, pero eso es solo una suposición.

Si es así, ¿puedo apuntar fácilmente a una gama de dispositivos (es decir, <3.0 y> = 3.0) o tendré que saltar a través de aros?

Considero que se hace "fácilmente". Tenga dos PreferenceActivityimplementaciones separadas , una con encabezados de preferencia y PreferenceFragmentsotra con el enfoque original. Elija el correcto en el punto que necesita (por ejemplo, cuando el usuario hace clic en el elemento del menú de opciones). Aquí hay un proyecto de muestra que demuestra esto. O bien, tenga un único PreferenceActivityque maneje ambos casos, como en este proyecto de muestra .

Si no se excluyó intencionalmente, ¿podemos esperar una nueva versión del paquete de compatibilidad?

Lo descubrirá cuando el resto de nosotros lo sepa, es decir, si se envía.

¿O hay otra solución alternativa que sea segura de usar?

Véase más arriba.

CommonsWare
fuente
Saludos Mark. Había visto que había hecho comentarios sobre esto en un par de lugares (grupo de google de Android y su blog) pero quería una respuesta definitiva (en la medida en que uno pueda obtener las circunstancias).
James
@ James: Sí, el problema estará en la definición de XML de preferencia, obteniendo algo que funcionará bien como fragmentos y también concatenados, ya que no estoy seguro de que <include>funcione con XML de preferencia. Por cierto, si está suscrito, la actualización del libro que hace referencia a este proyecto se anunció hace unos minutos.
CommonsWare
77
Lo siento, pero no estoy seguro de qué punto estás tratando de hacer aquí. No responde nada, simplemente comenta / adivina / se refiere a enlaces externos irrelevantes que no tienen nada que ver con el problema. La pregunta es si la omisión fue intencional o no, sin la versión de compatibilidad de PreferenceFragment no hay forma de extender PreferenceActivity de la manera que describió porque si PreferenceFragment no existe, tampoco getSupportFragmentManager () o cualquiera de los otros métodos requerido para usar fragmentos en primer lugar.
Justin Buser
8
@JustinBuser: "La pregunta es si la omisión fue intencional o no", las únicas personas que pueden responder ese trabajo para Google. Puede buscar un trabajo en Google para intentar averiguarlo. "no hay forma de extender PreferenceActivity de la manera que ha descrito": puede descargar el código al que me he vinculado.
CommonsWare
9
@JustinBuser Para el registro, Mark respondió mi pregunta. Eso es evidente al aceptar su respuesta.
James
21

La sutil implicación de la respuesta de @CommonsWare es que su aplicación debe elegir entre la API de compatibilidad o la API de fragmentos incorporada (desde SDK 11 más o menos). De hecho, eso es lo que ha hecho la recomendación "fácilmente". En otras palabras, si desea usar PreferenceFragment, su aplicación debe usar la API de fragmentos incorporada y tratar con los métodos obsoletos en PreferenceActivity. Por el contrario, si es importante que su aplicación use la compatibilidad. API se enfrentará a no tener una clase PreferenceFragment en absoluto. Por lo tanto, la orientación de dispositivos no es un problema, pero el salto de aro ocurre cuando tiene que elegir una u otra API y, por lo tanto, enviar su diseño a soluciones imprevistas. Necesito el compat. API, así que voy a crear mi propia clase PreferenceFragment y ver cómo funciona. En el peor de los casos, yo '

EDITAR: ¿Después de intentar y mirar el código en http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceFragment.java? av = h - crear mi propio PreferenceFragment no va a suceder. Parece que el uso liberal de package-private en PreferenceManager en lugar de 'protegido' es el bloqueador principal. Realmente no parece que haya seguridad o una buena motivación para haberlo hecho, y no es genial para las pruebas unitarias, pero bueno ... menos escribir, supongo ...

EDITAR v2: En realidad sucedió y funcionó. Definitivamente fue un dolor de cabeza hacer que el código funcionara con el JAR de API de compatibilidad. Tuve que copiar alrededor del 70% del paquete com.android.preference del SDK a mi aplicación y luego luchar con un código Java típicamente de calidad mediocre en Android. Usé la v14 del SDK. Hubiera sido mucho más fácil para un ingeniero de Goog hacer lo que hice, al contrario de lo que escuché decir algunos ingenieros líderes de Android sobre este tema.

Por cierto, ¿dije "apuntar a dispositivos no es un problema"? Es totalmente ... si usa com.android.preference no podrá cambiar con la API de compatibilidad sin una refactorización importante. Registro de diversión!

Tenaz
fuente
Déjame ser más directo. Si todo lo que le importa es apuntar a Honeycomb y más (¿cuál tiene la cuota de mercado?), ¡Vote por la respuesta de @Commonsware! Si te importa la mayoría de los dispositivos Android en el mercado hoy en día, deberías leer mi respuesta.
Tenaz
44
¿estarías dispuesto a compartir cómo hiciste esto? Me encuentro exactamente con el mismo problema, solo mi PreferenceActivity tiene que usar cargadores y, por lo tanto, debo usar la biblioteca de compatibilidad.
Karakuri
3
@Tenacious Me gusta tu investigación, bien hecho. Sin embargo, creo que alguien debería dejar las cosas claras en su primer comentario allí: el código de Commonsware funcionará en dispositivos pre y post HC; pruébelo primero antes de hacer comentarios como ese. Lo que debe tener en cuenta es el enlace tardío utilizado en tiempo de ejecución para admitir dispositivos anteriores. La verificación de versión en tiempo de ejecución se encarga de admitir a ambas familias de SO: este es un patrón de Android común (no uno que me guste, pero es importante que los desarrolladores de Android aprendan y se familiaricen) ... Entonces, para futuros lectores: no No descarte ninguno de los enfoques.
Richard Le Mesurier
@RichardLeMesurier, pero el método de Commonsware no es adecuado si necesita preferencias dentro de DrawerLayout
neworld
16

Sobre la base de la respuesta de CommonsWare, así como las observaciones de Tenacious, se me ocurrió una única solución de clase descendiente capaz de apuntar a todas las versiones actuales de la API de Android con un mínimo alboroto y sin duplicación de código o recurso. Consulte mi respuesta a la pregunta relacionada aquí: PreferenceActivity Android 4.0 y versiones anteriores

o en mi blog: http://www.blackmoonit.com/2012/07/all_api_prefsactivity/

Probado en dos tabletas con 4.0.3 y 4.0.4, así como en un teléfono con 4.0.4 y 2.3.3 y también en un emulador con 1.6.

Tío Code Monkey
fuente
10

En agosto de 2015, Google lanzó la nueva Biblioteca de soporte de preferencias v7 .

Ahora puede usar PreferenceFragmentCompat con cualquiera ActivityoAppCompatActivity

public static class PrefsFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
}

Tienes que configurar preferenceThemetu tema:

<style name="AppTheme" parent="@style/Theme.AppCompat.Light">
  ...
  <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

De esta forma, puede personalizar el preferenceThemeestilo de los diseños utilizados para cada tipo de preferencia sin afectar otras partes de su Actividad.

Gabriele Mariotti
fuente
1
Creo que te pierdes una función: onCreatePreferences
desarrollador de Android
Esto me salvó el día! Me pregunto por qué no es esto visible en la Estructura del Proyecto de Android Studio ... Por cierto, tienes un error tipográfico en tu código. Debe ser "extiende PreferenceFragmentCompat"
Grzegorz D.
7

La respuesta de Tenacious es correcta, pero aquí hay algunos detalles más.

La razón por la que no puede "crear un diseño normal y vincular los componentes de la vista a las preferencias compartidas manualmente" es que hay algunas omisiones sorprendentes en la API android.preferences. PreferenceActivity y PreferenceFragment tienen acceso a métodos críticos de PreferenceManager no públicos, sin los cuales no puede implementar una IU de preferencias propia.

En particular, para construir una jerarquía de Preferencias a partir de un archivo XML, debe usar un PreferenceManager, pero todos los constructores de PreferenceManager son privados de paquetes u ocultos. El método de adjuntar los oyentes Preference onClick a su actividad también es privado de paquete.

Y no puede evitar esto poniendo disimuladamente su implementación en el paquete android.preferences, porque los métodos no públicos en las API de Android en realidad se omiten del SDK. Con un poco de creatividad que implica reflexión y proxies dinámicos, aún puede llegar a ellos. La única alternativa, como dice Tenacious, es bifurcar todo el paquete android.preference, que incluye al menos 15 clases, 5 diseños y una cantidad similar de elementos style.xml y attrs.xml.

Entonces, para responder la pregunta original, la razón por la que Google no incluyó PreferenceFragment en el paquete de compatibilidad es que habrían tenido exactamente la misma dificultad que Tenacious y yo. Incluso Google no puede retroceder en el tiempo y hacer públicos esos métodos en las plataformas antiguas (aunque espero que lo hagan en futuras versiones).

mhsmith
fuente
2

El objetivo de mi aplicación es API +14, pero debido al uso de la biblioteca de soporte para una navegación sofisticada, no pude usarlo android.app.Fragmenty tuve que usarlo android.support.v4.app.Fragment, pero también necesitaba tenerlo PreferenceFragmentsin grandes cambios en el código.

Entonces, mi solución fácil para tener ambos mundos de biblioteca de soporte y PreferenceFragment:

private android.support.v4.app.Fragment fragment;
private android.app.Fragment nativeFragment = null;

private void selectItem(int position) {
    fragment = null;
    boolean useNativeFragment = false;
    switch (position) {
    case 0:
        fragment = new SampleSupprtFragment1();
        break;
    case 1:
        fragment = new SampleSupprtFragment2();
        break;
    case 2:
        nativeFragment = new SettingsFragment();
        useNativeFragment = true;
        break;
    }
    if (useNativeFragment) {
        android.app.FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, nativeFragment).commit();
    } else {
        if (nativeFragment != null) {
            getFragmentManager().beginTransaction().remove(nativeFragment)
                .commit();
            nativeFragment = null;
        }
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
            .replace(R.id.content_frame, fragment).commit();
    }
}
Mohsen Afshin
fuente
2

Necesitaba integrar las preferencias en el diseño de la aplicación y mantener el soporte para Android 2.3. Entonces todavía necesitaba PreferencesFragment.

Después de algunas búsquedas encontré android-support-v4-preferenciafragment lib. Esta lib ahorra mucho tiempo para copiar y refactorizar el PreferencesFragment original como dijo Tenacious. Funciona bien y los usuarios disfrutan de sus preferencias.

nuevo mundo
fuente