Andoid - Como evitar los Fragment superpuestos

Iniciado por MaX2, 22 Abril 2019, 20:00 PM

0 Miembros y 1 Visitante están viendo este tema.

MaX2

Hola, tengo creado un menú horizontal con pestañas desde las que llamo a la clase que extienden de Fragment, donde tengo puesto unos botones para abrir una segunda clase Fragment.

El problema es que cunado llamo a esta segundo Fragment el primer Fragment se siguen mostrando debajo.

Lo de poner un fondo no es efectivo, porque si pulsamos sobre una parte de la pantalla que contiene debajo un botón, éste sigue activo y mostraría su contenido.

Alguien me puede decir como evitar que se superpongan los Fragment ?

Este es el codigo que tengo puesto en el Fragment y su xml

Menu1.java extends Fragment


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

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

       bt_apuntes = rootView.findViewById(R.id.btn_Apuntes);
       bt_apuntes.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Apuntes_2 fl=new Apuntes_2();
               FragmentTransaction transaction=manager.beginTransaction();
               transaction.replace(R.id.fragmen_menu1,fl);
               transaction.addToBackStack(null);
               transaction.commit();
           }
       });

 return rootView;

}


Menu1.xml  (he quitado mas código que tengo puesto para otros botones para dejar de ejemplo solo uno)



<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
   ....
   ....
   android:clickable="true"
   android:focusable="true"
   tools:context=".Menu1">

   <FrameLayout
       ....
       ....
       android:id="@+id/fragmen_menu1">

           <androidx.constraintlayout.widget.ConstraintLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content">

               <Button
                   android:id="@+id/btn_Apuntes"
                   android:text="Apuntes"
                   ....
                   ....
                   ....
                   ..../>
           </androidx.constraintlayout.widget.ConstraintLayout>

   </FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>



P.D. lo de poner el android:clickable="true" y android:focusable="true" es porque he leido que para que no se superpongan los fragment hay que ponelo, pero a mi no me funciona.


Un saludo.

srWhiteSkull

#1
Tienes que hacerle un pop al contenedor o gestor de Fragments. Hace tiempo qeu no trabajo con Android pero creo que era con ésto :

https://developer.android.com/reference/android/app/FragmentManager.html

Investiga

PD O guarda la referncia del anterior y hazle un onDestroy(), porque por lo que veo lo que te puse está deprecated.

MaX2

Hola srWhiteSkull, gracias por responder tan pronto, la verdad es que no estoy muy puesto y ando un poco perdido, y no se muy bien lo que quiere decir de hacer un pop al contenedor, y la pagina oficial con la traducción que hace google hay veces que....

Creo que se lo que me quieres decir, sustituir el layaut de ese fragment por el segundo, de todas formas en un rato pongo el código en el primer post por si se puede ver mejor lo que estoy haciendo mal.

La verdad es que llevo viendo muchos vídeos, para ver si que codigo utilizandan en los ejemplos y los que he visto siempre llaman a un solo Fragment desde una clase que extiende de AppCompatActivity, lo de llamar a un fragmen desde otro fragmen no lo he visto


Editado
No me interesa destruir los fragment anteriores, porque necesito volver atrás.

Un saludo.

srWhiteSkull

No, me refiero hacer un pop, un pop es sacar el Fragment. Supuestamente los Fragment o las vistas se van apilando y existe un gestor que te indiqué en el enlace y que en la documentación recomiendan no usarlo por obsolescencia. Este gestor de fragmentos tiene funciones para sacar el último Fragment, y combinándolo con otro objeto que ya ni me acuerdo, recuerdo que podías hacer transiciones entre Fragments. La verdad es que no estoy muy puesto al día pero si recuerdo el problema que comentas.

En teoría según la doc debes evitar usar el sistema comentado y atenerte al ciclo de vida del Fragment por lo que sugiero que pruebes usar el método onDestroy() con el Fragment que se queda atrás, y para ello debes conservar de alguna manera su "referencia".

Quizás si no entiendes los términos descritos aquí lo que necesitas es más práctica y experiencia con la programación del Api de Android. Si no quieres destrozar el proyecto que tienes con experimentos te aconsejo que crees nuevos proyectos de prueba y cuando entiendas todo eso lo apliques al proyecto que tienes entre mano.

Suerte

srWhiteSkull

#4
Siento el doble post, pero viendo el código que has puesto creo que vas encaminado, prueba usar el método remove(FragmentoAnterior) y luego aplicar el reaplce(nuevoFragmento) o el add(NuevoFragmento).

PD Voy a echar un vistazo a algo que tenga por ahí.

srWhiteSkull

#5
En el activity :
Código (java) [Seleccionar]

   public void cambiaFrame(String nombre, Fragment fragmento){
       esPrincipal=false;
       FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
       if (getSupportFragmentManager().getBackStackEntryCount()>=1){ // Comprobamos la pila, y si existen más Fragments dentro entonces...
           if (getSupportFragmentManager().getFragments().get(1).getClass()!=fragmento.getClass()) {
               getSupportFragmentManager().popBackStack(); // Sacamos el de atrás de la pila (creo que era para evitar que cuando pulsaras el botón BACK volvieras a la vista anterior)
               ft.replace(R.id.fragment, fragmento); // y reeplazamos
               ft.addToBackStack(nombre); // Le ponemos un nombre a ese Fragment
           }
       } else {
           ft.add(R.id.fragment, fragmento); // En caso de no existir ninguno simplemente añadimos
           ft.addToBackStack(nombre);
       }
       getSupportActionBar().setDisplayHomeAsUpEnabled(true);
       getSupportActionBar().setDisplayShowHomeEnabled(true);
       findViewById(R.id.boton_sms).setVisibility(View.INVISIBLE);
       ft.commit();
   }


PD También podrías hacerlo invisible o destruirlo, al de atrás, prueba eso también si este código no te sirve.

Resto del código :
https://github.com/RufusWein/proyecto-fin-curso/blob/master/Cliente%20Android%20con%20Pasarela%20SMS/StudentManager/app/src/main/java/tk/srwhiteskull/studentmanager/ActividadPrincipal.java

MaX2

#6
He cambiado la imagen, porque he corregido los fallos que puse antes, pero todavía me siguen dando errores







MaX2

He cambiado la imagen porque parece que en el servidor donde estaba no se podia ver, a no ser que se entre con un proxy, asi que la he cambiado de sitio.

Como ves en esa imagen hay algunos errores que no se como corregirlos, otros como el "boton_sms" yo utilizo botones independientes.





srWhiteSkull

Los elementos que comienzan por R indican que son recursos estáticos. Eso significa que ya están creados y definidos en el proyecto. En el caso que te puse de ejemplo, no era para que hicieras un copy/paste, algo que deberías entender si vas a dedicarte a la programación.

Lo siguiente que te voy a mostrar, es un ejemplo más sencillo, pero no es para que hagas copy/paste, es para que asimiles el funcionamiento del proceso de transacción o reemplazo de fragmentos evitando el solapamiento de éstos al retroceder por medio del botón BACK del sistema.

Para que asimiles esto estudia el código y prueba comentar la línea de la función cambiaFrame() del Activity, getSupportFragmentManager().popBackStack(), que es la encargada de sacar el fragmento que se encuentra apilado y que aunque la transacción lo reemplaza en verdad permanece vivo en la aplicación. Verás también, en la consola de depuración, como el resultado de Fragmentos Apilados se incrementa según invoques los fragmentos al pulsar los botones Fragmento A y Fragmento B cuando dicha línea esté comentada pero cuando la línea no está comentada el apilamiento será siempre 1.

Normalmente cuando pulsas la tecla BACK del sistema en la actividad tenderá a volver hacia atrás, si no hay nada en la pila volverá al escritorio pero si existen fragmentos vivos en la pila irá haciendo pop liberando en cada pulsación cada fragmento. Esto lo verás al comentar la susodicha línea. Fíjate en la consola de depuración.

Recursos

Código (xml) [Seleccionar]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F44336"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_weight=".15"
        android:background="#00BCD4"
        android:orientation="horizontal"
        android:layout_height="0dp">

        <Button
            android:id="@+id/llamaFragmentoA"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="Fragmento A" />

        <Button
            android:id="@+id/llamaFragmentoB"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="Fragmento B" />
    </LinearLayout>

    <FrameLayout
        android:id="@+id/contenedor"
        android:layout_width="match_parent"
        android:layout_weight=".85"
        android:layout_height="0dp">

    </FrameLayout>
</LinearLayout>


Código (xml) [Seleccionar]
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_weight=".50"
            android:gravity="center"
            android:text="Fragmento A"
            android:layout_height="0dp" />

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight=".50"
            android:background="#4CAF50">

            <EditText
                android:id="@+id/editText"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:ems="10"
                android:gravity="center"
                android:inputType="textPersonName"
                android:text="A"
                android:textSize="200dp" />
        </FrameLayout>

    </LinearLayout>
</FrameLayout>


Código (xml) [Seleccionar]
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight=".50"
            android:background="#FF9800">

            <EditText
                android:id="@+id/editText"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:ems="10"
                android:gravity="center"
                android:inputType="textPersonName"
                android:text="B"
                android:textSize="200dp" />
        </FrameLayout>

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_weight=".50"
            android:gravity="center"
            android:text="Fragmento B"
            android:layout_height="0dp" />

    </LinearLayout>
</FrameLayout>


Codigo

Hay que añadir a cada clase el package con el nombre correspondiente que le hayas dado al proyecto.

Código (java) [Seleccionar]
import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.llamaFragmentoA).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Evento","Frgamento A");
                cambiaFrame("A",new FragmentoA());
            }
        });

        findViewById(R.id.llamaFragmentoB).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("Evento","Frgamento B");
                cambiaFrame("B",new FragmentoB());
            }
        });
    }

    public void cambiaFrame(String nombre, Fragment fragmento){
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        int numFragmentos = getSupportFragmentManager().getBackStackEntryCount();
        Log.d("Frame","Fragmentos Apilados = "+numFragmentos);
        if (numFragmentos>=1){ // Comprobamos la pila, y si existen más Fragments dentro entonces...
            // Comenta esta linea y compara el resultado sin comentar esta linea, cual es la conclusión?
            getSupportFragmentManager().popBackStack(); // Sacamos el de atrás de la pila (creo que era para evitar que cuando pulsaras el botón BACK volvieras a la vista anterior)

            ft.replace(R.id.contenedor, fragmento); // y reeplazamos
            ft.addToBackStack(nombre); // Le ponemos un nombre a ese Fragment

        } else {
            ft.add(R.id.contenedor, fragmento); // En caso de no existir ninguno simplemente añadimos
            ft.addToBackStack(nombre);
        }
        ft.commit();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        int numFragmentos = getSupportFragmentManager().getBackStackEntryCount();
        Log.d("Frame","Haciendo Pop, fragmentos apilados = "+numFragmentos);

    }
}


Código (java) [Seleccionar]
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**
* A simple {@link Fragment} subclass.
*/
public class FragmentoA extends Fragment {


    public FragmentoA() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragmento_a, container, false);
    }

}


Código (java) [Seleccionar]
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**
* A simple {@link Fragment} subclass.
*/
public class FragmentoB extends Fragment {


    public FragmentoB() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragmento_b, container, false);
    }

}


Este sistema permite solucionar el problema que tienes pero como indica la doc en Apis iguales o superiores a la 28 se debe prescindir del FragmentManager, aconsejando tener en cuenta el ciclo de vida.

MaX2

Esta tarde me llevare el portátil al curro y lo probare, pero por lo que veo es más o menos lo que yo tengo con un código parecido, y eso si me está funcionando, puedo mostrar desde los botones del AppCompatActivity los Fragment.


El problema viene en el siguiente paso, que en el código que pones no está, que sería poner unos botones en uno de los Fragment y abrir otro Fragment


Ahí es donde tengo el problema para mostrar ese segundo Fragment



Mira a ver si con el video se muestra algo más claro,

[youtube=640,360]https://youtu.be/tdGuSrzwNBQ[/youtube]