Restarting Activity creates duplicate fragments

冷暖自知 提交于 2020-01-06 05:48:07

问题


When my activity restarts i get duplicate fragments on top of each other, the new ones over the old ones, and only the most recent are editable

My fragment looks like this, please ignore all unnecessary code, the application at this point is already quite large and it is difficult to separate one thing from another:

package cu.me.cuanto;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class FragmentCambio extends Fragment {
    View view;
    EditText inputCUC,inputCUP;
    TextView exchangeExplain,eraseText,exchangeText;
    ImageView rotatingImage; //esta es la imagen que gira en el fragment
    ImageView backShadow;
    Animation rotate180,fabOpen,fabClose,rotateForward,rotateBackward,shadowIn,shadowOut;
    FloatingActionButton exchangeFab,optionsFab,eraseFab;
    int exchange=25;
    float cuc,cup;
    boolean isOptionsOpen=false;


    public FragmentCambio(){

    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view=inflater.inflate(R.layout.fragment_cambio,container,false);

        backShadow = view.findViewById(R.id.back_shadow_2);
        backShadow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animateFab();
            }
        });

        inputCUC=view.findViewById(R.id.input_cuc);
        inputCUP=view.findViewById(R.id.input_cup);
        rotatingImage=view.findViewById(R.id.rotating_image);
        exchangeExplain=view.findViewById(R.id.exchange_explain_into_cambio);
        eraseText=view.findViewById(R.id.erase_text_exchange_frag);
        exchangeText=view.findViewById(R.id.exchange_text_exchange_frag);
        exchangeFab=view.findViewById(R.id.exchangeFab);
        optionsFab=view.findViewById(R.id.optionsFab);
        eraseFab=view.findViewById(R.id.eraseFab);

        rotate180= AnimationUtils.loadAnimation(getContext(),R.anim.rotate_180);
        fabOpen=AnimationUtils.loadAnimation(getContext(),R.anim.fab_open);
        fabClose=AnimationUtils.loadAnimation(getContext(),R.anim.fab_close);
        rotateForward=AnimationUtils.loadAnimation(getContext(),R.anim.rotate_forward);
        rotateBackward=AnimationUtils.loadAnimation(getContext(),R.anim.rotate_backward);
        shadowIn=AnimationUtils.loadAnimation(getContext(),R.anim.shadow_in);
        shadowOut=AnimationUtils.loadAnimation(getContext(),R.anim.shadow_out);

        inputCUP.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                rotatingImage.startAnimation(rotate180); //animación de el icono del centro indicando conversión en sentido contrario
            }
        });
        inputCUC.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if(!inputCUC.getText().toString().equals("")){
                    cuc=Float.valueOf(inputCUC.getText().toString());
                    cup=cuc*exchange;
                    inputCUP.setText(String.format("%.2f",cuc*exchange));
                }
            }
        });

        optionsFab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                animateFab();
            }
        });
        eraseFab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                inputCUP.setText("");
                inputCUC.setText("");
                cuc=0;
                cup=0;
                animateFab();
            }
        });
        exchangeFab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                switch (exchange){
                    case 25:
                        exchange=24;
                        exchangeFab.setImageResource(R.drawable.custom24);
                        exchangeExplain.setText(String.format("Utilizando un cambio de %.0f:1",(float) exchange));
                        break;
                    case 24:
                        exchange=23;
                        exchangeFab.setImageResource(R.drawable.custom23);
                        exchangeExplain.setText(String.format("Utilizando un cambio de %.0f:1",(float) exchange));
                        break;
                    case 23:
                        exchange=25;
                        exchangeFab.setImageResource(R.drawable.custom25);
                        exchangeExplain.setText(String.format("Utilizando un cambio de %.0f:1",(float) exchange));
                        break;
                    default:
                        break;
                }
                //to be sure that CUC view is focused
                if(inputCUC.hasFocus()){
                    inputCUP.setText(String.format("%.2f",cuc*exchange));
                }
                else if(inputCUP.hasFocus()){
                    Toast.makeText(getContext(),R.string.exchange_change_not_possible,Toast.LENGTH_SHORT).show();
                    animateFab();
                }
                if(inputCUC.getText().toString().equals("")){
                    inputCUP.setText("");
                }

            }
        });

        inputCUP.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if(inputCUP.isFocused()){
                    if(!s.toString().equals("")){
                        cup=Float.valueOf(s.toString());
                        cuc=cup/25;
                        inputCUC.setText(String.format("%.2f",cup/25));
                    }
                    else
                        inputCUC.setText("");
                }
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
        inputCUC.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if(inputCUC.isFocused()){
                    if(!s.toString().equals("")){
                        cuc=Float.valueOf(s.toString());
                        cup=cuc*exchange;
                        inputCUP.setText(String.format("%.2f",cuc*exchange));
                    }
                    else
                        inputCUP.setText("");
                }
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
        rotatingImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(inputCUC.isFocused()){
                    inputCUP.requestFocus();
                }
                else if(inputCUP.isFocused()){
                    inputCUC.requestFocus();
                }
            }
        });



        return view;
    }

    void animateFab(){
        if(!isOptionsOpen){
            optionsFab.startAnimation(rotateForward);
            eraseFab.startAnimation(fabOpen);
            eraseFab.setClickable(true);
            eraseText.setVisibility(View.VISIBLE);
            exchangeFab.startAnimation(fabOpen);
            exchangeFab.setClickable(true);
            exchangeText.setVisibility(View.VISIBLE);
            backShadow.startAnimation(shadowIn);
            backShadow.setVisibility(View.VISIBLE);
            backShadow.setClickable(true);
            isOptionsOpen=!isOptionsOpen;
        }
        else {
            optionsFab.startAnimation(rotateBackward);
            eraseFab.startAnimation(fabClose);
            eraseFab.setClickable(false);
            eraseText.setVisibility(View.INVISIBLE);
            exchangeFab.startAnimation(fabClose);
            exchangeFab.setClickable(false);
            exchangeText.setVisibility(View.INVISIBLE);
            backShadow.startAnimation(shadowOut);
            backShadow.setVisibility(View.INVISIBLE);
            backShadow.setClickable(false);
            isOptionsOpen=!isOptionsOpen;
        }
    }
}

I believe the problem is that when activity restarts the onCreateView method is called again so a new view is created without erase the old one. The problem is annoying because the new views are created when the screen rotates, i've solved the problem forcing portrait, but this is not really a solution, at least not an elegant one.

In Activity:

package cu.me.cuanto;

import android.Manifest;
import android.app.Dialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.TextView;

public class ActivityMain extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, FragmentHome.FragmentHomeListener {

    //Variable Definitions
    FragmentTransaction mFragmentTransaction,mDelayedFragmentTransaction;
    Animation slideIn,slideOut,fullSlideIn;
    FrameLayout homeFragmentContainer,conteoFragmentContainer,exchangeFragmentContainer,debtsFragmentContainer;

    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mToggle;
    NavigationView mNavigationView;

    //Other elements like fragments and some other stuff
    FragmentHome home = new FragmentHome();
    FragmentConteo conteo = new FragmentConteo();
    FragmentCambio cambio = new FragmentCambio();
    FragmentRecargas deudas =  new FragmentRecargas();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //Return app to default theme for avoid keep using the one set in manifest
        setTheme(R.style.AppTheme);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if(!allPermissionsGranted()){
            final Dialog grantPermissions = new Dialog(ActivityMain.this);
            grantPermissions.setContentView(R.layout.dialog_grant_permissions);
            grantPermissions.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

            TextView grantPermissionsButton = grantPermissions.findViewById(R.id.main_grant_permissions);
            grantPermissionsButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    grantPhoneRelatedPermissions();
                    if(allPermissionsGranted()){
                        grantPermissions.dismiss();
                    }
                }
            });
            grantPermissions.show();
        }


        //para el intercambio de fragments
        slideIn= AnimationUtils.loadAnimation(getApplicationContext(),R.anim.slide_in);
        slideOut= AnimationUtils.loadAnimation(getApplicationContext(),R.anim.slide_out);
        fullSlideIn = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.full_slide_in);
        homeFragmentContainer=findViewById(R.id.home_fragment_container);
        conteoFragmentContainer=findViewById(R.id.conteo_fragment_container);
        exchangeFragmentContainer=findViewById(R.id.exchange_fragment_container);
        debtsFragmentContainer=findViewById(R.id.debts_fragment_container);

        //para el navigation drawer y la hamburquesa
        mDrawerLayout = findViewById(R.id.drawer_layout_id);
        mToggle = new ActionBarDrawerToggle(this,mDrawerLayout,R.string.open,R.string.close);
        mDrawerLayout.addDrawerListener(mToggle);
        mToggle.syncState();


        //Get no shadow under appBar
        getSupportActionBar().setElevation(0);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);


        mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
        mNavigationView.setNavigationItemSelectedListener(this);

        //Declare FragmentTransactions and set their views
        mFragmentTransaction = getSupportFragmentManager().beginTransaction();
        mDelayedFragmentTransaction = getSupportFragmentManager().beginTransaction();
        mFragmentTransaction.add(R.id.home_fragment_container,home,"Home Fragment");
        mFragmentTransaction.commit();
        //TODO: make this other shit after first shit
        mDelayedFragmentTransaction.add(R.id.conteo_fragment_container,conteo,"Conteo Fragment");
        mDelayedFragmentTransaction.add(R.id.exchange_fragment_container,cambio,"Exchange Fragment");
        mDelayedFragmentTransaction.add(R.id.debts_fragment_container,deudas,"Debts Fragment");
        mDelayedFragmentTransaction.commit();
        //Select First item on the list
        mNavigationView.setCheckedItem(R.id.home_fragment_selector);

    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(mToggle.onOptionsItemSelected(item)){
            return true;
        }
        return super.onOptionsItemSelected(item);
    }


    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(final MenuItem item) {

        final int id = item.getItemId();

        Runnable closeDrawerLayoutRunnable = new Runnable() {
            @Override
            public void run() {
                mDrawerLayout.closeDrawer(GravityCompat.START);
            }

        };
        Handler mHandler = new Handler();

        switch (id){
            case R.id.home_fragment_selector:
                if(homeFragmentContainer.getVisibility()!=View.VISIBLE){
                    homeFragmentContainer.startAnimation(fullSlideIn);
                    homeFragmentContainer.setVisibility(View.VISIBLE);
                    conteoFragmentContainer.setVisibility(View.INVISIBLE);
                    exchangeFragmentContainer.setVisibility(View.INVISIBLE);
                    debtsFragmentContainer.setVisibility(View.INVISIBLE);
                }
                break;
            case R.id.conteo_fragment_selector:
                if(conteoFragmentContainer.getVisibility()!=View.VISIBLE){
                    conteoFragmentContainer.startAnimation(fullSlideIn);
                    conteoFragmentContainer.setVisibility(View.VISIBLE);
                    homeFragmentContainer.setVisibility(View.INVISIBLE);
                    exchangeFragmentContainer.setVisibility(View.INVISIBLE);
                    debtsFragmentContainer.setVisibility(View.INVISIBLE);
                    conteo.isOptionsOpen=false;
                }
                break;
            case R.id.exchange_fragment_selector:
                if(exchangeFragmentContainer.getVisibility()!=View.VISIBLE) {
                    exchangeFragmentContainer.startAnimation(fullSlideIn);
                    exchangeFragmentContainer.setVisibility(View.VISIBLE);
                    homeFragmentContainer.setVisibility(View.INVISIBLE);
                    conteoFragmentContainer.setVisibility(View.INVISIBLE);
                    debtsFragmentContainer.setVisibility(View.INVISIBLE);
                    cambio.isOptionsOpen = false;
                }
                break;
            case R.id.debts_fragment_selector:
                if(debtsFragmentContainer.getVisibility()!=View.VISIBLE){
                    debtsFragmentContainer.startAnimation(fullSlideIn);
                    debtsFragmentContainer.setVisibility(View.VISIBLE);
                    homeFragmentContainer.setVisibility(View.INVISIBLE);
                    conteoFragmentContainer.setVisibility(View.INVISIBLE);
                    exchangeFragmentContainer.setVisibility(View.INVISIBLE);
                }
                break;
            case R.id.navigation_settings:
                Intent settings = new Intent(getApplicationContext(), ActivitySettings.class);
                startActivity(settings);
                break;
            case R.id.about_cuanto:
                Intent about = new Intent(getApplicationContext(), ActivityAbout.class);
                startActivity(about);
                break;
            default:
                break;
        }
        mHandler.post(closeDrawerLayoutRunnable);
        return true;
    }


    @Override
    public void onBackPressed() {

//        check if there are some FAB menu opened
        if(cambio.isOptionsOpen){
            cambio.animateFab();
            cambio.isOptionsOpen=false;
        }
        else if(conteo.isOptionsOpen){
            conteo.animateFab();
            conteo.isOptionsOpen=false;
        }

//        this sequence works fine just because first is checked if there are FABs open, then the selected fragment
        else if(homeFragmentContainer.getVisibility()!=View.VISIBLE){
            homeFragmentContainer.setVisibility(View.VISIBLE);
            mNavigationView.setCheckedItem(R.id.home_fragment_selector);

            if(conteoFragmentContainer.getVisibility()==View.VISIBLE){
                conteoFragmentContainer.startAnimation(slideOut);
                conteoFragmentContainer.setVisibility(View.INVISIBLE);
            }
            else if(exchangeFragmentContainer.getVisibility()==View.VISIBLE){
                exchangeFragmentContainer.startAnimation(slideOut);
                exchangeFragmentContainer.setVisibility(View.INVISIBLE);
            }
            else if(debtsFragmentContainer.getVisibility()==View.VISIBLE){
                debtsFragmentContainer.startAnimation(slideOut);
                debtsFragmentContainer.setVisibility(View.INVISIBLE);
            }
        }
        else{
            super.onBackPressed();
        }
     }

    //Custom interfaces implementation
    @Override
    public void onButtonListener(View view) {
        if(view.getId()==R.id.conteo_card){
            mNavigationView.setCheckedItem(R.id.conteo_fragment_selector);
            conteoFragmentContainer.startAnimation(fullSlideIn);
            conteoFragmentContainer.setVisibility(View.VISIBLE);
            Handler mHandler = new Handler();
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    homeFragmentContainer.setVisibility(View.INVISIBLE);
                    exchangeFragmentContainer.setVisibility(View.INVISIBLE);
                }
            },300);
            conteo.isOptionsOpen=false;
        }
        else if(view.getId()==R.id.cambio_card){
            mNavigationView.setCheckedItem(R.id.exchange_fragment_selector);
            exchangeFragmentContainer.startAnimation(fullSlideIn);
            exchangeFragmentContainer.setVisibility(View.VISIBLE);
            Handler mHandler = new Handler();
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    homeFragmentContainer.setVisibility(View.INVISIBLE);
                    conteoFragmentContainer.setVisibility(View.INVISIBLE);
                }
            },300);
            cambio.isOptionsOpen=false;
        }
        else if(view.getId()==R.id.debts_card){
            mNavigationView.setCheckedItem(R.id.debts_fragment_selector);
            debtsFragmentContainer.startAnimation(fullSlideIn);
            debtsFragmentContainer.setVisibility(View.VISIBLE);
            Handler mHandler = new Handler();
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    homeFragmentContainer.setVisibility(View.INVISIBLE);
                    conteoFragmentContainer.setVisibility(View.INVISIBLE);
                }
            },300);
        }
    }

    //My own Functions
    private void grantPhoneRelatedPermissions(){
        if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(getApplicationContext(),Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(ActivityMain.this,new String[]{Manifest.permission.CALL_PHONE,Manifest.permission.READ_PHONE_STATE},1);
            return;
        }
    }

    private boolean allPermissionsGranted(){
        if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(getApplicationContext(),Manifest.permission.READ_PHONE_STATE)== PackageManager.PERMISSION_GRANTED){
            return true;
        }
        else
            return false;
    }

}

回答1:


FragmentManager automatically restores any previously added Fragments when your Activity is recreated (i.e., when savedInstanceState != null). Therefore you'll want to ensure that you only add your initial Fragment once - when the savedInstanceState is null.

if (savedInstanceState == null) {
    // Create new instances if the savedInstanceState == null
    home = new FragmentHome();
    conteo = new FragmentConteo();
    cambio = new FragmentCambio();
    deudas =  new FragmentRecargas();
    //Declare FragmentTransactions and set their views
    mFragmentTransaction = getSupportFragmentManager().beginTransaction();
    mDelayedFragmentTransaction = getSupportFragmentManager().beginTransaction();
    mFragmentTransaction.add(R.id.home_fragment_container,home,"Home Fragment");
    mFragmentTransaction.commit();
    //TODO: make this other shit after first shit
    mDelayedFragmentTransaction.add(R.id.conteo_fragment_container,conteo,"Conteo Fragment");
    mDelayedFragmentTransaction.add(R.id.exchange_fragment_container,cambio,"Exchange Fragment");
    mDelayedFragmentTransaction.add(R.id.debts_fragment_container,deudas,"Debts Fragment");
    mDelayedFragmentTransaction.commit();
} else {
    // Get the already existing Fragments out of FragmentManager
    home = (FragmentHome) getSupportFragmentManager().findFragmentById(R.id.home_fragment_container);
    conteo = (FragmentConteo) getSupportFragmentManager().findFragmentById(R.id.conteo_fragment_container);
    cambio = (FragmentCambio) getSupportFragmentManager().findFragmentById(R.id.exchange_fragment_container);
    deudas = (FragmentRecargas) getSupportFragmentManager().findFragmentById(R.id.debts_fragment_container);
}

You'll note that when the saved instance state is not null, we get the already existing fragment instances from FragmentManager rather than creating a new instance.



来源:https://stackoverflow.com/questions/58458065/restarting-activity-creates-duplicate-fragments

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!