I have two fragment A and B.
Fragment A : have 1 view, i scroll view bottom. after move fragment B, touch back at fragment B move fragment A, i want to see view have scroll top(same refresh layout fragment A. when onCreate).
My view : NewHeader, PullToRevealListView, button search and FrameLayout is parent
Use this function to inflate your first fragment i.e call this function in onCreateView of fragment A
public View createPersistentView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState, int layout) {
if (_rootView == null) {
_rootView = inflater.inflate(layout, null);
} else {
((ViewGroup) _rootView.getParent()).removeView(_rootView);
}
return _rootView;
}
Related
I am using fragments to design my screen.When I navigate back to another fragment (from the back stack), the onCreateView(...) method gets called each time even if the fragment has already been created.How to avoid that the method onCreateView(...) gets called each time and make sure it's called only once (when it's created the first time)?
You can cache your inflated view to the local field if your want. For example:
public class ExampleFragment extends Fragment {
private View fragmentView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
if (fragmentView == null) {
fragmentView = inflater.inflate(R.layout.you_super_view_id, container);
}
return fragmentView;
}
}
But practically, it's ok that pager is reinflating views because it keeps only part of all fragments in memory at the time. So, I think the best idea is to let it work as it should
I have a RecyclerView in a Fragment and initially is hidden. When user clicks a button, I set visibility to true for the RecyclerView and I display some data I have on an ArrayList.
The problem starts when I move another fragment on top (I add the previous fragment with the RecyclerView in the backStack) : if I click back from the new fragment the previous fragment (the one with the RecyclerView ) is visible and in onCreateView() I log the values of the dataSet I'm using for the recyclerView and everything is there, but the recyclerView is empty ( only footer item is presented ).
If we call RvFragment the Fragment with the RecyclerView and NextFragment the fragment that comes to the backstack and then leaves the schema is :
(back pressed)
RvFragment ----------> NextFragment ------------> RvFragment
and here's the code from onCreateView() :
#Nullable
#Override
public View onCreateView(final LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_photo_comments, container, false);
ButterKnife.bind(this, view);
Timber.i("onCreateView data.size == %d", commentArrayList.size());
setToolbarTitle();
Picasso.with(getActivity())
.load(photo)
.placeholder(R.drawable.ic_timeline_image_placeholder)
.centerCrop()
.fit()
.into(ivPhoto);
if (hasCommentsVisible) {
Timber.i("comments are visible!! and dataSize == %d", commentArrayList.size());
llFlagsCommentsContainer.setVisibility(View.GONE);
rvCommentsList.setVisibility(View.VISIBLE);
}
tvComments.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
hasCommentsVisible = true;
llFlagsCommentsContainer.setVisibility(View.GONE);
rvCommentsList.setVisibility(View.VISIBLE);
}
});
initRecyclerView();
return view;
}
You can see with the log statements in the code above I can confirm the data exist. Thanks!
I'm not sure I understand how you set up your fragment stack but just to be sure : onCreateView won't be called again when you press the back button if the fragment is still on the stack.
https://developer.android.com/training/basics/fragments/fragment-ui.html#Replace
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
Only onStart() will.
If you want onCreateView to be called again then you need to use the
FragmentTransaction.replace(NextFragment)
without the addToBackStack() but it means the whole fragment will be recreated from scratch. Probably not what you want, especially if you are getting your data from a webservice.
Alternatively, to fully recreate your fragment every time you come back to it, you can simply remove it entirely :
FragmentTransaction.remove(RvFragment)
and then push your next fragment
I am using sliding menu in andorid, when i click on slide menu's list item, that time i can change fragment-A, and now my question is that One Button-A in Fragment-A, i want to change another Fragment-B on Button-A click listener, Fragment-B must have default back button on Actionbar, when press on Default Back button it goes to the Fragment-A, How to do this?
I am using this code:
getSupportFragmentManager().beginTransaction()
.replace(R.id.content_frame, My_Fragment_B_Object).commit();
getSlidingMenu().showContent();
// Fragment B class Code:
public class FragOne extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frag_b, container, false);
((FragmentChangeActivity) getActivity()).getSupportActionBar().setHomeButtonEnabled(true);
return view;
}
}
Ok i have a TabActivity with a FragmentActivity in each tab. In one of those tabs i have a root Fragment which contains a ScrollView with multiples HorizontalScrollView like the picture:
In each HorizontalScrollView i add dinamically a few of objects inflated like this:
To get the effect of Gallery, well the main problem is when I click in each item to replace for another fragment, i add this fragment to backstack, BUT when i press back to return to this fragment all my textview are contains the same text... It's so very strange, and i dont know WHY!? Some ideas?
I put the onCreateView function that i'm using:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
{
if (view == null) {
this.view = inflater.inflate(R.layout.activity_home, container, false);
} else {
((ViewGroup) view.getParent()).removeView(view);
}
return this.view;
}
I have an app with hierarchy like this:
FragmentTabHost (Main Activity)
- Fragment (tab 1 content - splitter view)
- Fragment (lhs, list)
- Framment (rhs, content view)
- Fragment (tab 2 content)
- Fragment (tab 2 content)
All fragment views are being inflated from resources.
When the app starts everything appears and looks fine. When I switch from the first tab to another tab and back again I get inflate exceptions trying to recreate tab 1's views.
Digging a little deeper, this is what's happening:
On the first load, inflating the splitter view causes its two child fragments to be added to the fragment manager.
On switching away from the first tab, it's view is destroyed but it's child fragments are left in the fragment manager
On switching back to the first tab, the view is re-inflated and since the old child fragments are still in the fragment manager an exception is thrown when the new child fragments are instantiated (by inflation)
I've worked around this by removing the child fragments from the fragment manager (I'm using Mono) and now I can switch tabs without the exception.
public override void OnDestroyView()
{
var ft = FragmentManager.BeginTransaction();
ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ListFragment));
ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ContentFragment));
ft.Commit();
base.OnDestroyView();
}
So I have a few questions:
Is the above the correct way to do this?
If not, how should I be doing it?
Either way, how does saving instance state tie into all of this so that I don't lose view state when switching tabs?
I'm not sure how to do this in Mono, but to add child fragments to another fragment, you can't use the FragmentManager of the Activity. Instead, you have to use the ChildFragmentManager of the hosting Fragment:
http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager()
http://developer.android.com/reference/android/support/v4/app/Fragment.html#getChildFragmentManager()
The main FragmentManager of the Activity handles your tabs.
The ChildFragmentManager of tab1 handles the split views.
OK, I finally figured this out:
As suggested above, first I changed the fragment creation to be done programatically and had them added to the child fragment manager, like so:
public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance)
{
var view = inflater.Inflate(Resource.Layout.MyView, viewGroup, false);
// Add fragments to the child fragment manager
// DONT DO THIS, SEE BELOW
var tx = ChildFragmentManager.BeginTransaction();
tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment());
tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment());
tx.Commit();
return view;
}
As expected, each time I switch tabs, an extra instance of Lhs/RhsFragment would be created, but I noticed that the old Lhs/RhsFragment's OnCreateView would also get called. So after each tab switch, there would be one more call to OnCreateView. Switch tabs 10 times = 11 calls to OnCreateView. This is obviously wrong.
Looking at the source code for FragmentTabHost, I can see that it simply detaches and re-attaches the tab's content fragment when switching tabs. It seems the parent Fragment's ChildFragmentManager is keeping the child fragments around and automatically recreating their views when the parent fragment is re-attached.
So, I moved the creation of fragments to OnCreate, and only if we're not loading from saved state:
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
if (savedInstanceState == null)
{
var tx = ChildFragmentManager.BeginTransaction();
tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment());
tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment());
tx.Commit();
}
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance)
{
// Don't instatiate child fragments here
return inflater.Inflate(Resource.Layout.MyView, viewGroup, false);
}
This fixed the creation of the additional views and switching tab's basically worked now.
The next question was saving and restoring view state. In the child fragments I need to save and restore the currently selected item. Originally I had something like this (this is the child fragment's OnCreateView)
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance)
{
var view = inflater.Inflate(Resource.Layout.CentresList, container, false);
// ... other code ommitted ...
// DONT DO THIS, SEE BELOW
if (savedInstance != null)
{
// Restore selection
_selection = savedInstance.GetString(KEY_SELECTION);
}
else
{
// Select first item
_selection =_items[0];
}
return view;
}
The problem with this is that the tab host doesn't call OnSaveInstanceState when switching tabs. Rather the child fragment is kept alive and it's _selection variable can be just left alone.
So I moved the code to manage selection to OnCreate:
public override void OnCreate(Bundle savedInstance)
{
base.OnCreate(savedInstance);
if (savedInstance != null)
{
// Restore Selection
_selection = savedInstance.GetString(BK_SELECTION);
}
else
{
// Select first item
_selection = _items[0];
}
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance)
{
// Don't restore/init _selection here
return inflater.Inflate(Resource.Layout.CentresList, container, false);
}
Now it all seems to be working perfectly, both when switching tabs and changing orientation.