Error DialogFragment starting in multiple fragments - android

I currently got a ViewPager that has 3 tabs. Each tab is a fragment. I made a custom dialogFragment to login. I want that logindialog to open only when i go on my 3rd tab. In my onCreateView, i created a new object of my logindialog and show. My problem is, whenever i switch from my 1st tab to 2nd, the dialog also appears and i don't want that.
This is my viewpager adapter
public Fragment getItem(int position) {
switch (position) {
case 0:
return frag1;
case 1:
return frag2;
case 2:
return frag3;
default:
return null;
}
}
My 3rd Frag
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_tab_annonce, container, false);
DialogFragment loginDialog = new DialogFragment();
loginDialog .show(getActivity().getSupportFragmentManager(), "customLogin");
return root;
}

ViewPager will create and retain "offscreen" pages as a performance optimization (with one offscreen page in either direction by default). What this means in practice is that when you are on Page 1, Page 2 has already been created off-screen. When you switch from Page 1 to Page 2, now Page 3 is created off-screen (and Page 1 is retained in case you want to switch back to it).
That means that onCreateView() is simply not the right place to show the error message.
You could perhaps create your own subclass of ViewPager.SimpleOnPageChangeListener and override onPageSelected() to show the dialog.
ViewPager.OnPageChangeListener listener = new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (position == 2) {
// show dialog
}
}
};
viewPager.addOnPageChangeListener(listener);

Related

Persistant fragments and viewpager

I want to use 3 fragments within an Android App, I red:
Creating-and-Using-Fragments
.But I want to use the viewpager for swiping and displaying the fragments like explained in:
ViewPager-with-FragmentPagerAdapter
.But this code or the default code of Android Studio Example, use newInstance to create the instances, each time it is needed and destroy when not needed.
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return FirstFragment.newInstance(0, "Page # 1");
case 1: // Fragment # 0 - This will show FirstFragment different title
return FirstFragment.newInstance(1, "Page # 2");
case 2: // Fragment # 1 - This will show SecondFragment
return SecondFragment.newInstance(2, "Page # 3");
default:
return null;
}
}
but I want to create once for all :
// Within an activity
private FragmentA fragmentA;
private FragmentB fragmentB;
private FragmentC fragmentC;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
fragmentA = FragmentA.newInstance("foo");
fragmentB = FragmentB.newInstance("bar");
fragmentC = FragmentC.newInstance("baz");
}
}
and only hide/show them, as in the example:
// ...onCreate stays the same
// Replace the switch method
protected void displayFragmentA() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (fragmentA.isAdded()) { // if the fragment is already in container
ft.show(fragmentA);
} else { // fragment needs to be added to frame container
ft.add(R.id.flContainer, fragmentA, "A");
}
// Hide fragment B
if (fragmentB.isAdded()) { ft.hide(fragmentB); }
// Hide fragment C
if (fragmentC.isAdded()) { ft.hide(fragmentC); }
// Commit changes
ft.commit();
}
But how to do that with a FragmentPagerAdapter
public Fragment getItem(int position) no longer has to be like that
Also, how to access a data
public double [][] tableaux;
that is in the MainActivity from one Fragment.
Data will be persistant if I assign a pointer of the Fragment I just created in the MainActivity onCreate to point on the MainActivity.tableaux
You can return you pre-initialized fragments in getItem method.
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: return fragmentA;
case 1: return fragmentB;
case 2: return fragmentC;
default: return null;
}
UPDATE: #Alok is right. You should not have fragment references in Activity. And I suggest instead of increasing offscreen page limit using setOffscreenPageLimit, you should consider saving & restoring fragment states using public void onSaveInstanceState(Bundle) and savedInstanceState argument of public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState).
#Lotfi you don't need to save your fragment inside activity, it is prone to leaks .
Also you can assign id to each of your fragment and later when you need to reuse that fragment you can ask fragment manager to return the fragment by passing ie. by calling fragment manager method findFragmentByID() inside your getItem().

android - inflate fragment view only when choosing its tab

I have a PagerAdapter which creates 3 fragments.
In the MainActivity I set the ViewPager like this:
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setOffscreenPageLimit(2);
pager.setAdapter(new PagerAdapter(getSupportFragmentManager()));
the pager.setOffScreenPageLimit(2) is from here https://stackoverflow.com/a/11852707/1662033, to make sure OnViewCreated is called once for each fragment.
Here is my PagerAdapter class:
public class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Home";
case 1:
return "Live";
case 2:
return "Gallery";
default:
return null;
}
}
#Override
public int getCount() {
return 3;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new HomeFragment();
case 1:
return new LiveFragment();
case 2:
return new GalleryFragment();
default:
return null;
}
}
}
In the current code: all of the fragments's onCreateView, onActivityCreated etc are called once, at the beginning and that's it.
The issue I am having is - in one of the fragments (LiveFragment) I have a custom view which connects to a camera and shows the live stream.
What I want is - to inflate the view of LiveFragment only when the user navigates to the fragment, instead of how its now - its inflated at the beginning with the other fragments.
Is there a way to call onCreateView only when fragment is chosen?
FragmentPagerAdapter creates all the Fragments and has all of them in memory at all time. i.e. All your Fragments are created only once and you can navigate around them.
FragmentStatePagerAdapter creates and has only 3 Fragments (the current Fragment and the left and right Fragments to the current one) in memory at any given time, by default. You cannot reduce that number. However, you can increase the number Fragments in memory by using the viewpager.setOffScreenPageLimit().
Since you have only 3 Fragments, all your 3 fragments are created when the Viewpager is initialised. You can track which Fragment is currently visible on the screen using viewpager.addOnPageChangeListener(). Using this you can change the View of your LiveFragment from dummy one to actual View only when the Fragment is currently visible.

Showing a fragment with different parameters in tabs of a view pager

I want to show a same fragment for example fragment having activities of a day of week in a viewpager for all days with different data. I will be giving the dayNumber parameter to each fragment being instantiated and showing related activities. The problem is I see same fragment in each tab no matter what parameter I passed. I think the last fragment added or instantiated by a pager overrides all the other tab fragments instance. Because when I open a list item in expendableList View it is opened in all fragments of the pager.
This is how I am using the pager and fragment.
Pager
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// When swiping between pages, select the
// corresponding tab.
if (bar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS)
if (bar.getSelectedNavigationIndex() != position)
bar.setSelectedNavigationItem(position);
// should be changed when some solution comes.
if (tab == 0) {
Fragment ev;
if ((ev = (Fragment) mPagerAdapter.instantiateItem(mPager,
0)) instanceof frTimetable)
((frTimetable) ev).refresh(day.Monday);
} else if (tab == 1) {
Fragment ac;
if ((ac = (Fragment) mPagerAdapter.instantiateItem(mPager,
1)) instanceof frTimetable)
((frTimetable) ac).refresh(day.Tuesday);
}
tab = position;
}
Fragement
{
//class other methods
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fr_timetable, container, false);
ExpandList = (ExpandableListView) view.findViewById(R.id.expActivityView);
//I will change the list items in refresh method of the fragment for a day type
ExpListItems = new ArrayList<Items>(Timetable_Provider.getAllActivites());
ExpAdapter = new ExpandListAdapter(getActivity(), ExpListItems);
ExpandList.setAdapter(ExpAdapter);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
instantiateItem() is certainly not what you want. That is called by ViewPager, not by a consumer of a ViewPager.
Normally, you would provide the data to the ViewPager as part of setting up the pages, inside of your PagerAdapter. For example, this sample app uses the arguments Bundle to pass the page number of the page to the Fragment that is the implementation of the page.
If the data inside a page needs to be updated, ideally the page itself determines on its own that this is needed and handles it. Or, use an event bus (e.g., LocalBroadcastManager, greenrobot's EventBus, Square's Otto) to publish information that relevant pages can pick up. There is no great way to get at an existing page from outside of the page itself using FragmentPagerAdapter or FragmentStatePagerAdapter (e.g., to have an activity push data into a page), which is one of the reasons I wrote ArrayPagerAdapter.

ViewPagerAdapter returns wrong position

I'm developing an application in which i use a ViewPager.
Underneath the ViewPager, there are some bullet points that should indicate which page is displayed, but their behaviour is wrong.
Here's the activity part:
ind1 = (ImageView)findViewById(R.id.page1);
ind2 = (ImageView)findViewById(R.id.page2);
ind3 = (ImageView)findViewById(R.id.page3);
ind4 = (ImageView)findViewById(R.id.page4);
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
Here's the Adapter:
/*Adapter for ViewPager*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter{
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position){
case 0:
ind1.setImageResource(on);
ind2.setImageResource(off);
ind3.setImageResource(off);
ind4.setImageResource(off);
return Frag1.newInstance();
case 1:
ind1.setImageResource(off);
ind2.setImageResource(on);
ind3.setImageResource(off);
ind4.setImageResource(off);
return Frag2.newInstance();
case 2:
ind1.setImageResource(off);
ind2.setImageResource(off);
ind3.setImageResource(on);
ind4.setImageResource(off);
return Frag3.newInstance();
case 3:
ind1.setImageResource(off);
ind2.setImageResource(off);
ind3.setImageResource(off);
ind4.setImageResource(on);
return Frag4.newInstance();
default:
ind1.setImageResource(on);
ind2.setImageResource(off);
ind3.setImageResource(off);
ind4.setImageResource(off);
return Frag1.newInstance();
}
}
#Override
public int getCount() {
return NUM_PAGES; /*NUM_PAGES = 4*/
}
}
Here's Frag1 (the other Fragments are equal to Frag1, the only differences are the layout and the loaded HTML file):
public class Frag1 extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.frag_page_1, container, false);
WebView wv = (WebView) rootView.findViewById(R.id.webView1);
wv.loadUrl("file:///android_asset/html/tabel1.html");
wv.setBackgroundColor(0x00000000);
wv.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
return rootView;
}
public static Frag1 newInstance(){
Frag1 f = new Frag1();
return f;
}
}
The wrong behaviour is the following: when activity starts, the first fragment is displayed but the 'active' bullet point is the second, for the second page the active point is the third, for the third page is active the fourth, for the fourth page the fourth point remains active. If i swipe back to the third page the second bullet point is active.
In simple terms, the active bullet point doesn't match with the displayed page.
I tried to print the position in the Adapter, like following, under each case::
Toast.makeText(context, String.valueOf(position), Toast.LENGTH_SHORT).show();
When activity starts, are printed two consecutive message, 0 and 1, without interacting with the ViewPager.
When i change page, the position is always wrong, but the relative fragment are displayed correctly.
I tried to set the bullet point state in the fragments class, but nothing changed.
Why the adapter indicates the wrong position? How can i make that the correct bullet point indicates the right displayed page?

Is the asynctask always executed when swiping through fragments?

Basicly I have very same code as described here :
In each of the fragments I have different asynctask, that just fetches data from website.
It looks like this :
TextView text;
String content;
View today;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
today = inflater.inflate(R.layout.today, container, false);
text = (TextView) today.findViewById(R.id.textView);
// ((TextView) today.findViewById(R.id.textView)).setText("Today");
new RetriveSiteData().execute("http://menza.lupajz.eu/?den=dnes");
content = (String) text.getText();
return today;
}
I am wondering if the data is always fetched, while swiping through fragments if the TabPagerAdapter class (on the website)
has this methode implemented the same way
#Override
public Fragment getItem(int i) {
switch (i) {
case 0:
//Fragement for Android Tab
return new Android();
case 1:
//Fragment for Ios Tab
return new Ios();
case 2:
//Fragment for Windows Tab
return new Windows();
}
return null;
}
In case if the data is always fetched and the asynctask is always executed how can I prevent that ? Maybe adding some onResume() methods to each fragment ?
Basically you have two options:
Execute the async tasks only once in the activity that contains the view pager, and then pass the data to your fragment. You can do it with some lazy loading as well.
Use the ViewPager.setOffscreenPageLimit(int) function to set the number of fragment to be retained in the view pager. Default value is set to 1, and thus the getItem() method is called every time you switch. I don't recommend going with this option though, especially when you have complex and memory consuming views.

Categories

Resources