preference-headers标头没有片段?

时间:2014-01-20 15:47:59

标签: android android-preferences

我正在编写一个自定义的Android偏好设置屏幕,当标题没有片段时,它会崩溃b / c它期待一个片段 - 我只想要一个标题,作为给定集合的标题部分。尝试搜索,但无济于事,如果这是一个重复,那就很抱歉。

pref_headers.xml

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
  <header android:id="@+id/headerStaffGeneral" android:title="@string/staff_general" /> <!-- This causes a crash as name == null -->
  <header
      android:id="@+id/headerSettings"
      android:fragment="com.example.ex.prefs.SettingsPreferencesFragment"
      android:title="@string/staff_manager_settings"
      android:icon="@drawable/ic_action_settings"
      />
</preference-headers>

如果需要包含任何其他代码,我可以将其包含在内,但问题的本质是我是否应该将崩溃的HEADER标记替换为其他内容。

更新

LogCat Output

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.ex/com.example.ex.prefs.PrefsActivity}: java.lang.NullPointerException: name == null
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2295)
at android.app.ActivityThread.access$700(ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1280)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:175)
at android.app.ActivityThread.main(ActivityThread.java:5279)
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:1102)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
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:582)
at android.preference.PreferenceActivity.switchToHeaderInner(PreferenceActivity.java:1245)
at android.preference.PreferenceActivity.switchToHeader(PreferenceActivity.java:1278)
at android.preference.PreferenceActivity.onCreate(PreferenceActivity.java:647)
at android.app.Activity.performCreate(Activity.java:5283)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1097)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2209)
... 11 more

PrefsActivity.java

package com.example.ex.prefs;

import java.util.ArrayList;
import java.util.List;

import android.preference.PreferenceActivity;
import android.widget.ListAdapter;

import com.example.ex.R;

public class PrefsActivity extends PreferenceActivity {

  private List<Header> mHeaders;

  protected void onResume() {
    super.onResume();

    setTitle(R.string.staff_manager_settings); 

    if (getListAdapter() instanceof PrefsHeaderAdapter)
      ((PrefsHeaderAdapter) getListAdapter()).resume();
  }

  protected void onPause() {
    super.onPause();
    if (getListAdapter() instanceof PrefsHeaderAdapter)
      ((PrefsHeaderAdapter) getListAdapter()).pause();
  }

  public void onBuildHeaders(List<Header> target) {
    // Called when the settings screen is up for the first time
    // we load the headers from our xml description

    loadHeadersFromResource(R.xml.pref_headers, target);

    mHeaders = target;
  }

  public void setListAdapter(ListAdapter adapter) {
    int i, count;

    if (mHeaders == null) {
      mHeaders = new ArrayList<Header>();
      // When the saved state provides the list of headers,
      // onBuildHeaders is not called
      // so we build it from the adapter given, then use our own adapter

      count = adapter.getCount();
      for (i = 0; i < count; ++i)
        mHeaders.add((Header) adapter.getItem(i));
    }

    super.setListAdapter(new PrefsHeaderAdapter(this, mHeaders));
  }

}

如果从HEADER文件中删除了第一个pref_headers.xml标记,则不会显示错误。

2 个答案:

答案 0 :(得分:1)

你应该有所作为,但也许你需要一个android:id值。如果您查看“设置”应用的首选项标题,则会看到以下内容:

<preference-headers
        xmlns:android="http://schemas.android.com/apk/res/android">


    <!-- WIRELESS and NETWORKS -->
    <header android:id="@+id/wireless_section"
        android:title="@string/header_category_wireless_networks" />

    <!-- Wifi -->
    <header
        android:id="@+id/wifi_settings"
        android:fragment="com.android.settings.wifi.WifiSettings"
        android:title="@string/wifi_settings_title"
        android:icon="@drawable/ic_settings_wireless" />

...

因此,他们似乎在标题列表中的部分标题中使用了<header>而没有android:fragment

答案 1 :(得分:1)

首选项标头的XML是正确的,不需要android:id属性(但永远不会受伤)。我需要添加两件事:setListAdapteronGetInitialHeader

以下是所有3个文件的最终代码:

prefs_headers.xml

<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
  <header android:title="@string/staff_general"/>
  <header
    android:id="@+id/headerSettings"
    android:fragment="com.example.ex.prefs.SettingsPreferencesFragment"
    android:icon="@drawable/ic_action_settings"
    android:title="@string/staff_manager_settings"/>
  <header
    android:id="@+id/headerHistory"
    android:fragment="com.example.ex.prefs.HistoryPreferencesFragment"
    android:icon="@drawable/ic_action_event"
    android:title="@string/staff_manager_history"/>
  <header android:title="@string/staff_special" />
  <header
    android:id="@+id/headerNetwork"
    android:fragment="com.example.ex.prefs.NetworkPreferencesFragment"
    android:icon="@drawable/ic_action_cloud"
    android:title="@string/staff_manager_network"/>
  <header
    android:id="@+id/headerPrinter"
    android:fragment="com.example.ex.prefs.PrinterPreferencesFragment"
    android:icon="@drawable/ic_action_star"
    android:title="@string/staff_manager_star_printer"/>
</preference-headers>

PrefsActivity.java

package com.example.ex.prefs;

import java.util.List;

import android.preference.PreferenceActivity;
import android.preference.PreferenceActivity.Header;
import android.util.Log;
import android.widget.ListAdapter;

import com.example.ex.R;

public class PrefsActivity extends PreferenceActivity {
  private static List<Header> _headers;

  @Override
  public void setListAdapter(ListAdapter adapter) {
      if (adapter == null) {
          super.setListAdapter(null);
      } else {
          super.setListAdapter(new PrefsHeaderAdapter(this, _headers));
      }
  }

  @Override
  public void onBuildHeaders(List<Header> target) {
    _headers = target;
    loadHeadersFromResource(R.xml.pref_headers, target);
  }
  @Override
  public Header onGetInitialHeader() {
    super.onResume();
    if (PrefsActivity._headers != null) {
      for (int i = 0; i < PrefsActivity._headers.size(); i++) {
        Header h = PrefsActivity._headers.get(i);
        if (PrefsHeaderAdapter.getHeaderType(h) != PrefsHeaderAdapter.HEADER_TYPE_CATEGORY) {
            return h;
        }
      }
    }
    return null;
  }
}

PrefsHeaderAdapter.java     package com.example.ex.prefs;

import java.util.List;

import android.content.Context;
import android.preference.PreferenceActivity.Header;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.ex.R;

public class PrefsHeaderAdapter extends ArrayAdapter<Header> {

  static final int           HEADER_TYPE_CATEGORY = 0;
  static final int           HEADER_TYPE_NORMAL   = 1;

  private LayoutInflater     mInflater;

  public PrefsHeaderAdapter(Context context, List<Header> objects) {
    super(context, 0, objects);
    mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  }

  public View getView(int position, View convertView, ViewGroup parent) {
    Header header = getItem(position);
    int headerType = getHeaderType(header);
    View view = null;

    switch (headerType) {
      case HEADER_TYPE_CATEGORY:
        view = mInflater.inflate(android.R.layout.preference_category, parent, false);
        ((TextView) view.findViewById(android.R.id.title)).setText(header.getTitle(getContext().getResources()));
        break;
      case HEADER_TYPE_NORMAL:
        view = mInflater.inflate(R.layout.preference_header_item, parent, false);
        ((ImageView) view.findViewById(android.R.id.icon)).setImageResource(header.iconRes);
        ((TextView) view.findViewById(android.R.id.title)).setText(header.getTitle(getContext().getResources()));
        ((TextView) view.findViewById(android.R.id.summary)).setText(header.getSummary(getContext().getResources()));
        break;
    }

    return view;
  }

  @Override
  public boolean isEnabled(int position) {
    return getItemViewType(position) != HEADER_TYPE_CATEGORY;
  }
  @Override
  public boolean areAllItemsEnabled() {
    return false;
  }
  @Override
  public int getViewTypeCount() { 
    return 2; 
  }
  @Override
  public boolean hasStableIds() { 
    return true; 
  }

  public static int getHeaderType(Header header) {
    if ((header.fragment == null) && (header.intent == null)) {
      return HEADER_TYPE_CATEGORY;
    } else {
      return HEADER_TYPE_NORMAL;
    }
  }
}

关键是重写onGetInitialHeader以确保它不是第一个标题b / c根据Android - Headers categories in PreferenceActivity with PreferenceFragment的信息,Android默认加载第一个是我的类别标题并假设它是一个片段但是,既然不是,它就崩溃了。此ListAdapter用于管理列表的特征并确定标题类型 - 这是可靠地确定标题类型所必需的,无需对答案进行硬编码。

我希望这可以帮助别人,因为我已经花了好几天了!