11

我想为列表视图中的每个项目创建一个片段,因为我想将一些逻辑分开。我为每个项目使用了一个视图支架。如果视图不存在,我创建一个新片段并将其添加到容器中。

holder.mMyFragment = new MyFragment(mActivity, this);
mActivity.getSupportFragmentManager().beginTransaction().add(R.id.my_container, holder.mMyFragment).commit();

同样对于每个项目,我调用 holder.mMyFragment.setUi(dataSource, position) 以根据数据源和位置设置片段的 UI。

我遇到的问题是我在片段类的 onCreateView 方法中初始化了片段的 UI 元素,但是当我将片段添加到项目时它没有被调用。所以后来当我调用 setUi() 时,它在片段中使用了一些 UI 元素,它会抱怨 NullPointerException。有人有建议吗?谢谢!

4

6 回答 6

13

有一个解决方案”。

问题是,您不能像在上面的代码中那样在列表视图中使用相同的“id”将片段直接添加到容器(FrameLayout)中。

诀窍是,创建“LinearLayout”的列表视图(Recyclerview)。然后在适配器中动态创建 FrameLayout 并为每个分配不同的 id。将 Fragment 膨胀到 FrameLayout 并将此 FrameLayout 膨胀到 LinearLayout。

@Override
protected void onBindBasicItemView(RecyclerView.ViewHolder holder, int position) {
    if (holder instanceof VideoHomeViewHolder) {
        VideoHomeViewHolder videoHomeViewHolder = (VideoHomeViewHolder) holder;
        FrameLayout frameLayout = new FrameLayout(mContext);
        frameLayout.setId(position+1); //since id cannot be zero
        FragmentHelper.popBackStackAndReplace(mFragmentManager, frameLayout.getId(),
                new ShowLatestVideosFragment(mShowLatestVideosItems.get(position)));
        videoHomeViewHolder.linearLayout.addView(frameLayout);
    }
}
于 2016-05-12T10:19:40.890 回答
12

我想为列表视图中的每个项目创建一个片段,因为我想将一些逻辑分开。

您不能将片段用作列表项视图,因为 API 不允许您使用 -View甚至Fragment不相关,因此您无法像那样使用它。制作自定义视图并使用适配器getViewTypeCountgetView使用不同的列表项行为。

Fragment 由 Activity 的 FragmentManager 或其他 Fragments 的子 FragmentManager 管理;而列表项视图由 ListView 和 ListAdapter 管理。您可以在 Fragments 中使用 ListViews,但反之则不行。

于 2013-09-05T20:32:48.700 回答
3

一个简单的方法。一个问题:您应该存储添加恢复片段状态。

holder.mMyFragment = new MyFragment(mActivity, this);
int id = View.generateViewId();
findViewByTag("abc").setId(id);
mActivity.getSupportFragmentManager().beginTransaction().add(id, holder.mMyFragment).commit();
于 2016-03-29T09:48:05.330 回答
2

Hi I was facing the same problem and I found the way to do it.
My problem was similar to you:

"I want to have a fragment for each item in a listview, because I want to separate some logic out"

In my app I have to give the option to display custom items in vertical (listView) and horizontal (ViewPager) mode. Additionally I had to deal with 18 custom items and each one with different logic, so the best approach was reusing the fragments that I was using for ViewPager in ListView.

I got it but not in the way you were trying, I mean, I used my fragments like "ViewHolders":

  1. Define fragment's widget like variables of class in each fragment.
  2. Create a custom ArrayAdapter and override: getViewTypeCount(), getItemViewType(int position), getCount(), getItem(int position) getView(int position, View convertView, ViewGroup parent)

In getView I checked what kind of layout I needed before "inflate" the respective XML, create a fragment, assign widget from XML to fragment (with rootView.findViewById) and set "tag" with the new fragment.

What you can see at this point is that fragments in ListView never got attached to Activity but you got what you wanted: logic distributed in several parts and all benefits of ListView (reuse of rows, scroll, etc).

If you need I can post part of my code but you have to deal with "spanglish" ;).



UPDATED

All the problem is because when you create a Fragment to be used with ViewPager, usually all "layout and "setup" code is inside onCreateView method, I mean:

  • Get the view object you are going to use (View rootView = inflater.inflate(R.layout.fragment_question_horizontal_container, container, false);)
  • Get the widgets from above layout, define behaviors, fonts, etc: (answer = (EditText)rootView.findViewById(R.id.answer_question_text);)

Until this point there is nothing weird.
If you are going to use a fragment with the behavior described above you have to "emulate" the call to onCreateView, fill the data and attach it to the listView.

Here is the trick: split the code in onCreateView in some methods that doesn't care about who's calling them. An example of my onCreateView:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.fragment_pregunta_horizontal_container, container, false);

    addAnswerLayout(rootView, R.layout.fragment_pregunta_texto, getActivity());
    setUpComponents(rootView);
    //those 2 methods are about my app logic so I'm not going to talk much about them
    setUpQuestionState(savedInstanceState);
    readSavedAnswer();

    return rootView;
}

public void addAnswerLayout(View rootView, int optionId, Context context) {

    mContext = context;

    RelativeLayout relativeLayout = (RelativeLayout)rootView.findViewById(R.id.pregunta_container);

    LayoutInflater inflater = ((Activity)mContext).getLayoutInflater();
    View newView = inflater.inflate(optionId, relativeLayout, false);

    relativeLayout.addView(newView);
}

public void setUpComponents(View rootView) {
    //next line: some heritage that you can ignore
    super.setUpComponents(rootView);

    respuesta = (EditText)rootView.findViewById(R.id.pregunta_respuesta_texto);
    respuesta.setTypeface(UiHelper.getInstance(getActivity()).typeface);
    respuesta.setTextColor(mContext.getResources().getColor(R.color.drs_gris));

    ...
}


Now let's go to the CustomArrayAdapter for list view:

  • Define your customArrayAdapter something like this: PreguntasVerticalArrayAdapter extends ArrayAdapter<Pregunta> where "Pregunta" is a generic Fragment with the logic from above.
  • Override getViewTypeCount(), getItemViewType(int position), getCount(), getItem(int position) getView(int position, View convertView, ViewGroup parent).

The getView follow the same behavior: get the object for the given position in params, reuse a "viewholder" and fill the data. Here my getView:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View rowView = convertView;

    Pregunta pregunta = mData.get(position);

    if (rowView == null)
        rowView = createQuestionUI(pregunta, parent, position);

    fillDataInRow((PreguntaUI)rowView.getTag(), pregunta, position);

    return rowView;
}

private View createPreguntaUI(Pregunta pregunta, ViewGroup parent, int position) {

    View rootView = null;

    LayoutInflater inflater = (mPreguntasVerticalFragment.getActivity()).getLayoutInflater();

    //you can ignore this part of the code ralted to Bundle.
    Bundle args = new Bundle();
    args.putLong(PreguntaUI.PREGUNTAUI_ID, pregunta.getIdpregunta());
    args.putInt(PreguntaUI.PREGUNTAUI_INDEX, position);
    args.putInt(PreguntaUI.PREGUNTAUI_TOTAL_QUESTIONS, getCount());

    //internal method of "pregunta" to know what kind of question it is.
    String tipo = pregunta.getTipo();

    if (tipo.equalsIgnoreCase(PreguntaType.TEXT.toString())) {

        rootView = inflater.inflate(R.layout.fragment_pregunta_vertical_container, parent, false);

        Pregunta_texto pregunta_texto = new Pregunta_texto();
        pregunta_texto.setArguments(args);

        //LOOK AT THIS POINT!!!: I'm calling the same methods that I called in onCreateView fragment's method.
        pregunta_texto.addAnswerLayout(rootView, R.layout.fragment_pregunta_texto,
                mPreguntasVerticalFragment.getActivity());
        pregunta_texto.setUpComponents(rootView);
        pregunta_texto.setUpQuestionState(null);
        pregunta_texto.readSavedAnswer();

        //I'm adding the fragment to reuse it when I can
        rootView.setTag(pregunta_texto);
    }
    else if ...

    return rootView;
}



That is all... at this point, if you have enough experience dealing with CustomArrayAdapters and Fragments you probably got it! :D

于 2014-09-14T00:44:50.223 回答
-1

来自 Android 文档:“片段表示 Activity 中的行为或用户界面的一部分。您可以在单个 Activity 中组合多个片段以构建多窗格 UI 并在多个 Activity 中重用片段。”

对于您的活动,您是否要添加 2 个片段,其中第一个显示 listView(= ListFragment,另一个在右侧,仅在用户单击项目时显示(来自第一个片段或 listView)?

于 2013-09-05T20:42:26.573 回答
-3

您可以使用 RecyclerView,而不是使用 ListFragment,Android 对此有文档:

在此处输入图像描述

https://developer.android.com/training/basics/fragments/creating.html#AddInLayout

于 2019-08-21T19:24:32.503 回答