2013-03-21 3 views
6

Vorrei visualizzare una schermata delle preferenze come quella nell'app delle impostazioni di Android: utilizzando le intestazioni, PreferenceActivity, PreferenceFragment e le categorie di intestazioni.Android - Categorie di intestazioni in PreferenzeAttività con preferenzaFragment

ho wan't questo risultato su una tavoletta:

enter image description here

E questo su uno smartphone:

enter image description here

Funziona se mi basta usare le intestazioni di base, ma se provo ad aggiungere categorie, funziona sullo smartphone e si blocca sul tablet, dove ottengo l'eccezione "java.lang.NullPointerException: name == null":

FATAL EXCEPTION: main 
java.lang.RuntimeException: Unable to start activity ComponentInfo{fr.ifremer.testandroid/fr.ifremer.testandroid.models.preferences.MainPreferenceActivity}: java.lang.NullPointerException: name == null 
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2110) 
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2135) 
    at android.app.ActivityThread.access$700(ActivityThread.java:140) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237) 
    at android.os.Handler.dispatchMessage(Handler.java:99) 
    at android.os.Looper.loop(Looper.java:137) 
    at android.app.ActivityThread.main(ActivityThread.java:4921) 
    at java.lang.reflect.Method.invokeNative(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:511) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1038) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805) 
    at dalvik.system.NativeStart.main(Native Method) 
Caused by: java.lang.NullPointerException: name == null 
    at java.lang.VMClassLoader.findLoadedClass(Native Method) 
    at java.lang.ClassLoader.findLoadedClass(ClassLoader.java:354) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:491) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:461) 
    at android.app.Fragment.instantiate(Fragment.java:574) 
    at android.preference.PreferenceActivity.switchToHeaderInner(PreferenceActivity.java:1222) 
    at android.preference.PreferenceActivity.switchToHeader(PreferenceActivity.java:1255) 
    at android.preference.PreferenceActivity.onCreate(PreferenceActivity.java:630) 
    at fr.ifremer.testandroid.models.preferences.MainPreferenceActivity.onCreate(MainPreferenceActivity.java:19) 
    at android.app.Activity.performCreate(Activity.java:5206) 
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1094) 
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2074) 
    ... 11 more 

I muggenti sono i pezzi di codice coinvolti. Li ho presi principalmente dalla fonte di app delle impostazioni di Android.

Qualche idea?

Grazie in anticipo


MainPreferenceActivity:

public class MainPreferenceActivity extends PreferenceActivity { 

    private static List<Header> _headers; 

    @Override 
    public void onBuildHeaders(List<Header> headers) { 

     _headers = headers; 
     loadHeadersFromResource(R.xml.preference_headers, headers); 
    } 

    @Override 
    public void setListAdapter(ListAdapter adapter) { 

     if (adapter == null) { 
      super.setListAdapter(null); 
     } else { 
      super.setListAdapter(new HeaderAdapter(this, _headers)); 
     } 
    } 
} 

PreferencesFragment:

public class PreferencesFragment extends PreferenceFragment { 

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

     String settings = getArguments().getString("settings"); 

     if (settings.equals("DIVE")) { 

      addPreferencesFromResource(R.xml.preference_dive_tile); 
     } 
     else if (settings.equals("MAP")) { 

      addPreferencesFromResource(R.xml.preference_map_tile); 
     } 
    } 
} 

preference_headers.xml:

<?xml version="1.0" encoding="utf-8"?> 
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android" > 

    <header 
     android:id="@+id/header_section_1" 
     android:title="Section 1" /> 

    <header 
     android:fragment="fr.ifremer.testandroid.models.preferences.PreferencesFragment" 
     android:summary="DIVE summary" 
     android:title="DIVE title" > 
     <extra 
      android:name="settings" 
      android:value="DIVE" /> 
    </header> 
    <header 
     android:fragment="fr.ifremer.testandroid.models.preferences.PreferencesFragment" 
     android:summary="MAP summary" 
     android:title="MAP title" > 
     <extra 
      android:name="settings" 
      android:value="MAP" /> 
    </header> 

</preference-headers> 

Ultimo ma non meno importante, HeaderAdapter:

public class HeaderAdapter extends ArrayAdapter<Header> { 

    static final int HEADER_TYPE_CATEGORY = 0; 
    static final int HEADER_TYPE_NORMAL = 1; 
    private static final int HEADER_TYPE_COUNT = HEADER_TYPE_NORMAL + 1; 

    private LayoutInflater mInflater; 

    private static class HeaderViewHolder { 
     ImageView icon; 
     TextView title; 
     TextView summary; 
    } 

    public HeaderAdapter(Context context, List<Header> objects) { 

     super(context, 0, objects); 

     mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    } 

    static int getHeaderType(Header header) { 

     if (header.fragment == null && header.intent == null) return HEADER_TYPE_CATEGORY; 
     else return HEADER_TYPE_NORMAL; 
    } 

    @Override 
    public int getItemViewType(int position) { 
     Header header = getItem(position); 
     return getHeaderType(header); 
    } 

    @Override 
    public boolean areAllItemsEnabled() { return false; /* because of categories */ } 

    @Override 
    public boolean isEnabled(int position) { return getItemViewType(position) != HEADER_TYPE_CATEGORY; } 

    @Override 
    public int getViewTypeCount() { return HEADER_TYPE_COUNT; } 

    @Override 
    public boolean hasStableIds() { return true; } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 

     HeaderViewHolder holder; 
     Header header = getItem(position); 
     int headerType = getHeaderType(header); 
     View view = null; 

     if (convertView == null) { 

      holder = new HeaderViewHolder(); 

      switch (headerType) { 

       case HEADER_TYPE_CATEGORY: 

        view = new TextView(getContext(), null, android.R.attr.listSeparatorTextViewStyle); 
        holder.title = (TextView) view; 
        break; 

       case HEADER_TYPE_NORMAL: 

        view = mInflater.inflate(R.layout.preference_header_item, parent, false); 
        holder.icon = (ImageView) view.findViewById(R.id.icon); 
        holder.title = (TextView) view.findViewById(R.id.title); 
        holder.summary = (TextView) view.findViewById(R.id.summary); 
        break; 
      } 

      view.setTag(holder); 
     } 
     else { 

      view = convertView; 
      holder = (HeaderViewHolder) view.getTag(); 
     } 

     // All view fields must be updated every time, because the view may be recycled 
     switch (headerType) { 

      case HEADER_TYPE_CATEGORY : 

       holder.title.setText(header.getTitle(getContext().getResources())); 
       break; 

      case HEADER_TYPE_NORMAL : 

       holder.icon.setImageResource(header.iconRes); 

       holder.title.setText(header.getTitle(getContext().getResources())); 
       CharSequence summary = header.getSummary(getContext().getResources()); 

       if (!TextUtils.isEmpty(summary)) { 

        holder.summary.setVisibility(View.VISIBLE); 
        holder.summary.setText(summary); 
       } 
       else { 
        holder.summary.setVisibility(View.GONE); 
       } 
       break; 
     } 

     return view; 
    } 
} 

risposta

1

Forse prima intestazione è predefinita del menu selezionato. Se è così, dovrebbe avere attributo di frammento per mostrarlo sul lato destro.

+0

Scusate il ritardo, ho rinunciato a questo problema per un po '. Hai ragione, non ha funzionato perché la prima intestazione era considerata come un frammento. Ma per sistemarlo, non ho aggiunto un frammento alla prima intestazione, vedere la mia risposta qui sotto. –

6

Come bestofbest1 ha detto, il problema è stato che Android ha cercato di mostrare il primo elemento in preferences_headers.xml, che non conteneva un frammento.

Per risolvere il problema, ho aggiunto in onCreate di MainPreferenceActivity la riga seguente (PRIMA super.onCreate) per selezionare un frammento di default quando si utilizza un tablet:

if(onIsMultiPane()) getIntent().putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, PreferencesFragment.class.getName()); 

Ho anche impostato un frammento di default in PreferencesFragment:

String settings = "DIVE"; 
if(getArguments() != null) settings = getArguments().getString("settings"); 

Quindi un ultimo problema, PreferenceActivity.EXTRA_SHOW_FRAGMENT non seleziona l'intestazione nella parte sinistra. Per risolvere il problema in MainPreferencesActivity salvare un riferimento a intestazioni (in onBuildHeaders), e aggiungere:

@Override 
protected void onResume() { 

    // Call super : 
    super.onResume(); 

    // Select the displayed fragment in the headers (when using a tablet) : 
    // This should be done by Android, it is a bug fix 
    if(_headers != null) { 

     final String displayedFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT); 
     if (displayedFragment != null) { 
      for (final Header header : _headers) { 
       if (displayedFragment.equals(header.fragment)) { 
        switchToHeader(header); 
        break; 
       } 
      } 
     } 
    } 
} 
2

avevo problemi di ottenere la soluzione di Tim a lavorare per me (il programma sarebbe ancora in crash). Ho lavorato in questo modo in un modo diverso selezionando semplicemente la prima intestazione non di categoria per impostazione predefinita anziché la prima nell'elenco.Per fare questo ho overrided il metodo onGetInitialHeader nel mio PreferenceActivity

@Override 
public Header onGetInitialHeader() { 
    for (int i = 0; i < mHeaders.size(); i++) { 
     Header h = mHeaders.get(i); 
     if (!isCategory(h)) { 
      return h; 
     } 
    } 
} 

protected static boolean isCategory(Header h) { 
    return h.fragment == null; 
} 

mHeaders è solo un riferimento all'elenco intestazione salvato nella chiamata a onBuildHeaders. Va anche notato che questo è solo un problema prima 4.3, da allora è stato risolto. Spero che questo aiuti qualcuno fuori

1

Come una soluzione più semplice della soluzione di Tim Autin, disabilitare completamente il multi-riquadro per produrre uno schermo simile a un singolo riquadro su tablet.

public class PreferencesActivity extends PreferenceActivity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     if(onIsMultiPane()) 
      getIntent().putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true); 
     super.onCreate(savedInstanceState); 
    } 
... 
} 
+0

Il mio frammento di preferenze ha funzionato in Android 4 e 5, ma era bloccato in modalità a riquadro singolo quando si utilizza Android 6. Questa era l'unica soluzione che obbligava tutto a multi-pannello. Grazie!!! – Logic1

Problemi correlati