2012-07-30 37 views
5

我想保持這些方法的ListFragment的ListView控件:如何樣式

setListShown(true); 
setListShownNoAnimation(true); 

,但如果我用

onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 

膨脹的Fragment使用自定義風格的佈局,以前的方法可以不會被使用,並且顯示出這個例外:

07-30 20:17:46.937: E/AndroidRuntime(1374): Caused by: java.lang.IllegalStateException: Can't be used with a custom content view 
07-30 20:17:46.937: E/AndroidRuntime(1374):  at android.support.v4.app.ListFragment.setListShown(ListFragment.java:282) 
07-30 20:17:46.937: E/AndroidRuntime(1374):  at android.support.v4.app.ListFragment.setListShown(ListFragment.java:258) 

那麼,有什麼可能的解決方案呢?

編輯:

Fragment使用裝載機的ListView從數據庫填充。所以這就是爲什麼我想保持這些方法的原因,他們需要在這裏:

public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
     mAdapter.swapCursor(data); 
     if (isResumed()) { 
      setListShown(true); 
     } else { 
      setListShownNoAnimation(true); 
     } 
    } 

最簡單的解決方案是基於ListFragment源,以使自己的代碼,也進步小部件添加到佈局以顯示相同的效果。到現在爲止,我會按照我的說法刪除這些行。如果我做了修改,我會在這裏粘貼它。

+0

你充氣什麼XML? – Estel 2012-07-30 22:24:42

+0

這只是一個帶有listview的線性佈局,將style屬性傳遞給它們。 – giorgiline 2012-07-30 23:30:17

回答

17

剛剛與onCreateView()在您CustomListView佈局取代原來的ListView。爲我工作。

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
     Bundle savedInstanceState) { 
    View layout = super.onCreateView(inflater, container, 
      savedInstanceState); 
    ListView lv = (ListView) layout.findViewById(android.R.id.list); 
    ViewGroup parent = (ViewGroup) lv.getParent(); 

    // Remove ListView and add CustomView in its place 
    int lvIndex = parent.indexOfChild(lv); 
    parent.removeViewAt(lvIndex); 
    LinearLayout mLinearLayout = (LinearLayout) inflater.inflate(
      R.layout.custom_list, container, false); 
    parent.addView(mLinearLayout, lvIndex, lv.getLayoutParams()); 
    return layout; 
} 
-1

setListShown(true);setListShownNoAnimation(true);不受支持包的支持,僅適用於Api Level 11+。您必須放棄< = Api級別10(不良選擇)的支持或刪除線路。

如果我理解正確的documentation您不必設置它們反正因爲true是默認值

+0

嗯,我必須找到其他方式,因爲我需要那些裝載機。我用這個信息編輯了這個問題。 – giorgiline 2012-07-30 23:29:01

+0

我完全誤解了錯誤信息。支持這些方法,但不支持自定義內容vie。也許看[源代碼](http://bit.ly/PgVG6d)有幫助 – 2012-07-30 23:53:15

+0

是的,我會這樣做,也許我會採取一些源代碼的一部分。謝謝。 – giorgiline 2012-07-30 23:57:52

8

我試過這個解決方案,它的工作原理:

support ListFragment源代碼中說,

如果你重寫此方法與自己的自定義內容, 考慮在你的佈局 文件的標準佈局android.R.layout,所以塔您繼續保留ListFragment的所有標準行爲 。特別是,這是目前唯一能夠顯示內置不確定進度狀態的方法。

所以,我已經採取了list_content.xml並應用到列表我自己的風格:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     style="@style/page_background"> 

    <LinearLayout android:id="@+id/progressContainer" 
      android:orientation="vertical" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      android:visibility="gone" 
      android:gravity="center"> 

     <ProgressBar style="?android:attr/progressBarStyleLarge" 
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content" /> 
     <TextView android:layout_width="wrap_content" 
       android:layout_height="wrap_content" 
       android:textAppearance="?android:attr/textAppearanceSmall" 
       android:paddingTop="4dip" 
       android:singleLine="true" /> 

    </LinearLayout> 

    <FrameLayout android:id="@+id/listContainer" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent"> 

     <ListView android:id="@android:id/list" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" 
       android:drawSelectorOnTop="false" 
       style="@style/rc_listview"/> 
     <TextView android:id="@android:id/empty" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" 
       android:gravity="center" 
       android:textAppearance="?android:attr/textAppearanceLarge" /> 
    </FrameLayout> 

</FrameLayout> 

然後做了一個固定的ListFragment從支持源代碼複製。

  • 我重命名了一些ID以匹配XML文件中的ID。
  • 刪除其onCreateView()

    public class FixedListFragment extends Fragment { 
    
        final private Handler mHandler = new Handler(); 
    
        final private Runnable mRequestFocus = new Runnable() { 
         public void run() { 
          mList.focusableViewAvailable(mList); 
         } 
        }; 
    
        final private AdapterView.OnItemClickListener mOnClickListener 
          = new AdapterView.OnItemClickListener() { 
         public void onItemClick(AdapterView<?> parent, View v, int position, long id) { 
          onListItemClick((ListView)parent, v, position, id); 
         } 
        }; 
    
        ListAdapter mAdapter; 
        ListView mList; 
        View mEmptyView; 
        TextView mStandardEmptyView; 
        View mProgressContainer; 
        View mListContainer; 
        CharSequence mEmptyText; 
        boolean mListShown; 
    
        public FixedListFragment() { 
        } 
    
        /** 
        * Provide default implementation to return a simple list view. Subclasses 
        * can override to replace with their own layout. If doing so, the 
        * returned view hierarchy <em>must</em> have a ListView whose id 
        * is {@link android.R.id#list android.R.id.list} and can optionally 
        * have a sibling view id {@link android.R.id#empty android.R.id.empty} 
        * that is to be shown when the list is empty. 
        * 
        * <p>If you are overriding this method with your own custom content, 
        * consider including the standard layout {@link android.R.layout#list_content} 
        * in your layout file, so that you continue to retain all of the standard 
        * behavior of ListFragment. In particular, this is currently the only 
        * way to have the built-in indeterminant progress state be shown. 
        */ 
    
        /** 
        * Attach to list view once the view hierarchy has been created. 
        */ 
        @Override 
        public void onViewCreated(View view, Bundle savedInstanceState) { 
         super.onViewCreated(view, savedInstanceState); 
         ensureList(); 
        } 
    
        /** 
        * Detach from list view. 
        */ 
        public void onDestroyView() { 
         mHandler.removeCallbacks(mRequestFocus); 
         mList = null; 
         mListShown = false; 
         mEmptyView = mProgressContainer = mListContainer = null; 
         mStandardEmptyView = null; 
         super.onDestroyView(); 
        } 
    
        /** 
        * This method will be called when an item in the list is selected. 
        * Subclasses should override. Subclasses can call 
        * getListView().getItemAtPosition(position) if they need to access the 
        * data associated with the selected item. 
        * 
        * @param l The ListView where the click happened 
        * @param v The view that was clicked within the ListView 
        * @param position The position of the view in the list 
        * @param id The row id of the item that was clicked 
        */ 
        public void onListItemClick(ListView l, View v, int position, long id) { 
        } 
    
        /** 
        * Provide the cursor for the list view. 
        */ 
        public void setListAdapter(ListAdapter adapter) { 
         boolean hadAdapter = mAdapter != null; 
         mAdapter = adapter; 
         if (mList != null) { 
          mList.setAdapter(adapter); 
          if (!mListShown && !hadAdapter) { 
           // The list was hidden, and previously didn't have an 
           // adapter. It is now time to show it. 
           setListShown(true, getView().getWindowToken() != null); 
          } 
         } 
        } 
    
        /** 
        * Set the currently selected list item to the specified 
        * position with the adapter's data 
        * 
        * @param position 
        */ 
        public void setSelection(int position) { 
         ensureList(); 
         mList.setSelection(position); 
        } 
    
        /** 
        * Get the position of the currently selected list item. 
        */ 
        public int getSelectedItemPosition() { 
         ensureList(); 
         return mList.getSelectedItemPosition(); 
        } 
    
        /** 
        * Get the cursor row ID of the currently selected list item. 
        */ 
        public long getSelectedItemId() { 
         ensureList(); 
         return mList.getSelectedItemId(); 
        } 
    
        /** 
        * Get the activity's list view widget. 
        */ 
        public ListView getListView() { 
         ensureList(); 
         return mList; 
        } 
    
        /** 
        * The default content for a ListFragment has a TextView that can 
        * be shown when the list is empty. If you would like to have it 
        * shown, call this method to supply the text it should use. 
        */ 
        public void setEmptyText(CharSequence text) { 
         ensureList(); 
         if (mStandardEmptyView == null) { 
          throw new IllegalStateException("Can't be used with a custom content view"); 
         } 
         mStandardEmptyView.setText(text); 
         if (mEmptyText == null) { 
          mList.setEmptyView(mStandardEmptyView); 
         } 
         mEmptyText = text; 
        } 
    
        /** 
        * Control whether the list is being displayed. You can make it not 
        * displayed if you are waiting for the initial data to show in it. During 
        * this time an indeterminant progress indicator will be shown instead. 
        * 
        * <p>Applications do not normally need to use this themselves. The default 
        * behavior of ListFragment is to start with the list not being shown, only 
        * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}. 
        * If the list at that point had not been shown, when it does get shown 
        * it will be do without the user ever seeing the hidden state. 
        * 
        * @param shown If true, the list view is shown; if false, the progress 
        * indicator. The initial value is true. 
        */ 
        public void setListShown(boolean shown) { 
         setListShown(shown, true); 
        } 
    
        /** 
        * Like {@link #setListShown(boolean)}, but no animation is used when 
        * transitioning from the previous state. 
        */ 
        public void setListShownNoAnimation(boolean shown) { 
         setListShown(shown, false); 
        } 
    
        /** 
        * Control whether the list is being displayed. You can make it not 
        * displayed if you are waiting for the initial data to show in it. During 
        * this time an indeterminant progress indicator will be shown instead. 
        * 
        * @param shown If true, the list view is shown; if false, the progress 
        * indicator. The initial value is true. 
        * @param animate If true, an animation will be used to transition to the 
        * new state. 
        */ 
        private void setListShown(boolean shown, boolean animate) { 
         ensureList(); 
         if (mProgressContainer == null) { 
          throw new IllegalStateException("Can't be used with a custom content view"); 
         } 
         if (mListShown == shown) { 
          return; 
         } 
         mListShown = shown; 
         if (shown) { 
          if (animate) { 
           mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
             getActivity(), android.R.anim.fade_out)); 
           mListContainer.startAnimation(AnimationUtils.loadAnimation(
             getActivity(), android.R.anim.fade_in)); 
          } else { 
           mProgressContainer.clearAnimation(); 
           mListContainer.clearAnimation(); 
          } 
          mProgressContainer.setVisibility(View.GONE); 
          mListContainer.setVisibility(View.VISIBLE); 
         } else { 
          if (animate) { 
           mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
             getActivity(), android.R.anim.fade_in)); 
           mListContainer.startAnimation(AnimationUtils.loadAnimation(
             getActivity(), android.R.anim.fade_out)); 
          } else { 
           mProgressContainer.clearAnimation(); 
           mListContainer.clearAnimation(); 
          } 
          mProgressContainer.setVisibility(View.VISIBLE); 
          mListContainer.setVisibility(View.GONE); 
         } 
        } 
    
        /** 
        * Get the ListAdapter associated with this activity's ListView. 
        */ 
        public ListAdapter getListAdapter() { 
         return mAdapter; 
        } 
    
        private void ensureList() { 
         if (mList != null) { 
          return; 
         } 
         View root = getView(); 
         if (root == null) { 
          throw new IllegalStateException("Content view not yet created"); 
         } 
         if (root instanceof ListView) { 
          mList = (ListView)root; 
         } else { 
          mStandardEmptyView = (TextView)root.findViewById(android.R.id.empty); 
          if (mStandardEmptyView == null) { 
           mEmptyView = root.findViewById(android.R.id.empty); 
          } else { 
           mStandardEmptyView.setVisibility(View.GONE); 
          } 
          mProgressContainer = root.findViewById(R.id.progressContainer); 
          mListContainer = root.findViewById(R.id.listContainer); 
          View rawListView = root.findViewById(android.R.id.list); 
          if (!(rawListView instanceof ListView)) { 
           if (rawListView == null) { 
            throw new RuntimeException(
              "Your content must have a ListView whose id attribute is " + 
              "'android.R.id.list'"); 
           } 
           throw new RuntimeException(
             "Content has view with id attribute 'android.R.id.list' " 
             + "that is not a ListView class"); 
          } 
          mList = (ListView)rawListView; 
          if (mEmptyView != null) { 
           mList.setEmptyView(mEmptyView); 
          } else if (mEmptyText != null) { 
           mStandardEmptyView.setText(mEmptyText); 
           mList.setEmptyView(mStandardEmptyView); 
          } 
         } 
         mListShown = true; 
         mList.setOnItemClickListener(mOnClickListener); 
         if (mAdapter != null) { 
          ListAdapter adapter = mAdapter; 
          mAdapter = null; 
          setListAdapter(adapter); 
         } else { 
          // We are starting without an adapter, so assume we won't 
          // have our data right away and start with the progress indicator. 
          if (mProgressContainer != null) { 
           setListShown(false, false); 
          } 
         } 
         mHandler.post(mRequestFocus); 
        } 
    } 
    

在那之後,一切都完美。 創建Fragment時,只需使此list_content.xml充氣。

public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    View view = inflater.inflate(R.layout.list_content, container, false); 
    return view; 
} 
+0

我在onActivityCreated中的setListAdapter()上得到一個nullpointerexception。任何指針 – 2013-02-05 22:55:10

+0

@codingcrow對不起,聽到這個,但我已經修改了所有這些在我的應用程序,我沒有得到任何錯誤,也許別的東西... – giorgiline 2013-02-24 12:05:11

+0

你需要創建新的代碼?習慣和標準佈局合併是否充足?我問,因爲我比較你的答案與[這一個](http://stackoverflow.com/questions/10608624/listfragment-does-not-accept-my-layout)。 – 2014-02-08 23:45:35

0

另一種解決方案是爲我工作也只是把此行

<include layout="@android:layout/list_content" /> 

,而不是你的ListView。

您可以在其上構建自己的內容或用其他視圖包圍它。例如,這裏是我的佈局看起來像:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:orientation="vertical" 
android:layout_width="match_parent" 
android:layout_height="match_parent" > 

<RelativeLayout 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:padding="@dimen/default_view_padding" 
     android:gravity="center" 
     android:weightSum="4"> 

    <ImageButton 
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:id="@+id/btnCounterBack" 
      android:src="?attr/ic_previous" 
      android:background="?android:attr/selectableItemBackground" 
      android:contentDescription="@null"/> 

    <TextView 
      android:id="@+id/tvCounterLevel" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:textAppearance="?android:attr/textAppearanceMedium" 
      android:text="@string/counter_categories" 
      android:layout_gravity="center" 
      android:paddingTop="@dimen/default_view_padding" 
      android:paddingBottom="@dimen/default_view_padding" 
      android:layout_alignParentTop="true" 
      android:layout_alignParentLeft="true" 
      android:layout_alignParentStart="true" 
      android:gravity="center"/> 

</RelativeLayout> 

<LinearLayout 
     android:layout_width="match_parent" 
     android:layout_height="0dp" 
     android:layout_weight="1" 
     android:gravity="center_vertical" 
     android:background="?attr/dropShadowBackground" 
     android:orientation="vertical" > 

    <include layout="@android:layout/list_content" /> 

</LinearLayout>