将片段添加到listview项目中

时间:2013-09-05 20:27:08

标签: android android-listview android-fragments

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

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。有没有人有建议?谢谢!

6 个答案:

答案 0 :(得分:8)

  

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

您不能将片段用作列表项视图,因为API不允许您 - ViewFragment甚至没有关联,所以这里有#s}你无法像那样使用它。制作自定义视图并使用适配器getViewTypeCountgetView来使用不同的列表项行为。

片段由Activity的FragmentManager或其他Fragments子FragmentManager管理;而列表项视图由ListView&管理。 ListAdapter。您可以在片段中使用ListView,但不能反过来使用。

答案 1 :(得分:8)

为此,

有一个解决方案”。

问题是,您 无法将片段直接添加到容器中 (FrameLayout),并且在listview中具有与上述代码中相同的“id”。

技巧是,创建“LinearLayout”的listview(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);
    }
}

答案 2 :(得分:3)

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

holder.mMyFragment = new MyFragment(mActivity, this);
int id = View.generateViewId();
findViewByTag("abc").setId(id);
mActivity.getSupportFragmentManager().beginTransaction().add(id, holder.mMyFragment).commit();

答案 3 :(得分:1)

您好,我遇到了同样的问题,我找到了办法 我的问题与你类似:

  

“我希望列表视图中的每个项目都有一个片段,因为我想将一些逻辑分开”

在我的应用程序中,我必须提供以垂直(listView)和水平(ViewPager)模式显示自定义项目的选项。另外,我必须处理18个自定义项目,每个项目具有不同的逻辑,因此最好的方法是重复使用我在ListView中用于ViewPager的片段。

我得到了它,但不是你想要的方式,我的意思是,我使用了像“ViewHolders”这样的片段:

  1. 将片段的小部件定义为每个片段中类的变量。
  2. 创建自定义ArrayAdapter并覆盖:getViewTypeCount(), getItemViewType(int position), getCount(), getItem(int position) getView(int position, View convertView, ViewGroup parent)
  3. getView中,我在“膨胀”各自的XML之前检查了我需要的布局类型,创建一个片段,将小部件从XML分配到片段(使用rootView.findViewById)并设置“标签”新片段。

    你在这一点上可以看到的是ListView中的片段从未附加到Activity但你得到了你想要的东西:逻辑分布在几个部分和ListView的所有好处(重用行,滚动等)。
    /> 如果你需要我可以发布我的部分代码,但你必须处理“spanglish”;)。



    <强>已更新

    所有问题都是因为当您创建要与ViewPager一起使用的片段时,通常所有“布局和”设置“代码都在onCreateView方法内,我的意思是:

    • 获取您要使用的视图对象(View rootView = inflater.inflate(R.layout.fragment_question_horizontal_container, container, false);
    • 从上面的布局中获取小部件,定义行为,字体等:(answer = (EditText)rootView.findViewById(R.id.answer_question_text);

    直到这一点,没有什么奇怪的 如果要使用具有上述行为的片段,则必须“模拟”对onCreateView的调用,填充数据并将其附加到listView。

    以下是诀窍:在某些方法中将onCreateView中的代码拆分,而不关心谁在调用它们。我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));
    
        ...
    }
    


    现在让我们转到CustomArrayAdapter获取列表视图:

    • 将customArrayAdapter定义为:PreguntasVerticalArrayAdapter extends ArrayAdapter<Pregunta>其中“Pregunta”是具有上述逻辑的通用片段。
    • 覆盖getViewTypeCount(), getItemViewType(int position), getCount(), getItem(int position) getView(int position, View convertView, ViewGroup parent)

    getView遵循相同的行为:获取params中给定位置的对象,重用“viewholder”并填充数据。我的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;
    }
    



    这就是......在这一点上,如果你有足够的经验处理CustomArrayAdapters和Fragments,你可能会得到它! :d

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

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

enter image description here

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