2012-06-18 9 views
17

Ho difficoltà a cercare un buon esempio di MultiSelectListPreference fornito nell'API di Android. Ho visto molti riferimenti a this blog, e se questo è il risultato finale che desidero, non voglio creare una classe per ogni preferenza di selezione multipla che voglio implementare. In definitiva voglio vedere il codice XML preferenze per una semplice finestra di dialogo multi-selezione (che popoleranno i valori per in modo dinamico), anche per la chiamata a addPreferencesFromResource(R.xml.preferences);Esempio MultiSelectListPreference

Attualmente, ho:

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

e quando provo a chiamare addPreferencesFromResource nelle mie attività onCreate chiamare sto ottenendo il seguente errore:

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 

Attendo con ansia la vostra risposta!

+0

Perché prendere così tanti problemi. Nel tuo frammento "onCreate" ottieni la preferenza usando "findPreference" e usa il metodo "setEntries/setEntryValues" per impostare dinamicamente. – Ishaan

risposta

11

Sigrist è corretto, per risolvere l'errore iniziale, si sta vedendo. Ha bisogno dei valori di default forniti, anche se è vuoto. Questo mi ha aiutato nel fatto che voglio fornire valori a RUNTIME, ma non scherzo con l'implementazione completa.

Vedere questo codice per come fornisco i valori in fase di esecuzione, senza dover gestire l'implementazione 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[]{})); 
} 
} 

Nel mio strings.xml

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

Nel mio 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"/> 

So che questo è un po 'di una vecchia questione, ma mi ha aiutato, ecco la mia risposta!

42

è necessario specificare la proprietà 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" 
     /> 

Se non si desidera che i valori di default, creare un array vuoto nel vostro strings.xml

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

Questo è fondamentalmente quello che suggerisce l'API di Android. Sono interessato ad aggiungere voci su RUNTIME. Non c'è nulla di dinamico nella modifica di stringhe.xml – mohbandy

3

Ho creato MultiSelectListPreference per i dispositivi eseguire Android nell'API prima del livello 11.

  • Supporta ChangeListener ricezione lista di sel valori
  • Supporta automaticamente l'impostazione del riepilogo.
  • Esempi allegati.

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

Non è possibile utilizzare le voci dinamiche caricando con questa classe. – James