Agregar fragmento en el elemento de vista de lista

7 minutos de lectura

Quiero tener un fragmento para cada elemento en una vista de lista, porque quiero separar algo de lógica. Estoy usando un titular de vista para cada elemento. Si la vista no existe, creo un nuevo fragmento y lo agrego al contenedor.

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

También para cada elemento, llamo a holder.mMyFragment.setUi(dataSource, position) para configurar la interfaz de usuario del fragmento en función de la fuente de datos y la posición.

El problema que tengo es que inicializo los elementos de la interfaz de usuario del fragmento en el método onCreateView de la clase de fragmento, pero no se llama cuando agrego el fragmento al elemento. Entonces, más tarde, cuando llamo a setUi (), que usa algunos elementos de la interfaz de usuario en el fragmento, se queja de una NullPointerException. alguien tiene una sugerencia? ¡Gracias!

  • Es tarde, pero @Brabbeldas me pregunta cómo lo obtuve, así que actualicé mi respuesta. Lo estoy usando en una aplicación privada.

    – Miguel HincapieC

    19 mayo 2015 a las 20:15

HAY UNA SOLUCION” para esto.

El problema es que tu no se puede agregar fragmento directamente al contenedor(FrameLayout) con el mismo “id” en la vista de lista como lo hizo en el código anterior.

los truco es, crear una vista de lista (Recyclerview) de “LinearLayout”. Luego dinamicamente crear FrameLayout en el adaptador y asignar identificaciones diferentes para cada. Inflar fragmento a FrameLayout y este FrameLayout a 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);
    }
}

  • su solución está funcionando, pero a veces hay una excepción No se encontró ninguna vista para id 0x2 para fragmento

    – Adeel Turco

    5 de diciembre de 2017 a las 8:22


  • en mi caso, la lista de datos se actualiza y no hay un tiempo constante, ya que refleja los cambios del servidor, como elementos que pueden agregarse o eliminarse, por lo que tengo que actualizar el adaptador en consecuencia.

    – Adeel Turco

    5 de diciembre de 2017 a las 8:25

  • ¿Qué hay dentro de la función FragmentHelper.popBackStackAndReplace()?

    – Anuro Barman

    7 de marzo de 2018 a las 11:33

avatar de usuario
gunar

Quiero tener un fragmento para cada elemento en una vista de lista, porque quiero separar algo de lógica.

No puede usar fragmentos como vistas de elementos de lista porque la API no le permite: View y Fragment ni siquiera están relacionados, por lo que no hay forma de que puedas usarlo así. Haz vistas personalizadas y usa el adaptador getViewTypeCount y getView para usar un comportamiento de elemento de lista diferente.

Los fragmentos son administrados por el FragmentManager de Activity o por otro FragmentManager hijo de Fragments; mientras que las vistas de elementos de lista son administradas por ListView y ListAdapter. Puede usar ListViews en Fragmentos, pero no al revés.

  • No. PODEMOS UTILIZAR Fragmentos como elemento de lista . Encuentre mi respuesta a continuación y vote 🙂

    – Srikanth P.

    12 mayo 2016 a las 10:24


  • La API permite agregar fragmentos en una vista. Entonces, si listView tiene una lista de Vistas, también puede tener una lista de Fragmentos.

    – Ioane Sharvadze

    28 de agosto de 2018 a las 8:34

Una forma sencilla. Un problema: debe almacenar agregar estado de fragmento de restauración.

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

avatar de usuario
MiguelHincapiéC

Hola, estaba enfrentando el mismo problema y encontré la manera de hacerlo.
Mi problema era similar al tuyo:

“Quiero tener un fragmento para cada elemento en una vista de lista, porque quiero separar algo de lógica”

En mi aplicación, tengo que dar la opción de mostrar elementos personalizados en modo vertical (listView) y horizontal (ViewPager). Además, tuve que lidiar con 18 elementos personalizados y cada uno con una lógica diferente, por lo que el mejor enfoque fue reutilizar los fragmentos que estaba usando para ViewPager en ListView.

Lo obtuve, pero no de la forma en que lo intentabas, es decir, usé mis fragmentos como “ViewHolders”:

  1. Defina el widget del fragmento como variables de clase en cada fragmento.
  2. Cree un ArrayAdapter personalizado y anule: getViewTypeCount(), getItemViewType(int position), getCount(), getItem(int position) getView(int position, View convertView, ViewGroup parent)

En getView Verifiqué qué tipo de diseño necesitaba antes de “inflar” el XML respectivo, crear un fragmento, asignar widget de XML a fragmento (con rootView.findViewById) y establezca “etiqueta” con el nuevo fragmento.

Lo que puede ver en este punto es que los fragmentos en ListView nunca se adjuntaron a Activity pero obtuvo lo que quería: lógica distribuida en varias partes y todos los beneficios de ListView (reutilización de filas, desplazamiento, etc.).

Si lo necesita, puedo publicar parte de mi código, pero tiene que lidiar con “spanglish”;).

ACTUALIZADO

Todo el problema se debe a que cuando crea un fragmento para usar con ViewPager, generalmente todo el código de “diseño y configuración” está dentro onCreateView método, quiero decir:

  • Obtenga el objeto de vista que va a usar (View rootView = inflater.inflate(R.layout.fragment_question_horizontal_container, container, false);)
  • Obtenga los widgets del diseño anterior, defina comportamientos, fuentes, etc.: (answer = (EditText)rootView.findViewById(R.id.answer_question_text);)

Hasta este punto no hay nada extraño.
Si va a utilizar un fragmento con el comportamiento descrito anteriormente, debe “emular” la llamada a onCreateView, completar los datos y adjuntarlos a listView.

Aquí está el truco: dividir el código en onCreateView en algunos métodos a los que no les importa quién los llama. un ejemplo de mi 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));

    ...
}

Ahora vayamos a CustomArrayAdapter para la vista de lista:

  • Defina su customArrayAdapter algo como esto: PreguntasVerticalArrayAdapter extends ArrayAdapter<Pregunta> donde “Pregunta” es un Fragmento genérico con la lógica de arriba.
  • Anular getViewTypeCount(), getItemViewType(int position), getCount(), getItem(int position) getView(int position, View convertView, ViewGroup parent).

los getView siga el mismo comportamiento: obtenga el objeto para la posición dada en params, reutilice un “visor” y complete los datos. Aquí mi 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;
}

Eso es todo… en este punto, si tiene suficiente experiencia en el manejo de CustomArrayAdapters y Fragments, ¡probablemente lo haya entendido! 😀

De la documentación de Android: “A Fragmento representa un comportamiento o una parte de la interfaz de usuario en una actividad. Puede combinar varios fragmentos en una sola actividad para crear una interfaz de usuario de varios paneles y reutilizar un fragmento en varias actividades”.

Para su actividad, ¿quiere agregar 2 fragmentos donde el primero muestre una vista de lista (= Fragmento de lista y el otro está a la derecha y se muestra solo cuando el usuario hace clic en un elemento (desde el primer fragmento o vista de lista)?

avatar de usuario
kim hyun

En lugar de usar ListFragment, puede usar RecyclerView, Android tiene documentación sobre eso:

ingrese la descripción de la imagen aquí

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad