I'm getting a strange problem with Fragment and ViewPager.
I have 1 ViewPager contains 3 Fragments.
When my app run for the first time. onCreateView on all my fragments get called and ViewPager works perfectly.
Code for initalizing ViewPager
private void initPager() {
mFragmentList.add(new MainSongListFragment());
mFragmentList.add(new MainAlbumArtFragment());
mFragmentList.add(new MainLyricsFragment());
if (mFragmentList == null)
mFragmentList = new ArrayList<Fragment>();
MainPagerAdapter pagerAdapter = new MainPagerAdapter(
getSupportFragmentManager(), mFragmentList);
mMainPager.setAdapter(pagerAdapter);
myListViewInsideFragment= (ListView) findViewById(R.id.my_list);
}
When I press back button and run app again. I get error at the code:
myListViewInsideFragment= (ListView) findViewById(R.id.my_list);
It returns null because the onCreateView method on Fragment does not get called so all views on this fragment is not avaiable.
Code in My Fragment:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.lo_my_list, container,
false);
mListView = (ListView) view
.findViewById(R.id.my_list);
return view;
}
My question is:
Why does not onCreateView method get called when Activity get destroyed and re-run again?.
Thank you.
Related
I have a fragment that holds a viewpager and tablayout. I can access the tablayout from the fragment but when I try to do the same for the view pager it always returns null
//this is a method on the pagefragment
public override void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
tabs = this.View.FindViewById<TabLayout>(Resource.Id.mytab);
vp = (ViewPager) Activity.FindViewById(Resource.Id.myviewpage);
SetUpViewPager(vp);
tabs.SetupWithViewPager(vp);
}
vp alway returns null so the app crashes when it get to SetUpViewPager(vp) but the the tabs is accessible
I have a fragment that holds a viewpager and tablayout.
Problem is that you are hosting your ViewPager in a fragment, but you're trying to find it in the Activity. In the Fragment, you can code like this:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.fragment1, container, false);
var tabs = view.FindViewById<TableLayout>(Resource.Id.mytab);
var vp1 = (ViewPager)view.FindViewById(Resource.Id.myviewpage);
return view;
}
i have a activity A with fragment FA, from this fragment i go to FAB, this last fragment is a FragmentPagerAdapter.
When i go from A to FA to FB, press back button and return to FA, and go again to FB this fragment is not showing anything.
My getView method from pager adapter its not called.
This is my transactions code:
From A to FA:
private void setFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment replyGroupsFragment = new ReplyGroupsFragment();
Bundle bundle = new Bundle();
bundle.putString("title", reportName);
replyGroupsFragment.setArguments(bundle);
fragmentManager.beginTransaction().replace(R.id.container, replyGroupsFragment, "ReplyGroupsFragment").commit();
getSupportActionBar().setTitle(getString(R.string.info));
getSupportActionBar().setDisplayShowTitleEnabled(true);
}
From FA to FB (Use butterkniffe)
#OnItemClick(R.id.listViewReplyGroup)
void listClick(int position) {
List<DomainReplie> domainReplieArrayList = generateRepliesList(position);
setRepliesFragment((ArrayList<DomainReplie>) domainReplieArrayList);
}
And my PagerFragment contains this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ReplyGroupsActivity replyGroupsActivity = (ReplyGroupsActivity) getActivity();
List<DomainReplie> replies = getArguments().getParcelableArrayList("replies");
adapter = new PagerAdapter(replyGroupsActivity.getSupportFragmentManager(), this, replies);
setActionBar();
}
#Override
public void onResume() {
super.onResume();
FirextApplication.getInstance().getBus().register(this);
}
#Override
public void onPause() {
super.onPause();
FirextApplication.getInstance().getBus().unregister(this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.work_containter_replies, container, false);
ButterKnife.inject(this, view);
pager.setAdapter(adapter);
return view;
}
Life cyrcle on both case are equals, but in second round screen doesnt show anything.
A little bit late, but maybe it will help others.
I had the same problem. Finally I've solved it and I made the same mistake as you... Here is the line which is causing the problem:
fragmentManager.beginTransaction().replace(R.id.container, replyGroupsFragment, "ReplyGroupsFragment").commit();
You are calling replace(), but replace() just replaces current fragment - you have to call add() for adding another fragment to back stack - that's also what fixes ButterKnife to load/update views again.
Note: ButterKnife changes a lot! Use bind() instead of inject()
I am working on App where i am attaching 5 fragments on an activity. Everything is working great but when i put my app in background from any fragment and after some time when my app resumes it crashes. I get reference of my Activty as null. here is my code
This is code in Activty from where i am attaching fragment
FragmentManager fragmentManager = getSupportFragmentManager();
searchFragment = SearchFragment.newInstance(MainBaseActivity.this);
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.frameLayoutMain, searchFragment, "SearchFragment");
fragmentTransaction.commit();
And this is my Fragment class
public static SearchFragment newInstance(MainBaseActivity mainBaseActivity) {
fragment = new SearchFragment();
fragment.mainBaseActivity = mainBaseActivity;
fragment.inflater = (LayoutInflater) mainBaseActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
fragment.myApplication = ((MyApplication) mainBaseActivity.getApplicationContext());
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.search_fragment, container, false);
preferences = mainBaseActivity.getSharedPreferences(Constant.CHAI_APP, Context.MODE_PRIVATE); // here i get null pointer
editor = preferences.edit();
return view;
}
Fragments can be killed and recreated by the system at various times. You cannot trust the kind of initialization you do in your newInstance() - when the fragment is recreated, the fields won't be initialized.
Remove these initializations:
fragment.mainBaseActivity = mainBaseActivity;
fragment.inflater = (LayoutInflater) mainBaseActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
fragment.myApplication = ((MyApplication) mainBaseActivity.getApplicationContext());
and use getActivity() in your fragment when you need to access your hosting activity or the Application.
For inflater, one is already passed in as an argument to onCreateView(). No need to fetch it yourself.
(To pass params to a fragment that persist over fragment recreation, use setArguments().)
Fragment has the getActivity() method to retrieve the activity to which it is attached. Use it in place of mainBaseActivity
The question pretty much sums it up, I am trying to save my fragments so when I rotate the screen the application does not crash but I am not sure where or how to save my fragments, I have tried using a fragment manager and setting retainstate to true as well as checking the saved instance state.
This is my code :
EventsActivty - Hosts the fragments
#Override
public void onCreate(Bundle savedInstanceState) {
new AsyncLogin().execute(username, password);
super.onCreate(savedInstanceState);
username = getIntent().getStringExtra("username");
password = getIntent().getStringExtra("password");
}
private List<Fragment> getFragments(){
List<Fragment> fList = new ArrayList<Fragment>();
EventListFragment eventListFragment = (EventListFragment)
EventListFragment.instantiate(this, EventListFragment.class.getName());
EventGridFragment eventGridFragment = (EventGridFragment)
EventGridFragment.instantiate(this, EventGridFragment.class.getName());
fList.add(eventListFragment);
fList.add(eventGridFragment);
return fList;
}
The getFragments is called here, on the OnPostExecute of the AsyncTask
protected void onPostExecute(JSONObject jsonObject) {
try {
getEvents(jsonObject);
setContentView(R.layout.eventlist);
List<Fragment> fragments = getFragments();
pageAdapter = new MyPageAdapter(getSupportFragmentManager(), fragments);
ViewPager pager = (ViewPager)findViewById(R.id.viewpager);
pager.setAdapter(pageAdapter);
}
Fragment 1 : OnCreateView
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
eventObjects = ((EventsActivity)getActivity()).getEventObjects();
setRetainInstance(true);
View view = inflater.inflate(R.layout.eventlist ,container,false);
final ListView listView = (ListView) view.findViewById(R.id.listView);
listView.setAdapter(new MyCustomBaseAdapter(getActivity(), eventObjects));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
Object o = listView.getItemAtPosition(position);
EventObject fullObject = (EventObject)o;
System.out.println("asd");
}
});
return view;
}
}
Fragment 2:
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setRetainInstance(true);
View view = inflater.inflate(R.layout.eventgrid ,container,false);
GridView gridView = (GridView) view.findViewById(R.id.eventgrid);
gridView.setAdapter(new ImageAdapter(view.getContext())); // uses the view to get the context instead of getActivity().
return view;
}
Actually, a FragmentActivity will automatically restore your fragments in onCreate().
Weird thing is, you call AsyncTask first, prior to calling through to super.onCreate() and retrieving username and password from the Intent.
Even having set that aside, that approach will make your activity spawn a new login task every time it's rotated.
Better way is to check savedInstanceState for null:
if (savedInstanceState == null) {
// first launch
new AsyncLogin().execute(username, password);
}
...
That way it's only going to run when Activity is created for the first time.
Second, you need to completely unbind login info from your activity. Make the AsyncTask return whatever login result you get to the Application and store it there. And your activity and fragments to retrieve that info from the Application - that way you get full control over you login procedure: you check whether there's an active session, if there isn't you check whether the AsyncTask is already running, if it isn't - you need to launch it. If it is - you need to wait for it to finish (show progress bar or something).
You should understand that retaining the fragments won't prevent the activity from being distroyed.
Now take a look here:
#Override
public void onCreate(Bundle savedInstanceState) {
new AsyncLogin().execute(username, password);
// .....
}
When a screen orientation change occurs, your activity is still destroyed. As a result a new AsyncTask is started. But when the old AsyncTask completes the job, it tries to deliver the result to the old activity, that was destroyed already. As a result this will cause a crash.
The solution would be to put the AsyncTask in the fragment itself. So that the instance to not be destroyed on orientation change.
See if this article can help you.
Once again, I am so annoyed by the AndroidSDK . Why does this have to be so complicated?
I have a ViewPager that I want to populate with my Fragment. I implemented this on an Activity and it works. But when its in another Fragment the whole thing crashes and the erros messages tells me nothing:
07-01 12:58:20.210: E/AndroidRuntime(10146): java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
Here is my code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_tabwrapper, container);
List<Fragment> fragments = getFragments();
pageAdapter = new MyPageAdapter(getActivity()
.getSupportFragmentManager(), fragments);
pager = (ViewPager) v.findViewById(R.id.viewpager);
pager.setAdapter(pageAdapter); // <- CRASH!
return v;
}
BTW: The whole thing is based on this tutorial: http://architects.dzone.com/articles/android-tutorial-using
This error occurs beacuse you have to remove all views before set adapter in view pager so get right way..... So check this:
MyPageAdapter pageAdapter ;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_tabwrapper, container);
List<Fragment> fragments = getFragments();
pageAdapter = new MyPageAdapter(getActivity()
.getSupportFragmentManager(), fragments);
pager = (ViewPager) v.findViewById(R.id.viewpager);
pager.removeallviews();
pager.setAdapter(pageAdapter); // <- CRASH!
return v;
}
you should not use the ViewGroup that onCreateView provides as container to put your inflated view:
Change
View v = inflater.inflate(R.layout.fragment_tabwrapper, container);
to
View v = inflater.inflate(R.layout.fragment_tabwrapper, null);
This might shed a little light on the topic: http://developer.android.com/about/versions/android-4.2.html#NestedFragments
IMHO: I don't understand why developers put up with these tools. I find them impossible to work with. No wonder there are so many low-quality apps on the Play store. I'm looking forward to the day this project is done. Won't be working with this OS anytime soon again ...