2012-06-18 11 views
17

Tengo dificultades para encontrar un buen ejemplo de la MultiSelectListPreference proporcionada en la API de Android. He visto muchas referencias a this blog, y aunque este es el resultado final que deseo, no quiero crear una clase para cada preferencia de selección múltiple que deseo implementar. En última instancia quiero ver el xml preferencias para un simple diálogo de selección múltiple (que voy a rellenar los valores de forma dinámica), así que la llamada a addPreferencesFromResource(R.xml.preferences);Ejemplo de MultiSelectListPreferencia

Actualmente, tengo:

<MultiSelectListPreference 
    android:defaultValue="" 
    android:enabled="true" 
    android:entries="@array/pref_default_entries" 
    android:entryValues="@array/pref_default_values" 
    android:key="TargetList" 
    android:persistent="true" 
    android:summary="@string/TargetSummary" 
    android:title="@string/TargetTitle" /> 

y cuando intento llamar addPreferencesFromResource en mis actividades onCreate llamar estoy consiguiendo el error siguiente:

06-18 13:59:30.690: E/AndroidRuntime(6052): FATAL EXCEPTION: main 
06-18 13:59:30.690: E/AndroidRuntime(6052): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tracker/com.tracker.TrackerActivity}: android.view.InflateException: Binary XML file line #37: Error inflating class java.lang.reflect.Constructor 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1818) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1834) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.app.ActivityThread.access$500(ActivityThread.java:122) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1027) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.os.Handler.dispatchMessage(Handler.java:99) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.os.Looper.loop(Looper.java:132) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.app.ActivityThread.main(ActivityThread.java:4126) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at java.lang.reflect.Method.invokeNative(Native Method) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at java.lang.reflect.Method.invoke(Method.java:491) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at dalvik.system.NativeStart.main(Native Method) 
06-18 13:59:30.690: E/AndroidRuntime(6052): Caused by: android.view.InflateException: Binary XML file line #37: Error inflating class java.lang.reflect.Constructor 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.GenericInflater.createItem(GenericInflater.java:397) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.GenericInflater.onCreateItem(GenericInflater.java:417) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.GenericInflater.createItemFromTag(GenericInflater.java:428) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.GenericInflater.rInflate(GenericInflater.java:481) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.GenericInflater.inflate(GenericInflater.java:326) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.GenericInflater.inflate(GenericInflater.java:263) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.PreferenceManager.inflateFromResource(PreferenceManager.java:269) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.PreferenceActivity.addPreferencesFromResource(PreferenceActivity.java:1366) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at com.tracker.TrackerActivity.onCreate(TrackerActivity.java:30) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1050) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1782) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  ... 11 more 
06-18 13:59:30.690: E/AndroidRuntime(6052): Caused by: java.lang.reflect.InvocationTargetException 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at java.lang.reflect.Constructor.constructNative(Native Method) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at java.lang.reflect.Constructor.newInstance(Constructor.java:416) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.GenericInflater.createItem(GenericInflater.java:383) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  ... 21 more 
06-18 13:59:30.690: E/AndroidRuntime(6052): Caused by: java.lang.NullPointerException 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.content.res.AssetManager.getResourceTextArray(AssetManager.java:215) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.content.res.Resources.getTextArray(Resources.java:435) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.content.res.TypedArray.getTextArray(TypedArray.java:628) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.MultiSelectListPreference.onGetDefaultValue(MultiSelectListPreference.java:210) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.Preference.<init>(Preference.java:257) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.DialogPreference.<init>(DialogPreference.java:69) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.DialogPreference.<init>(DialogPreference.java:90) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  at android.preference.MultiSelectListPreference.<init>(MultiSelectListPreference.java:49) 
06-18 13:59:30.690: E/AndroidRuntime(6052):  ... 24 more 

Espero su respuesta!

+0

¿Por qué tomar tantos problemas. En su fragmento "onCreate", obtenga la preferencia usando "findPreference" y use el método "setEntries/setEntryValues" para establecer dinámicamente. – Ishaan

Respuesta

11

Sigrist es correcta, para resolver el error inicial que está viendo. Necesita valores predeterminados proporcionados, incluso si está vacío. Esto me ayudó en que quiero proporcionar valores en RUNTIME, pero no meterse con la implementación completa.

Consulte este código para ver cómo proporciono valores en tiempo de ejecución, sin tener que manejar la implementación completa.

public class CalendarListPreference extends MultiSelectListPreference { 

ContentResolver cr; 
Cursor cursor; 
String[] projection = new String[] {CalendarContract.Calendars.NAME, CalendarContract.Calendars.CALENDAR_DISPLAY_NAME}; 
String selection = "(" + CalendarContract.Calendars.VISIBLE + " = ?)"; 
String[] selectionArgs = new String[] { "1" }; 

public CalendarListPreference(Context context, AttributeSet attrs) { 
    super(context, attrs); 

    List<CharSequence> entries = new ArrayList<CharSequence>(); 
    List<CharSequence> entriesValues = new ArrayList<CharSequence>(); 

    cr = context.getContentResolver(); 
    cursor = cr.query(CalendarContract.Calendars.CONTENT_URI, projection, selection, selectionArgs, null); 

    while (cursor.moveToNext()) { 
     String name = cursor.getString(0); 
     String displayName = cursor.getString(1); 

     entries.add(name); 
     entriesValues.add(displayName); 
    } 

    setEntries(entries.toArray(new CharSequence[]{})); 
    setEntryValues(entriesValues.toArray(new CharSequence[]{})); 
} 
} 

En mi strings.xml

<string-array name="pref_calendar_list_default"> 
</string-array> 

En mi preferences.xml

<com.mynameistodd.autovolume.CalendarListPreference 
android:defaultValue="@array/pref_calendar_list_default" 
android:key="@string/pref_calendar_list_key" 
android:summary="@string/pref_calendar_list_summary" 
android:title="@string/pref_calendar_list_title" 
android:dependency="@string/pref_calendar_enabled_key"/> 

Sé que esto es un poco de una vieja pregunta, pero me ayudó, así que aquí es ¡mi respuesta!

42

Es necesario especificar la propiedad DefaultValues ​​

<MultiSelectListPreference 
     android:dialogTitle="@string/mode_repeat" 
     android:key="mode_repeat" 
     android:summary=""   
     android:title="@string/mode_repeat" 
     android:entries="@array/weekdays" 
     android:entryValues="@array/weekdays_values" 
     android:defaultValue="@array/empty_array" 
     /> 

Si no desea que los valores por defecto, crear una matriz vacía en su strings.xml

<string-array name="empty_array"/> 
+4

Esto es básicamente lo que sugiere la API de Android. Estoy interesado en agregar entradas en RUNTIME. No hay nada dinámico sobre la modificación de strings.xml – mohbandy

3

he creado para dispositivos MultiSelectListPreference ejecutando Android en la API antes que en el nivel 11.

  • Admite ChangeListener recibiendo la lista de sel valores ectivos.
  • Admite la configuración automática del resumen.
  • Ejemplos adjuntos.

https://gist.github.com/cardil/4754571

package pl.wavesoftware.widget; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Iterator; 
import java.util.List; 

import android.app.AlertDialog.Builder; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.content.DialogInterface.OnMultiChoiceClickListener; 
import android.content.res.TypedArray; 
import android.preference.ListPreference; 
import android.util.AttributeSet; 

public class MultiSelectListPreference extends ListPreference { 

    private String separator; 
    private static final String DEFAULT_SEPARATOR = "\u0001\u0007\u001D\u0007\u0001"; 
    private boolean[] entryChecked; 

    public MultiSelectListPreference(Context context, AttributeSet attributeSet) { 
     super(context, attributeSet); 
     entryChecked = new boolean[getEntries().length]; 
     separator = DEFAULT_SEPARATOR; 
    } 

    public MultiSelectListPreference(Context context) { 
     this(context, null); 
    } 

    @Override 
    protected void onPrepareDialogBuilder(Builder builder) { 
     CharSequence[] entries = getEntries(); 
     CharSequence[] entryValues = getEntryValues(); 
     if (entries == null || entryValues == null 
       || entries.length != entryValues.length) { 
      throw new IllegalStateException(
        "MultiSelectListPreference requires an entries array and an entryValues " 
          + "array which are both the same length"); 
     } 

     restoreCheckedEntries(); 
     OnMultiChoiceClickListener listener = new DialogInterface.OnMultiChoiceClickListener() { 
      public void onClick(DialogInterface dialog, int which, boolean val) { 
       entryChecked[which] = val; 
      } 
     }; 
     builder.setMultiChoiceItems(entries, entryChecked, listener); 
    } 

    private CharSequence[] unpack(CharSequence val) { 
     if (val == null || "".equals(val)) { 
      return new CharSequence[0]; 
     } else { 
      return ((String) val).split(separator); 
     } 
    } 

    /** 
    * Gets the entries values that are selected 
    * 
    * @return the selected entries values 
    */ 
    public CharSequence[] getCheckedValues() { 
     return unpack(getValue()); 
    } 

    private void restoreCheckedEntries() { 
     CharSequence[] entryValues = getEntryValues(); 

     // Explode the string read in sharedpreferences 
     CharSequence[] vals = unpack(getValue()); 

     if (vals != null) { 
      List<CharSequence> valuesList = Arrays.asList(vals); 
      for (int i = 0; i < entryValues.length; i++) { 
       CharSequence entry = entryValues[i]; 
       entryChecked[i] = valuesList.contains(entry); 
      } 
     } 
    } 

    @Override 
    protected void onDialogClosed(boolean positiveResult) { 
     List<CharSequence> values = new ArrayList<CharSequence>(); 

     CharSequence[] entryValues = getEntryValues(); 
     if (positiveResult && entryValues != null) { 
      for (int i = 0; i < entryValues.length; i++) { 
       if (entryChecked[i] == true) { 
        String val = (String) entryValues[i]; 
        values.add(val); 
       } 
      } 

      String value = join(values, separator); 
      setSummary(prepareSummary(values)); 
      setValueAndEvent(value); 
     } 
    } 

    private void setValueAndEvent(String value) { 
     if (callChangeListener(unpack(value))) { 
      setValue(value); 
     } 
    } 

    private CharSequence prepareSummary(List<CharSequence> joined) { 
     List<String> titles = new ArrayList<String>(); 
     CharSequence[] entryTitle = getEntries(); 
     CharSequence[] entryValues = getEntryValues(); 
     int ix = 0; 
     for (CharSequence value : entryValues) { 
      if (joined.contains(value)) { 
       titles.add((String) entryTitle[ix]); 
      } 
      ix += 1; 
     } 
     return join(titles, ", "); 
    } 

    @Override 
    protected Object onGetDefaultValue(TypedArray typedArray, int index) { 
     return typedArray.getTextArray(index); 
    } 

    @Override 
    protected void onSetInitialValue(boolean restoreValue, 
      Object rawDefaultValue) { 
     String value = null; 
     CharSequence[] defaultValue; 
     if (rawDefaultValue == null) { 
      defaultValue = new CharSequence[0]; 
     } else { 
      defaultValue = (CharSequence[]) rawDefaultValue; 
     } 
     List<CharSequence> joined = Arrays.asList(defaultValue); 
     String joinedDefaultValue = join(joined, separator); 
     if (restoreValue) { 
      value = getPersistedString(joinedDefaultValue); 
     } else { 
      value = joinedDefaultValue; 
     } 

     setSummary(prepareSummary(Arrays.asList(unpack(value)))); 
     setValueAndEvent(value); 
    } 

    /** 
    * Joins array of object to single string by separator 
    * 
    * Credits to kurellajunior on this post 
    * http://snippets.dzone.com/posts/show/91 
    * 
    * @param iterable 
    *   any kind of iterable ex.: <code>["a", "b", "c"]</code> 
    * @param separator 
    *   separetes entries ex.: <code>","</code> 
    * @return joined string ex.: <code>"a,b,c"</code> 
    */ 
    protected static String join(Iterable<?> iterable, String separator) { 
     Iterator<?> oIter; 
     if (iterable == null || (!(oIter = iterable.iterator()).hasNext())) 
      return ""; 
     StringBuilder oBuilder = new StringBuilder(String.valueOf(oIter.next())); 
     while (oIter.hasNext()) 
      oBuilder.append(separator).append(oIter.next()); 
     return oBuilder.toString(); 
    } 

} 
+2

No es posible usar entradas dinámicas cargando con esta clase. – James