Android: fragment restore after screen orientation change - android

I'm starting with fragments and I'm facing a problem. I wan't to restore my fragment after a screen rotation.
My app looks like this: on landscape I have a button on the left area, which updates the a label on the right area. If on portrait mode, I'm navigating to a new activity. However, I wan't to maintain the fragment state after rotating.
Code looks like this:
Left area fragment:
public class ListFragment extends Fragment implements OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
Button button = (Button) view.findViewById(R.id.button1);
button.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
updateDetail();
break;
}
}
public void updateDetail() {
String newTime = String.valueOf(System.currentTimeMillis());
DetailFragment fragment = (DetailFragment) getFragmentManager()
.findFragmentById(R.id.detailFragment);
if (fragment != null && fragment.isInLayout()) {
fragment.setText(newTime);
} else {
Intent intent = new Intent(getActivity().getApplicationContext(),
DetailActivity.class);
intent.putExtra("value", newTime);
startActivity(intent);
}
}
}
Right area activity:
public class DetailActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
finish();
return;
}
if (savedInstanceState == null) {
setContentView(R.layout.activity_detail);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String s = extras.getString("value");
TextView view = (TextView) findViewById(R.id.detailsText);
view.setText(s);
}
}
}
}
Right area fragment:
public class DetailFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater
.inflate(R.layout.fragment_detail, container, false);
return view;
}
public void setText(String item) {
TextView view = (TextView) getView().findViewById(R.id.detailsText);
view.setText(item);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getFragmentManager();
DetailFragment fragment = (DetailFragment)fm.findFragmentById(R.id.detailFragment);
if (fragment == null) {
fragment = new DetailFragment();
fragment.setTargetFragment(this, 0);
fm.beginTransaction().add(R.id.detailFragment, fragment).commit();
}
}
}
What can I possibly be doing wrong?

You have a few options you can use the Bundle in a couple of methods to save and restore the state of the fragments.
Or you can possibly use the setRetainInstance method:
onCreate(Bundle save)
{
super.onCreate(save);
setRetainInstance(true);
}
Keep in mind that the setRetainInstance method doesn't work for fragments in the backstack.

#Override
public void onConfigurationChanged(Configuration newConfig) {
int orientation = getResources().getConfiguration().orientation;
switch (orientation) {
case Configuration.ORIENTATION_LANDSCAPE:
getSupportFragmentManager().beginTransaction().replace(R.id.detailFragment, new DetailFragment()).commitAllowingStateLoss();
getSupportFragmentManager().beginTransaction().remove(new DetailFragment()).commitAllowingStateLoss();
break;
case Configuration.ORIENTATION_PORTRAIT:
getSupportFragmentManager().beginTransaction().replace(R.id.detailFragment, new DetailFragment()).commitAllowingStateLoss();
getSupportFragmentManager().beginTransaction().remove(new DetailFragment()).commitAllowingStateLoss();
break;
}
super.onConfigurationChanged(newConfig);
}
You can check on the onConfigurationChange and ensure that your fragment state is not lost by doing a commitAllowingStateLoss

Related

Fragment Buttons don't work after orientation changes

The buttons don't work when I change into landscape mode and back to portrait. Anyways, here is my code:
public class fragmentone extends Fragment {
Button biological;
Button natural;
View myView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
myView = inflater.inflate(R.layout.firstlay, container, false);
biological = (Button) myView.findViewById(R.id.biological);
biological.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BiologicalHazards fragment = new BiologicalHazards();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction().addToBackStack("hi")
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.replace(R.id.container1, fragment)
.commit();
}
});
natural = (Button) myView.findViewById(R.id.natural);
natural.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NaturalHazards fragment = new NaturalHazards();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction().addToBackStack("hi")
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.replace(R.id.container1, fragment)
.commit();
}
});
return myView;
}
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
getActivity().setContentView(R.layout.firstlay);
}else{
getActivity().setContentView(R.layout.firstlay);
}
}
}
Does anyone know how to solve this? Any help is appreciated and thanks in advance.
you need to add setRetainInstance(true); //to your fragment onCreateView()
Also needn't be doing this getActivity().setContentView(R.layout.firstlay); in your onConfigurationChanged() method.
Hope this help :)
I think You to have re initilize when change orientation
try your modified code
private LayoutInflater inflater;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myView = inflater.inflate(R.layout.firstlay, container, false);
reInitilize(myView);
return myView;
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
myView = inflater.inflate(R.layout.firstlay, null);
reInitilize(myView);
}else{
myView = inflater.inflate(R.layout.firstlay, null);
reInitilize(myView);
}
}
private void reInitilize(View myView) {
// TODO Auto-generated method stub
biological = (Button) myView.findViewById(R.id.biological);
biological.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BiologicalHazards fragment = new BiologicalHazards();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction().addToBackStack("hi")
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.replace(R.id.container1, fragment)
.commit();
}
});
natural = (Button) myView.findViewById(R.id.natural);
natural.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NaturalHazards fragment = new NaturalHazards();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction().addToBackStack("hi")
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.replace(R.id.container1, fragment)
.commit();
}
});
}
You don't need to do handle orientation your self just to change your layout. Create 2 files with this same name R.layout.fistlay. One in res/layout, one in res/layout-land. Android will automatically pick the right one base on the orientation.
you could implement the method
#Override
protected void onSaveInstanceState(Bundle bundle){
// store some variable like boolean, ints, String to the bundle
bundle.putBoolean('currentFragment', isInFragmentA);
// call the default method
super.onSaveInstanceState(bundle);
}
After this, you could check if you are on a configuration change as following:
void onCreate(Bundle bundle){
if (bundle == null){
//start the activity as the use is opening the app as normal
} else {
// bundle is not null
//get back the variable you have store and act accordingly
// e.g do some FragmentTransition
}
}
Hope this help

Android Fragment initialization to various Custom Fragments, but only One at a time

I am trying to initialize a generic fragment in such manner that it can be used globally no matter what the instance of it is. For example:
public MainActivity extends Activity{
private Fragment fragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first_layout);
FragmentManager fragManager = getFragmentManager();
FragmentTransaction transaction = fragManager.beginTransaction();
if(condition1){
fragment = new MyFirstFragment();
transaction
.add(R.id.registrationFragment, fragment); //registrationFragment is FrameLayout
transaction.commit();
}else if(condition2){
fragment = new MySecondFragment();
transaction
.add(R.id.registrationFragment, fragment); //registrationFragment is FrameLayout
transaction.commit();
}
//create intent filter
//create MyBroadcast object
//registerReceiver(receiverObject, intentFilter)
}
//Some action happens
class MyBroadcast extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(action1)) {
fragment.performAction();
}
}
}
public class MyFirstFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(
R.layout.fragment_holder_a, container, false);
//setWidgets etc...
// TODO Auto-generated method stub
return view;
}
//Used from main
public void performAction(String text){
//set some TextView's text from MainActivity event
//different from MySecondFragment performAction method
}
}
public class MySecondFragmentFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(
R.layout.fragment_holder_b, container, false);
//setWidgets etc...
// TODO Auto-generated method stub
return view;
}
//Used from main
public void performAction(String text){
//set some TextView's text from MainActivity event
//different from MyFirstFragment performAction method
}
}
The problem is that in my real application I may have more than 2 Fragments that the Fragment object could become an instance of. I am trying to avoid a lot of if statements to check which fragment instance was inflated with the FragmentManager. Also the problem I am facing is that When i declare the Fragment fragment = new MyFirstFragment(); I cannot access the fragment.performAction("Stringssss") method. This is not the solution I am looking for:
public MainActivity extends Activity{
private MyFirstFragment myFirstFrag;
private MySecondFragment mySecondFrag;
private MyThirdFragment myThirdFrag;
private MyNthFragment myNthFrag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first_layout);
FragmentManager fragManager = getFragmentManager();
FragmentTransaction transaction = fragManager.beginTransaction();
if(condition1){
myFirstFrag = new MyFirstFragment();
transaction
.add(R.id.registrationFragment, myFirstFrag); //registrationFragment is FrameLayout
transaction.commit();
}else if(condition2){
mySecondFrag = new MySecondFragment();
transaction
.add(R.id.registrationFragment, mySecondFrag); //registrationFragment is FrameLayout
transaction.commit();
}else if(condition3){
//...
}else if(conditionNth){
//...
}
//create intent filter
//create MyBroadcast object
//registerReceiver(receiverObject, intentFilter)
}
//Some action happens
class MyBroadcast extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(action1)) {
if(null != myFirstFrag)
myFirstFrag.performAction();
}else if(null != mySecondFrag){
mySecondFrag.performAction();
}else if(null != myThirdFrag){
myThirdFrag.performAction();
}else if(null != myNthFrag){
myNthFrag.performAction();
}
}else if(action.equals(actionNTH)){
if(null != myFirstFrag)
myFirstFrag.performAction();
}else if(null != mySecondFrag){
mySecondFrag.performAction();
}else if(null != myThirdFrag){
myThirdFrag.performAction();
}else if(null != myNthFrag){
myNthFrag.performAction();
}
}
}
}
As you can see in the BroadcastReceiver a lot of If-else statements start to pile up making the code clunky and dirty. I am looking for a clean solution that will not require me to have a bunch of if-else statements and keep the code simple.
Thank you.

Fragment is re-created after back from Activity

I have a problem related to fragment life cycle.
Before doing this, I will set DONT KEEP ACTIVITY mode(Setting -> Developer options).
In my project have 2 activity:
Activity 1: keep and control Fragment A and Fragment B
Activity 2: do not have fragment.
Activity 1 will be called first, then Fragment A and Fragment B will be called to visible.
Start Activity 2 from Activity 1, this cause Activity 1 will be destroyed and Fragment A & Fragment B will be destroyed too (Because of dont keep activity mode).
Press back key from Activity 2 to back Activity 1
Problem occurs here: Fragment A and Fragment B will be automatically called onCreateView() after back from activity 2 -> I want to avoid this. Can you give me some tips to resolve it?
Update Code
Activity 1
public class MainActivity extends Activity implements OnClickListener {
private Button mBtnShowFragmentA;
private Button mBtnShowFragmentB;
private Button mBtnGoAcitivity2;
protected String mCurrentFragmentTag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_activity_1);
mBtnShowFragmentA = (Button) findViewById(R.id.btn_show_fragmentA);
mBtnShowFragmentB = (Button) findViewById(R.id.btn_show_fragmentB);
mBtnGoAcitivity2 = (Button) findViewById(R.id.btn_go_activity2);
mBtnShowFragmentA.setOnClickListener(this);
mBtnShowFragmentB.setOnClickListener(this);
mBtnGoAcitivity2.setOnClickListener(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
}
protected void addFragment(int contentId, Fragment fragment, boolean isAddStack,
String stackName) {
FragmentManager fm = getFragmentManager();
String newFragment = fragment.getClass().getName();
FragmentTransaction ft = fm.beginTransaction();
Fragment currentFragment = fm.findFragmentByTag(mCurrentFragmentTag);
if (currentFragment != null && !TextUtils.equals(currentFragment.getTag(), newFragment)) {
ft.hide(currentFragment);
}
if (fm.findFragmentByTag(newFragment) != null) {
fragment = (Fragment) fm.findFragmentByTag(newFragment);
}
if (!fragment.isAdded()) {
ft.add(contentId, fragment, newFragment);
} else {
ft.show(fragment);
}
if (isAddStack) {
ft.addToBackStack(stackName);
}
try {
ft.commitAllowingStateLoss();
} catch (Exception e) {
}
mCurrentFragmentTag = newFragment;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_show_fragmentA:
FragmentA fragmentA = new FragmentA();
addFragment(R.id.activity_main_content, fragmentA, false, null);
break;
case R.id.btn_show_fragmentB:
FragmentB fragmentB = new FragmentB();
addFragment(R.id.activity_main_content, fragmentB, false, null);
break;
case R.id.btn_go_activity2:
Intent intent = new Intent(getApplicationContext(), SettingActivity.class);
startActivity(intent);
break;
default:
break;
}
}
}
Fragment A
public class FragmentA extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_fragment_a, container, false);
return view;
}
}
Fragment B
public class FragmentB extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_fragment_b, container, false);
return view;
}
}
Activity 2
public class SettingActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_activity_2);
}
#Override
protected void onDestroy() {
super.onDestroy();
}
#Override
public void onBackPressed() {
super.onBackPressed();
}
}
...................
Use below code to remove activity from stack.
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager .getBackStackEntryCount() > 0) {
fragmentManager .popBackStack();
}

Android :Programmatically go back FragmentActivity from fragment

Iam new to android .I need to go back to the FragmentActivity from fragment page.
My work flow is:
MainActivity->ProfileActivity->ProfilePhotoEditFragment
I need to go back to
ProfilePhotoEditFragment -> ProfileActivity
manifest
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
ProfilePhotoEditFragment.java
public class ProfilePhotoEditFragment extends Fragment implements OnClickListener {
ViewUtils mViewUtils;
Bundle mSavedInstanceState;
private OnNavigateProfileListener mOnNavigateProfileListener;
private Button mCancelButton;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context mContext = getActivity().getApplicationContext();
mViewUtils = new ViewUtils(mContext);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mSavedInstanceState = savedInstanceState;
View view = inflater.inflate(R.layout.fragment_edit_profile_pic, container, false);
mCancelButton = (Button) view.findViewById(R.id.cancel);
mCancelButton.setOnClickListener(this);
return view;
}
#Override
public void onStart() {
super.onStart();
}
#Override
public void onClick(View view) {
if (view.getId() == mCancelButton.getId()){
onBackPressed();
}
}
public void onBackPressed() {
// do something on back.
return;
}
}
remove current fragment ProfilePhotoEditFragment in onBackPressed()
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
//( "stackzeo");
} else {
getSupportFragmentManager().popBackStack();
removeCurrentFragment();
//("stacknotzeo");
}
..
public void removeCurrentFragment() {
Fragment currentFrag = (Fragment) getFragmentManager()
.findFragmentById(R.id.content_frame);
if (currentFrag != null)
getFragmentManager().beginTransaction().remove(currentFrag);
getFragmentManager().beginTransaction().commit();
}
Call findFragmentById() on FragmentManager and determine which fragment is in your R.id.content_frame container.
Thank you All. this my working code
....
#Override
public void onClick(View view) {
if (view.getId() == mCancelButton.getId()){
getActivity().getSupportFragmentManager().popBackStack();
removeCurrentFragment();
}
}
public void removeCurrentFragment()
{
FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
Fragment currentFrag = getActivity().getSupportFragmentManager().findFragmentById(R.id.fragment_common_profile_layout);
String fragName = "NONE";
if (currentFrag!=null)
fragName = currentFrag.getClass().getSimpleName();
if (currentFrag != null)
transaction.remove(currentFrag);
transaction.commit();
}
getActivity().onBackPressed();
You should be able to call this code from every fragment.
Please check, if getActivity() returns null.
Hope this helps.
just write down
public void onBackPressed(){
super.onBackPressed();
}

How to implement saveFragmentInstanceState?

Could you anyone advice me how to implement saveFragmentInstanceState() or some other methods to retrieving fragment instance when back button is pressed. I use own stack for fragment, viz code bellow:
public class stackA extends ActivityInTab {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("StackA", "onCreate");
if(savedInstanceState == null){
navigateTo(new fragmentA());
}
}
}
Next there is implementation of ActivityInTab class. I think for this class must be implemented methods for saving and retrieving fragment state but still I can't find the way how to do this.
abstract class ActivityInTab extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_in_tab);
Log.i("ActivityInTab", "onCreate");
}
protected void navigateTo(Fragment newFragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, newFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
#Override
public void onBackPressed() {
Log.i("ActivityInTab", "onBackPressed");
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 1) {
super.onBackPressed();
} else {
// Otherwise, ask user if he wants to leave :)
//showExitDialog();
super.onBackPressed();
}
}
}
And finally, this is imlementation of Fragments:
public class fragmentA extends Fragment {
private LinearLayout ll;
private FragmentActivity fa;
private String textViewText;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i("Fragment A", "onCreateView");
fa = super.getActivity();
ll = (LinearLayout) inflater.inflate(R.layout.fragmenta, container,
false);
Button next = (Button) ll.findViewById(R.id.button1);
Button randomBtn = (Button) ll.findViewById(R.id.random_button);
final TextView randomText = (TextView) ll
.findViewById(R.id.random_textview);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
((ActivityInTab) getActivity()).navigateTo(new
fragmentB());
}
});
randomBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
randomText.setText(String.valueOf(Math.random()));
textViewText = randomText.getText().toString();
}
});
if (savedInstanceState != null) {
randomText.setText(savedInstanceState.getString("TextView"));
}
return ll;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// setRetainInstance(true);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("TextView", textViewText);
}
}
I'm able to save instance state only for orientation changes using onSaveInstanceState(Bundle outState), but no for back button.
I would be very grateful for any advice, thanks.
In the onBackPressed Method you have access to the FragmentManager which can retrieve any fragment for you using the FragmentManager methods "findFragmentById" or "findFragmentByTag".
You can get direct access to any fragment whos state you want to save using either of those two methods depending on how you added the fragments.

Categories

Resources