studio preferencefragmentcompat findpreference android android-fragments android-4.4-kitkat

android - preferencefragmentcompat - preferencefragment deprecated



isValidFragment Android API 19 (9)

Aquí está mi archivo headers_preferences.xml:

<?xml version="1.0" encoding="utf-8"?> <preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> <header android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs1Fragment" android:title="Change Your Name" /> <header android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs2Fragment" android:title="Change Your Group''''s Name" /> <header android:fragment="com.gammazero.signalrocket.AppPreferencesFragment$Prefs3Fragment" android:title="Change Map View" /> </preference-headers>

En mi PreferencesActivity, donde se produce el código isValidFragment, simplemente lo di vuelta en su cabeza:

@Override protected boolean isValidFragment(String fragmentName) { // return AppPreferencesFragment.class.getName().contains(fragmentName); return fragmentName.contains (AppPreferencesFragment.class.getName()); }

Siempre que use la cadena AppPreferencesFragment al comienzo de todos mis nombres de fragmentos, todos validan muy bien.

Cuando pruebo mi aplicación con Android KitKat, tengo un error en PreferenceActivity.

Las subclases de PreferenceActivity deben anular isValidFragment (String) para verificar que la clase Fragment sea válida. com.crbin1.labeltodo.ActivityPreference no ha verificado si el fragmento com.crbin1.labeltodo.StockPreferenceFragment es válido

En la documentación, encuentro la siguiente explicación

protected boolean isValidFragment (String fragmentName)

Agregado en el nivel de API 19

Las subclases deben anular este método y verificar que el fragmento dado sea un tipo válido para adjuntar a esta actividad. La implementación predeterminada es verdadera para las aplicaciones creadas para Android: targetSdkVersion anterior a KITKAT. Para versiones posteriores, arrojará una excepción.

No encuentro ningún ejemplo para resolver el problema.


Descubrí que podía tomar una copia de los nombres de mi fragmento de mi recurso de encabezado a medida que se cargaba:

public class MyActivity extends PreferenceActivity { private static List<String> fragments = new ArrayList<String>(); @Override public void onBuildHeaders(List<Header> target) { loadHeadersFromResource(R.xml.headers,target); fragments.clear(); for (Header header : target) { fragments.add(header.fragment); } } ... @Override protected boolean isValidFragment(String fragmentName) { return fragments.contains(fragmentName); } }

De esta forma, no necesito recordar actualizar una lista de fragmentos enterrados en el código si deseo actualizarlos.

Tenía la esperanza de usar getHeaders() y la lista existente de encabezados directamente, pero parece que la actividad se destruye después de onBuildHeaders() y se recrea antes de que se isValidFragment() .

Esto puede deberse a que el Nexus 7 en el que estoy probando no hace actividades de preferencia de dos paneles. De ahí la necesidad del miembro de la lista estática también.


Esta API se agregó debido a una vulnerabilidad recientemente descubierta. Por favor vea http://ibm.co/1bAA8kF o http://ibm.co/IDm2Es

10 de diciembre de 2013: "Recientemente hemos revelado una nueva vulnerabilidad para el Equipo de seguridad de Android. [...] Para ser más precisos, cualquier aplicación que extendiera la clase PreferenceActivity utilizando una actividad exportada era automáticamente vulnerable. Se proporcionó un parche en Android KitKat. Si se preguntaba por qué su código ahora está roto, es debido al parche de Android KitKat que requiere que las aplicaciones anulen el nuevo método, PreferenceActivity.isValidFragment, que se ha agregado a Android Framework ". - Desde el primer enlace de arriba


Intenta esto ... así es como verificamos la validez del fragmento.

protected boolean isValidFragment(String fragmentName) { return StockPreferenceFragment.class.getName().equals(fragmentName); }


No estoy seguro de si la implementación de Lane está libre de las vulnerabilidades que se discuten here pero si lo es, entonces creo que una mejor solución sería evitar el uso de esa lista estática y simplemente hacer lo siguiente:

@Override protected boolean isValidFragment(String fragmentName) { ArrayList<Header> target = new ArrayList<>(); loadHeadersFromResource(R.xml.pref_headers, target); for (Header h : target) { if (fragmentName.equals(h.fragment)) return true; } return false; }


Por pura curiosidad, también puedes hacer esto:

@Override protected boolean isValidFragment(String fragmentName) { return MyPreferenceFragmentA.class.getName().equals(fragmentName) || MyPreferenceFragmentB.class.getName().equals(fragmentName) || // ... Finish with your last fragment. ;}


Verificado con un dispositivo real de 4.4:

(1) si su archivo proguard.cfg tiene esta línea ( que muchos definen de todos modos ):

-keep public class com.fullpackage.MyPreferenceFragment

(2) que la implementación más eficiente sería:

@TargetApi(Build.VERSION_CODES.HONEYCOMB) public class EditPreferencesHC extends PreferenceActivity { ... protected boolean isValidFragment (String fragmentName) { return "com.fullpackage.MyPreferenceFragment".equals(fragmentName); } }


esta es mi solución:

  • si necesitas encabezados de reconstrucción dinámica
  • si usa extras para iniciar la actividad de preferencias, ¡el enfoque onBuildHeaders () fallará! (con los extras de inicio de intento a continuación, ¿por qué?) - simple porque nunca se llama a onBuildHeaders ():

    Intent.putExtra (PreferenceActivity.EXTRA_SHOW_FRAGMEN, Fragment.class.getName ()); Intent.putExtra (PreferenceActivity.EXTRA_NO_HEADERS, true);

Esta es una clase de ejemplo:

/** * Preference Header for showing settings and add view as two panels for tablets * for ActionBar we need override onCreate and setContentView */ public class SettingsPreferenceActivity extends PreferenceActivity { /** valid fragment list declaration */ private List<String> validFragmentList; /** some example irrelevant class for holding user session */ SessionManager _sessionManager; @Override public void onBuildHeaders(List<Header> target) { /** load header from res */ loadHeadersFromResource(getValidResId(), target); } /** * this API method was added due to a newly discovered vulnerability. */ @Override protected boolean isValidFragment(String fragmentName) { List<Header> headers = new ArrayList<>(); /** fill fragments list */ tryObtainValidFragmentList(getValidResId(), headers); /** check id valid */ return validFragmentList.contains(fragmentName); } /** try fill list of valid fragments */ private void tryObtainValidFragmentList(int resourceId, List<Header> target) { /** check for null */ if(validFragmentList==null) { /** init */ validFragmentList = new ArrayList(); } else { /** clear */ validFragmentList.clear(); } /** load headers to list */ loadHeadersFromResource(resourceId, target); /** set headers class names to list */ for (Header header : target) { /** fill */ validFragmentList.add(header.fragment); } } /** obtain valid res id to build headers */ private int getValidResId() { /** get session manager */ _sessionManager = SessionManager.getInstance(); /** check if user is authorized */ if (_sessionManager.getCurrentUser().getWebPart().isAuthorized()) { /** if is return full preferences header */ return R.xml.settings_preferences_header_logged_in; } else { /** else return short header */ return R.xml.settings_preferences_header_logged_out; } } }


mi solución (en lugar de crear ArrayList de clase) ya que los fragmentos que están cargados suponen ser la subclase de PreferenceFragment.class ejecutar esta comprobación en el método @OverRide

@Override protected boolean isValidFragment(String fragmentName) { try { Class cls = Class.forName(fragmentName); return (cls.getSuperclass().equals(PreferenceFragment.class)); // true if superclass is PreferenceFragmnet } catch (ClassNotFoundException e) { e.printStackTrace(); } return false; }