Catégories des en - têtes Android dans L'activité de préférence avec fragment de préférence
j'aimerais afficher un écran de préférences comme celui de L'application Android settings : utiliser les en-têtes, PreferenceActivity, PreferenceFragment et headers catégories.
Je ne veux pas ce résultat sur une tablette:
et celui - ci sur un smartphone:
cela fonctionne si j'utilise juste les en-têtes de base, mais si j'essaie de ajouter des catégories, il fonctionne sur le smartphone, et se planter sur la tablette, où je reçois l'exception "java.lang.NullPointerException: nom == 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.access0(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
ci-dessous sont les morceaux de code. Je les ai obtenus principalement à partir de L'application Android settings app source.
une idée ?
Merci d'avance
activité principale de référence:
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>
Last mais pas moins, 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;
}
}
4 réponses
peut-être que le premier en-tête est le menu sélectionné par défaut. Si oui, il devrait avoir fragment d'attribut pour montrer côté droit.
comme bestofbest1 dit, le problème était que Android a essayé de montrer le premier élément dans le preferences_headers.xml, qui ne contenait pas de fragment.
pour le corriger, j'ai ajouté dans Mainpreference Activit's onCreate la ligne ci-dessous (avant super.onCreate) pour sélectionner un fragment par défaut lors de l'utilisation d'une tablette:
if(onIsMultiPane()) getIntent().putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, PreferencesFragment.class.getName());
j'ai aussi défini un fragment par défaut dans PreferencesFragment:
String settings = "DIVE";
if(getArguments() != null) settings = getArguments().getString("settings");
Puis un dernier problème, PreferenceActivity.EXTRA_SHOW_FRAGMENT ne sélectionne pas l'en-tête du côté gauche. Pour le corriger dans MainPreferencesActivity enregistrer une référence à vos en-têtes( dans onbuild headers), et ajouter:
@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;
}
}
}
}
}
j'ai eu des problèmes pour obtenir la solution de Tim de travailler pour moi (le programme serait toujours crash). J'ai travaillé sur cette question d'une manière différente en sélectionnant simplement le premier en-tête de non-catégorie par défaut au lieu du premier dans la liste. Pour ce faire, j'ai remplacé la méthode onGetInitialHeader
dans mon 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
n'est qu'une référence à la liste d'en-tête enregistrée dans l'appel à onBuildHeaders
. Il convient également de noter qu'il ne s'agit que d'un problème avant 4.3, il a depuis été corrigé. Espérons que cela aide quelqu'un
comme une forme plus simple de la solution de Tim Autin, désactiver multi-pane tout à fait pour produire une seule vitre, le téléphone-comme l'affichage sur les tablettes.
public class PreferencesActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if(onIsMultiPane())
getIntent().putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true);
super.onCreate(savedInstanceState);
}
...
}