2013-03-21 32 views
6

我想要顯示Android設置應用中的首選項屏幕:使用標頭,PreferenceActivity,PreferenceFragment和標頭類別。Android - 帶PreferenceFragment的PreferenceActivity中的標頭類別

我wan't在平板電腦這樣的結果:

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

回答

1

也許第一個報頭是默認選擇的菜單。如果是這樣,它應該有片段屬性來顯示它的右側。

+0

對不起,我遲到了,我放棄了這個問題一會兒。你是對的,但它並不能工作,因爲第一頭被認爲是一個片段。 但要解決這個問題,我沒有一個片段添加到第一個報頭,SE我的回答如下。 – 2013-05-28 13:42:39

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

我得到了Tim的解決方案,爲我工作的問題(該方案將仍然崩潰)。我以不同的方式解決了這個問題,只需選擇默認的第一個非類別標題而不是列表中的第一個。要做到這一點,我overrided方法onGetInitialHeaderPreferenceActivity

@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,它已被固定。希望這可以幫助別人了

1

由於蒂姆Autin的解決方案更簡單的形式,完全禁用多窗格生產在平板電腦上的單面板,類似電話的顯示屏。

public class PreferencesActivity extends PreferenceActivity { 

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

我的偏好片段的Android 4和5的工作,但使用的是Android 6.當這迫使一切恢復多窗格唯一的解決方案被卡在單窗口模式。謝謝!!! – Logic1 2018-02-04 00:37:36