【问题标题】:How can style the ListView of a ListFragment如何设置 ListFragment 的 ListView 的样式
【发布时间】:2012-07-30 20:34:19
【问题描述】:

我想保留这些方法:

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 源编写我自己的代码,并将进度小部件添加到布局中以显示相同的效果。到目前为止,我将按照我被告知的那样删除这些行。如果我做修改,我会在这里粘贴。

【问题讨论】:

  • 它只是一个带有列表视图的线性布局,将样式属性传递给它们。

标签: android listview android-listfragment


【解决方案1】:

只需将原来的ListView 替换为onCreateView() 中的CustomListView 布局即可。为我工作。

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

【讨论】:

    【解决方案2】:

    我已经尝试过这个解决方案并且它有效:

    support ListFragment源代码中说,

    如果您使用自己的自定义内容覆盖此方法, 考虑在您的布局中包含标准布局 android.R.layout 文件,以便您继续保留所有的标准行为 列表片段。特别是,这是目前唯一的方法 显示内置的不确定进度状态。

    所以,我采用了 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;
    }
    

    【讨论】:

    • 我在 onActivityCreated 中的 setListAdapter() 处收到空指针异常。任何指针
    • @codingcrow 很遗憾听到这个消息,但我已经在我的应用程序中修改了所有这些,我没有收到任何错误,也许还有其他错误......
    • 您需要创建新代码吗?与自定义和标准布局合并的膨胀是否足够?我问是因为我将您的答案与this one 进行比较。
    【解决方案3】:

    另一个对我也有用的解决方案就是把这条线

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

    【讨论】:

      【解决方案4】:

      setListShown(true);setListShownNoAnimation(true); 不受支持包支持,仅在 Api Level 11+ 中可用。您要么必须放弃对

      如果我对documentation 的理解正确,则无论如何都不必设置它们,因为true 是默认值

      【讨论】:

      • 好吧,我必须找到其他方法,因为我需要那些用于装载机的方法。我已经使用该信息编辑了问题。
      • 我完全误解了错误信息。支持这些方法,但不支持自定义内容视图。也许看看source code 有帮助
      • 是的,我会这样做,也许我会获取部分源代码。谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-02
      • 2014-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多