How can I maintain a child view's state when switching fragments? - android

I am having a hard time understanding how the fragment lifecycle relates to switching between fragments in the back stack. Please bear with me if my question exposes more than one misconception.
Here is my code:
public class SomeFragment extends Fragment {
private SomeCustomView customView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.some_fragment, container, false);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Create the child view
customView = (SomeCustomView) getView().findViewById(R.id.some_fragment_child_view);
customView.initializeMyCustomView();
}
}
As you can see, my fragment has a child view. The child view is a custom one. Here's code:
public class SomeCustomView extends SurfaceView implements SurfaceHolder.Callback {
private boolean aVariableWhichMustPersistForLifetimeOfApplication;
}
Whenever this fragment is added to the back stack and then later restored, the variable customView is recreated, and so I loose the value of aVariableWhichMustPersistForLifetimeOfApplication. This is creating all sorts of problems for me.
The application started out using an Activity that only displayed SomeCustomView and there were no fragments. Now I have to add functionality and so I have turned the custom view into a fragment, and thus I arrive at this problem.

I found an answer which works for me. The FragmentTransaction class has a number of methods which allow you to switch fragments in/out. (Android documentation for FragmentTransaction is here and a great StackOverflow explanation is here.)
In my case, I wanted SomeFragment to never loose the data contained in its view. To do this, use this code:
SomeFragment fragment = new SomeFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.activity_fragment_placeholder, fragment, "some_fragment");
transaction.commit();
and then later:
getFragmentManager().beginTransaction().hide(fragment).commit();
You can now add/attach a different fragment to R.id.activity_fragment_placeholder. Notice that I'm using hide() rather than replace(), that's the key difference that keeps the view from being destroyed. When you want the fragment back, you can use show() or Android will do this automatically when the user clicks "Back" if you use addToBackStack() when adding/attaching your other fragment.

Related

How to make changes in widget in parent activity based on the logic written in fragment

As an exercise i am writing a simple Ebook reader app. This has only 1 Main Activity with 3 widgets on it, 2 buttons (Previous, Next) and one Fragment container(I have used Frame Layout). All pages are different fragments that I have created that will go inside the container, and these fragments have only 1 scrollable text view that will only display text. when "next" button is pressed it should go to page2(fragment2) and when previous is pressed it should go back(previous fragment).
My problem is I don't want the "previous" button to show up on the initial screen (page1) and similarly the "next" button should not be observed on the last page.
The approach i tried was, in my fragment1(page1) class, i wrote an if condition like,
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragmentpage1,container,false);
textView1=(TextView)view.findViewById(R.id.tview);
**View listView = getActivity().findViewById(R.id.previous);**
textView1.setText(R.string.page1);
if (textView1.isEnabled()){
listView.setVisibility(View.INVISIBLE);
}
return view;
}
I am checking if textview1(the first fragment) is present or not , if yes then hide the previous button on the Main Activity. This works but it completely hides the previous button even when i go to page2. I tried all possible "is" options but none of them are giving me the results I want.
One workaround that i found was to add "setvisibility" of previous button to all fragments, so on fragment1 it is invisible and then on fragment2 i changed that to visible. But that becomes lengthy if there are 100s of fragments(pages).
Please provide me with a simple solution, I am new to Android.
Below is my Main Activity code:(Do let me know if any changes needs to be done to make code more clean)
public class MainActivity extends FragmentActivity {
Fragment fragment;
FragmentManager fm=getSupportFragmentManager();
FragmentTransaction ft;
private static Button next;
private static Button previous;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
next=(Button)findViewById(R.id.next);
previous=(Button)findViewById(R.id.previous);
create();
}
public void create(){
fragment= new FragmentPage1();
ft= fm.beginTransaction();
ft.add(container,fragment);
ft.commit();
}
public void next(View view){
fragment= new FragmentPage2();
ft= fm.beginTransaction();
ft.replace(container,fragment);
ft.commit();
}
}
Maybe you can try to use the getItem() method in FragmentPagerAdapter class.
Try to disable or enable your buttons using the switch case. Hope this can help =)

Why is fragment calling OnCreate / OnCreateView when using fragment transactions with FrameLayout?

The main page of my application has a FrameLayout.
I'm instantiating two fragments when the activity starts, and I'm trying to use a menu button to swap between the fragment.
scanHistoryFrag = new HistoryFragment();
scanFrag = new ScanFragment();
I never replace these objects - I use the same ones throughout the lifecycle of the application. However, when I swap them in my FrameLayout...
private void ChangeFragment(Android.Support.V4.App.Fragment fragment)
{
Android.Support.V4.App.FragmentTransaction ft = SupportFragmentManager.BeginTransaction();
ft.Replace(Resource.Id.fragmentContainer, fragment);
ft.Commit();
}
OnCreate and OnCreateView are called on the Fragment again... which means any adjustments I made post creation on that fragment are overwritten with initial values again. I can't seem to find any explanation for why this is happening or how I might avoid it.
The ChangeFragment method is being called by OnOptionsItemSelected, as I'm using a menu button to toggle them.
I never replace these objects - I use the same ones throughout the lifecycle of the application.
Initialization of a subclass of Fragment just create a instance of this class object, the constructor of this class will be called, but it will not go through the lifecycle of Fragment unless this Fragment is added, for more information, you can refer to Fragments. To understand it easier, I personal think the instance saves the data state of this Fragment class, but the events of lifecycle handle the view state of this Fragment.
which means any adjustments I made post creation on that fragment are overwritten with initial values again.
Yes, you're right. To avoid overwritting with initial values again, we can cache the fragment's view in OnCreateView for example like this:
private View rootView;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
if (rootView == null)
{
//first time creating this fragment view
rootView = inflater.Inflate(Resource.Layout.fragmentlayout1, container, false);
//Initialization
//TODO:
}
else
{
//not first time creating this fragment view
ViewGroup parent = (ViewGroup)rootView.Parent;
if (parent != null)
{
parent.RemoveView(rootView);
}
}
return rootView;
}

Android retaining information across fragments

I have five fragments a user can switch between. One of these fragments loads a list of users from the server to populate the UI list on the fragment. I need the list information to persist if a user swipes to a different fragment and then swipes back to the original. I do not want the fragment to reload the users every time a user leaves the fragment and goes back.
I am looking at setRetainInsance(true) and was wondering if this is possible solution? What would be the best way for the fragment to retain the information without being created from scratch each time.
I am using this to switch between fragements -getSupportFragmentManager().beginTransaction().replace(R.id.searchLayout, ratingFragment).commit();
A Fragment is Just like any other object.
on Fragment transaction , the Fragment does not call OnCreate() method instead it starts from onCreateView , therefore , load your users and save it an instance variable and assign it in onCreate()
Example
class MyFragment extends Fragment{
List<users> userList;
void onCreate(){
userList = getUserList();}
//the list is loaded during Oncreate();
now imagine you have replaced the Fragment
now According to Andorid Framework , onCreate() is not Called again
instead onCreateView() is called
void onCreateView(){
//you can check whether instance Variable is initialised or not
if(userList != null) {
listview.setAdapter(new Myadapter(this,userList);
Replace the fragment by adding it to backstack.
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.replace(container, fragment, tag);
fragmentTransaction.commit();
Also create object of View and return it if it's not null.
private void View view ;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (view != null)
return view;
view = inflater.inflate(R.layout.fragment_browse_recipe, container, false);
//initialize layout views
return view;
}

Get fragment instance within PreferencesFragment

Via a checkbox in my preferences, the view of a fragment must be updated. The preferences are within a PreferenceFragment:
public class SettingsFragment extends PreferenceFragment {...}
This fragment is instantiated in an Activity using:
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
My app has some other fragments, which extend android.support.v4.app.Fragment. Upon the checkbox change in the preferences, I have to update the view in this fragment:
public class OfferingsFragment extends android.support.v4.app.Fragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
// This view needs to be updated
View view = inflater.inflate(R.layout.offering_tiles, null);
...
}
}
My question is: How can I update this view from within the PreferencesFragment? Apparently, I cannot use getFragmentManager to get an instance of OfferingsFragment because OfferingsFragment is a fragment from support library. Is there a way to access OfferingsFragment from within PreferenceFragment?
You have a few options based on how your app is setup. If both fragments are not active at the same time, the Offerings fragment can just read the value from the SharedPreferences when it creates the view.
If both fragments are active and the view needs to be refreshed, you can either create a callback through the parent activity using an interface, example here. Or an easier route is to use an event bus like Otto or Green Robot

Find which frame the current fragment is displayed in

I have an issue with fragment navigation in frame-layouts. Namely I cannot find out in which frame the fragment is currently being displayed in.
My setup is the following:
I have 3 parent fragments (PA, PB, PC), each with their own unique framelayouts. Additionally I have 3 Childfragments (CA,CB, CC) and two Grandchildfragments (GA, GB)
The parent fragments call the childfragments into the appropriate framelayouts (works fine), but now I want the childfragments to replace themselves with the appropriate grandchildren, but I have no way of finding out in which framelayout the childfragments are currently displayed in. (I hope that makes sense)
Pseudo-code example:
public class Parent extends Fragment{
//lots of stuff here
public void replaceFragmentWithChild(){
Fragment frg = new childFragment();
FragmentManager mgr = ((FragmentActivity) getActivity()).getSupportFragmentManager();
FragmentTransaction trx = mgr.beginTransaction();
trx.addToBackStack(null);
trx.replace(R.id.content_view_a, childFragment).commit();
}
}
public class Child extends Fragment{
//lots of stuff here
public void replaceFragmentWithGrandChild(){
Fragment frg = new GrandChildFragment();
FragmentManager mgr = ((FragmentActivity) getActivity()).getSupportFragmentManager();
FragmentTransaction trx = mgr.beginTransaction();
trx.addToBackStack(null);
//here is the problem. I would like to call trx.replace(GET-CURRENT-CONTENT-VIEW, frg)
trx.replace(???, frg).commit();
}}
Any input would be much appreciated.
Edit: Ok, I have found a possible solution by saving the ViewGroup Container in the onCreateView and passing that to the trx.replace function.
But unfortunately I am getting the following Message: java.lang.IllegalArgumentException: No view found for id 0x7f0c0057 (de.tel.quenference.activities:id/sara_content_view) for fragment PaperviewFragment{421da470 #2 id=0x7f0c0057} which makes little sense to me, because the view is the same one I am in,or?
What am I missing?
Ok, I figured it out and I sort of want to shoot myself.
First off, the 'No View Found' error stems from the fact, that my parentfragment did not call transaction.addToBackStack (I added it into my example code but not my production code...)
Just putting that in unfortunately did not fix everything, but I had to also change the ParentFragment to not call getChildFragmentManager, but rather the normal FragmentManager
otherwise the grandchildfragment would not appear in the frameLayout.
Basically my working code looks like this:
public class Parent extends Fragment{
//lots of stuff here
public void replaceFragmentWithChild(){
Fragment frg = new childFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
trx.addToBackStack(null);
trx.replace(R.id.content_view_a, childFragment).commit();
}
}
public class Child extends Fragment{
private ViewGroup locContainer;
//lots of stuff here
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
locContainer = container;
return super.onCreateView(inflater, container, savedInstanceState);
}
public void replaceFragmentWithGrandChild(){
Fragment frg = new GrandChildFragment();
FragmentTransaction trx = getFragmentManager().beginTransaction();
trx.addToBackStack(
trx.replace(locContainer.getId(), frg).commit();
}}

Categories

Resources