I'm building an application that entirely consists of fragments and a single activity. I have this usecase where every fragment should be initialized once and everytime it is replaced it gets resume/paused. I thought having a single instance of fragment would do the work but its not happening as expected.
I want to know how to achieve this so that only one instance of fragment gets resume/pause everytime.
Use this method to switch Fragments
void switchFragment(#NonNull Fragment fragment, boolean addToBackStack) {
final String NAME = fragment.getClass().getName();
final FragmentManager fm = getSupportFragmentManager();
final boolean fragmentPopped = fm.popBackStackImmediate(NAME, 0);
if (fragmentPopped || fm.findFragmentByTag(NAME) != null) {
return;
}
if (addToBackStack) {
fm.beginTransaction()
.replace(mContainerResId, fragment, NAME)
.commit();
} else {
fm.beginTransaction()
.replace(mContainerResId, fragment, NAME)
.addToBackStack(NAME)
.commit();
}
}
And in Fragment class create view like below
private View rootView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if (rootView == null) {
rootView = inflater.inflate(R.layout.your_layout, container, false);
}
return rootView;
}
Related
Basically, I have a MainActivity that displays different Fragments when clicked on menu options.
Stats, is a Fragment that displays 4 fragments in it. Every time it is displayed, will replace 4 FrameLayouts on the view.
The first time work perfectly, but when I change to different fragments and get back in Stats, it seems not to replace the FrameLayouts to Fragments...
Stats as follows:
public class StatsFragment extends Fragment {
private View rootView;
private MoreSoldBarChartFragment moreSold = MoreSoldBarChartFragment.newInstance(null);
private MoreIncomeBarChartFragment moreIncome = MoreIncomeBarChartFragment.newInstance(null);
private SoldLineChartFragment soldLine = SoldLineChartFragment.newInstance(null);
private IncomeLineChartFragment incomeLine = IncomeLineChartFragment.newInstance(null);
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_stats, container, false);
initViews();
return rootView;
}
public void initViews() {
replace(moreIncome, R.id.moreIncome);
replace(moreSold, R.id.moreSold);
replace(soldLine, R.id.soldLine);
replace(incomeLine, R.id.incomeLine);
}
private void replace(Fragment fragmentToReplace, int container) {
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(container, fragmentToReplace);
ft.commit();
}
public static StatsFragment newInstance(Bundle args) {
StatsFragment fragment = new StatsFragment();
fragment.setHasOptionsMenu(true);
fragment.setArguments(args);
return fragment;
}
}
You should be using getChildFragmentManager() to display Fragments within Fragments:
private void replace(Fragment fragmentToReplace, int container) {
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.replace(container, fragmentToReplace);
ft.commit();
}
As that ensures that the Fragments are properly handled when the parent's state is changed (i.e., when it is swapped with another Fragment and back). When you use getActivity.getSupportFragmentManager(), Fragments have no idea that these Fragments are related and can't restore the Fragments.
I have an Activity with a ViewPager, each page of the ViewPager has a Fragment.
Inside my Screen3Fragment I have a LinearLayout (lly_fragments) where I am showing some other fragments. I start by showing the fragment Screen3_1
public class Screen3Fragment extends Fragment {
private FragmentManager manager;
private FragmentTransaction transaction;
public static Screen3Fragment newInstance() {
final Screen3Fragment mf = new Screen3Fragment();
return mf;
}
public Screen3Fragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_screen3, container, false);
Screen3_1Fragment frag31 = new Screen3_1Fragment();
manager = getChildFragmentManager();
transaction = manager.beginTransaction();
transaction.add(R.id.lly_fragments,frag31,"frag31");
transaction.addToBackStack("frag31");
transaction.commit();
return v;
}
}
This works fine without problems. Problem comes when, from within frag31 (which is inside Screen3Fragment), I want to call fragt32, for that I do the following.
public class Screen3_1Fragment extends Fragment {
private ImageButton imgbt_timer;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_screen3_1,container,false);
imgbt_timer = (ImageButton) v.findViewById(R.id.bT_scr31_timer);
imgbt_timer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getChildFragmentManager().beginTransaction()
.replace(R.id.lly_fragments, new Screen3_2Fragment(), "frag32")
.commit();
}
});
return v;
}
}
As I read in other answers, the line transaction.replaceshould do the trick and replace the existing frag31 by the given frag32 inside the same given container lly_fragments.
However, I get java.lang.IllegalArgumentException: No view found for id..... I am not sure why.
getFragmentManager() will return always attributes from parent, in most cases the activities. getChildFragmentManager() wil return parent attributes (in your case, Screen3Fragment attributes). It should be used when you add a fragment inside a fragment.
In your case, Screen3Fragment should be added using getFragmentManager() and Screen3_1Fragment should be added using getChildFragmentManager() because Screen3_1Fragment is Screen3Fragment child. Screen3Fragment is the parent.
I recomand you to use always getFragmentManager() with add method, not replace because your parent will be the same.
getChildFragmentManager() can be used when you add a ViewPager inside a fragment.
You can use the callback, as following :
that worked for me, I hope my answers helps you
1) create a interface
public interface ChangeFragmentListener {
void onChangeFragmentLicked(int fragment);
}
2) implement the interface and transaction methods in your activity:
public class MainActivity extends AppCompatActivity implements ChangeFragmentListener {
FragmentTransaction fragmentTransaction;
Fragment1 fragment1;
Fragment2 fragment2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
fragment1 = new Fragment1();
fragment1.setChangeFragmentListener(this);
fragment2 = new Fragment2();
fragment2.setChangeFragmentListener(this);
initListeners();
}
void changeToFrag1() {
fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.activity_main_fragment_container,fragment1, "");
fragmentTransaction.commit();
}
void changeToFrag2() {
fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.activity_main_fragment_container, fragment2, "");
fragmentTransaction.commit();
}
#Override
public void onChangeFragmentLicked(int fragment) {
switch (fragment){
case 1:
changeToFrag1();
break;
case 2:
changeToFrag2();
break;
}
}
3) Create object from the interface to handle the callback:
public class Fragment1 extends Fragment {
private ChangeFragmentListener changeFragmentListener;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_fragment1, container, false);
view.findViewById(R.id.fragment1_textView).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
changeFragmentListener.onChangeFragmentLicked(2);
}
});
return view;
}
public Fragment1 setChangeFragmentListener(ChangeFragmentListener changeFragmentListener) {
this.changeFragmentListener = changeFragmentListener;
return this;
}
}
I am new to android, and i am facing this problem when the screen orientation is changed. The fragment gets called twice whenever screen orientation changes. Below is the sample of my code. I checked other posts, but couldnt find answer. Anyone guide me through this.
public class SampleFragment extends Fragment {
static final String TAG_NAME = SampleFragment.class.getSimpleName();
List<PhrToolBar> mToolBarList;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
DaggerHelper.getAppProviderComponent().inject(this);
mRootView = null;
getActivity().setTitle("Personal Health Records");
mRootView = inflater.inflate(R.layout.sample_phr_main_fragment, container, false);
mBinding = DataBindingUtil.bind(mRootView);
mBinding.setViewModel(mViewModel);
setHasOptionsMenu(true);
return mRootView;
}
Simple add this code
if (savedInstanceState == null) {
// only create fragment if activity is started for the first time
mFragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
FragmentOne fragment = new FragmentOne();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
} else {
// do nothing - fragment is recreated automatically
}
I am facing a problem in regarding fragment.
In my scenario,
There are two fragment associated with FragmentActivity.
In FragmentActivity, there are a container layout (Frame Layout) in which all fragment will replace.
public void replaceFragment(Fragment fragmentClass) {
String selectedFragment = fragmentClass.getClass().getName();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction
.replace(R.id.content_frame, fragmentClass);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
First time , I set a List type fragment (Fragment A) in which get the data from web service and papulate over listview. I execute the AsyncTask from onCreateView() method.
In MainActivity: onCreate
SherlockFragment fragment = new FragmentA();
replaceFragment(fragment);
On list item click of Fragment A, Fragment A will callback the activity method to replace it to details type fragment Fragment B.
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
callback = (ICallBack) activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
/*View rootView = inflater.inflate(R.layout.fragment_locate, container,
false);*/
View rootView = inflater.inflate(R.layout.fragment_a, container,
false);
ListView list = (ListView) rootView
.findViewById(R.id.listView);
adapter = new MyListAdapter();
list.setAdapter(adapter);
list
.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent,
View convertView, int position, long id) {
SherlockFragment fragment = new SalonDetailFragment();
callback.replaceFragment(fragment);
}
});
ListDataTask task = new ListDataTask();
task.execute();
return rootView;
}
class ListDataTask extends AsynTask<Void,Void,List<Data>>{
public Void doInBackground(Void parems){
List<Data> = getDataFromServer();
}
onPostExecute(List<Data> data){
adapter.addAllData(data);
adapter.notifyDataSetChanged();
}
}
When I press back button, from Fragment B then Application will show Fragment A but it execute Asynctask again and get the data to papulate the listview.
So I need to know, How to maintain the pervious state of Fragment like Activity.
Is there are any way to not to create Fragment after come back from Other activity
Have a look my pseudo code.
I got solution. Simple.... do the check null value of rootview
public class FragmentA extends Fragment {
View _rootView;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (_rootView == null) {
// Inflate the layout for this fragment
_rootView = inflater.inflate(R.layout.fragment_a, container, false);
// Find and setup subviews
_listView = (ListView)_rootView.findViewById(R.id.listView);
...
} else {
// Do not inflate the layout again.
// The returned View of onCreateView will be added into the fragment.
// However it is not allowed to be added twice even if the parent is same.
// So we must remove _rootView from the existing parent view group
// (it will be added back).
((ViewGroup)_rootView.getParent()).removeView(_rootView);
}
return _rootView
I have the same problem and solved by replacing
fragmentTransaction
.replace(R.id.content_frame, fragmentClass);
to
fragmentTransaction
.add(R.id.content_frame, fragmentClass);
Replace will always create new instance on back press while Add is just add a new fragment in Stack. for more information check this link
I'm trying out some fragments right now. I've got a fragment with a button and when I click that button I switch to another fragment. Now when I push on the back button I return to the first fragment that's good. Now when I click again on that button a new fragment is started. So I always start a new fragment. I think thats not the way it needs to be done. Is there a better way to for example resume the already created fragment?
My code:
public static class PlaceholderFragment extends Fragment{
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
Button test = (Button)rootView.findViewById(R.id.button);
test.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View view)
{
Log.d("Test", "Button clicked.");
TestFrag newFragment = new TestFrag();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
return rootView;
}
}
public static class TestFrag extends Fragment {
public TestFrag() {
Log.d("Test","New fragment");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main2, container, false);
return rootView;
}
}
When you use the fragment transaction you can specify a tag for the fragment
fragmentTransaction.add(R.id.main_fragment, newFragment, fragTag);
fragmentTransaction.commit();
Then later you can find a fragment by tag from the fragmentManager
newFragment = fragMan.findFragmentByTag(fragTag);
if newFragment then is null you should create the Fragment since it wasnt found by the tag
Thanks to Joakim:
fragmentTransaction.add(R.id.main_fragment, newFragment, fragTag); fragmentTransaction.commit();
Then later you can find a fragment by tag from the fragmentManager
newFragment = fragMan.findFragmentByTag(fragTag); if newFragment then is null you should create the Fragment since it wasnt found by the tag. If the tag isn't null you can just add and commit the new one.