问题
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