Android - 使用PreferenceFragment的PreferenceActivity中的标头类别

时间:2013-03-21 15:28:09

标签: android header fragment categories preferenceactivity

我想在Android设置应用中显示偏好设置屏幕:使用标题,PreferenceActivity,PreferenceFragment和标题类别。

我想把这个结果放在平板电脑上:

enter image description here

这是智能手机上的一个:

enter image description here

如果我只使用基本标题,它会起作用,但是如果我尝试添加类别,它会在智能手机上运行,​​并在平板电脑上崩溃,在那里我得到异常“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

贝娄是涉及的代码片段。我主要是从Android设置应用来源获得它们。

有什么想法吗?

提前致谢


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>

最后但并非最不重要的是,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 个答案:

答案 0 :(得分:6)

正如bestofbest1所说,问题是Android试图在preferences_headers.xml中显示第一个元素,该元素不包含片段。

为了解决这个问题,我在MainPreferenceActivity的onCreate中添加了以下一行(BEFORE super.onCreate),以便在使用平板电脑时选择默认片段:

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

我还在PreferencesFragment中设置了一个默认片段:

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

然后是最后一个问题,PreferenceActivity.EXTRA_SHOW_FRAGMENT没有选择左侧的标题。要在MainPreferencesActivity中修复它,请保存对标题的引用(在onBuildHeaders中),并添加:

@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;
                }
            }
        }
    }
}

答案 1 :(得分:2)

我遇到了让蒂姆的解决方案为我工作的问题(程序仍会崩溃)。我通过默认选择第一个非类别标题而不是列表中的第一个来以不同的方式解决这个问题。为此,我在onGetInitialHeader

中覆盖了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只是对onBuildHeaders调用中保存的标题列表的引用。还应该指出的是,这只是4.3之前的一个问题,因此已经修复。希望这可以帮助别人

答案 2 :(得分:1)

也许第一个标题是默认选择的菜单。如果是这样,它应该有fragment属性来显示它的右侧。

答案 3 :(得分:1)

作为Tim Autin解决方案的一种简单形式,完全禁用多窗格以在平板电脑上生成单窗格,类似手机的显示。

Sleep