Okay, so I have a FragmentPagerAdapter with 3 pages on it...
Is there a way I can HIDE a page from the SlidingTabLayout? I don't want to destroy the page, as I want to be able to unhide it later. Is there a way to do this? Or do I have to destroy the page, and add it back in later?
Hold all your pages in an Array (or a List).
boolean isHide;
public int getCount (){
if(isHide){
return container.size() - 1;
}
return container.size();
}
public Fragment getItem(int position) {
if(isHide && position == positionToHide){
return container.get(position + 1);
}
return container.get(position);
}
One solution would be to prevent transition to that fragment by hiding the tab button and/or disabling swipe.
fragment will still be loaded but you want be able to move to it.
Related
I'm using ViewPager2 and with FragmentStateAdapter to retain state of fragments. I show 2 fragments in the pager when user is loggedOut but when user logins again the i want to recreate only the 2nd fragment to show another fragment in its replacement without impacting on first fragment. But when I call notifyDataSetChanged then ViewPager doesn't recreate the fragment. If I override getItemId method then createFragment method is called but still old fragment is showing. What could be the possible issue?
#Override
public long getItemId(int position) {
if (redrawFragments && position == 1) {
redrawFragments = false;
return -1;
}
I'm using a ViewPager and displaying a lot of different Fragments inside it, not only in content but they use different classes as well. The list to be displayed should be changed dynamically and even though I manage to swap items around and add new ones to the adapter(and calling notifyDataSetChanged), if I try changing the next item it will still slide to it when using mPager.setCurrentItem(mPager.getCurrentItem() + 1);
I am just adding a new Fragment between the current item and the current next one, it is displayed correctly in the adapter but as the next one was already preloaded then getItem in the adapter is not even called.
Is there another method "stronger" than notifyDataSetChanged that tells my ViewPager that it should get the next item again?
CODE SAMPLES:
The add and get item methods inside my FragmentPagerAdapter(only samples, not the actual code)
public void add(#NonNull Integer fragmentIndex) {
mFragmentOrder.add(fragmentIndex);
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
int selectedFragment = mFragmentOrder(position);
Fragment fragment;
switch (selectedFragment) {
case 1:
fragment = new FragmentA();
break;
case 2:
fragment = new FragmentB();
break;
case 3:
fragment = new FragmentC();
break;
default:
fragment = new FragmentD();
break;
}
return fragment;
}
This is the function used to go to the next item(I don't allow swiping)
public void goToNext() {
mPager.setCurrentItem(mPager.getCurrentItem() + 1);
}
EDITS:
Edit 1: I had already tried using a FragmentStatePagerAdapter instead and setting the OffscreenPageLimit to 0, but to no avail.
Edit 2: [Solution] Using a FragmentStatePagerAdapter AND overwriting the getItemPosition function to return POSITION_NONE or the index in the appropriate cases solved the problem. For some reason even after implementing the right version of this function the normal FragmentPagerAdapter kept delivering the wrong Fragment.
By default, FragmentPagerAdapter assumes that the number and positions of its items remain fixed. Therefore, if you want to introduce for dynamism, you have to provide for it yourself by implementing the getItemPosition(Object object) method in the inherited adapter class. A very basic (but unefficient) implementation would be this:
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Every time the parent view is determining whether the position of one of its child views (items) has changed, this code will force the fragment to be recreated. If you want to avoid the recreation when unnecessary, you have to include some logic in the method. Something like this:
#Override
public int getItemPosition (Object object) {
if (fragmentOrder.indexOf(object) == -1) {
return POSITION_NONE;
} else {
return index;
}
}
Finally, pay attention to possible memory leaks by adding an onDestroyView method to your fragments and nullifying the views you are using.
Here is a good discussion of these issues with the two PagerAdapters.
I have a list, within a list, within a list, and so on. There's about 5 tiers.
It's easy enough to create 5 activities for each list on phones, but what if I want to support tablets as well? So I'd need to work with master detail flow.
However, I can't seem to find any tutorials or information in relations to a nested master detail flow.
Anyway, here is an illustration of what I'm describing:
In the tablet layout, I want the screen to shift 2 tiers at a time. User can advanced to the next tier by selecting a list item from the right tier. To go back to the previous tier, user can tap the back button.
Any idea how I can achieve this?
After a full day scouring the internet, I finally found a solution. To get a "Nested Master Details Flow" effect simply use a ViewPager with FragmentPageAdapter. The Master Detail Flow will look like this:
To change to a two panel mode when the user switches to landscape, in your extended FragmentPagerAdapter class, override the following method:
#Override
public float getPageWidth(int position) {
DisplayMetrics metrics = getResources().getDisplayMetrics();
// if the width is greater than 900dp halve the width of the page
if ((metrics.widthPixels / metrics.density) > 900) {
return (0.5f);
}
return super.getPageWidth(position);
}
To provide an "up button" for the view pager:
viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
// This method will be invoked when a new page becomes selected.
#Override
public void onPageSelected(int position) {
if (position == 0) {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
});
You can tell the "up button" to go back a page like this (where viewpager is a member variable of your activity, holding the a reference to your ViewPager):
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int position = viewpager.getCurrentItem();
if (position > 0) viewpager.setCurrentItem(position-1);
return true;
}
REFERENCES:
ViewPager with FragmentPagerAdapter
Display back button on action bar
Multiple-View ViewPager Options
How to implement a ViewPager with different Fragments / Layouts and example github project
I'm using scrollable tabs with FragmentPagerAdapter and I need to switch between the tabs programmatically
After clicking the create group button, I need to switch to the next tab i.e. manage group. How to do that?
For just moving 1 step ahead you can do:
int current = mPager.getCurrentItem();
mPager.setCurrentItem(current + 1, true);
If you want to scroll to any specific index you can just pass that too:
mPager.setCurrentItem(index, true);
You can also set smoothScroll flag as true/false which will give you smooth transition effect.
EDIT:
In your activity make a method :
public void scrollPager(int index) {
if (mPager != null) {
mPager.setCurrentItem(index, true);
}
}
From your fragment when you hit the button you can either create a call back or simply call activity's method:
((ActivityName) getActivity()).scrollPager(1);
All I want to do is a horizontal carousel in Android.
If I have 3 screens A B and C then I want my ViewPager to allow me to move like
A <-> B,
B <-> C,
C <-> A.
GTalk for Android's conversation can be switched like this.
Samsung's homescreen and application screen can be switched like this.
A B and C are fragments and I'm using an adapter that extends FragmentPagerAdapter. All the fragments will contain a webview.
I have looked here here and here but none of them seem to be doing what I want.
Can anyone guide me in the right direction?
(Cross-posting my answer from an identical StackOverflow question)
One possibility is setting up the screens like this:
C' A B C A'
C' looks just like C, but when you scroll to there, it switches you to the real C.
A' looks just like A, but when you scroll to there, it switches you to the real A.
I would do this by implementing onPageScrollStateChanged like so:
#Override
public void onPageScrollStateChanged (int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
int curr = viewPager.getCurrentItem();
int lastReal = viewPager.getAdapter().getCount() - 2;
if (curr == 0) {
viewPager.setCurrentItem(lastReal, false);
} else if (curr > lastReal) {
viewPager.setCurrentItem(1, false);
}
}
}
Note that this calls the alternate form of setCurrentItem and passes false to cause the jump to happen instantly rather than as a smooth scroll.
There are two main drawbacks I see to this. Firstly, upon reaching either end the user has to let the scrolling settle before they can go further. Secondly, it means having a second copy of all of the views in your first and last page. Depending on how resource-heavy your screens are, that may rule out this technique as a possible solution.
Note also that since the view pager doesn't let clicks go through to underlying controls until after the scrolling has settled, it's probably fine to not set up clicklisteners and the like for the A' and C' fragments.
Edit: Having now implemented this myself, there's another pretty major drawback. When it switches from A' to A or C' to C, the screen flickers for a moment, at least on my current test device.
ViewPager settings:
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mViewPager.setAdapter(new YourPagerAdapter(getSupportFragmentManager()));
//Set the number of pages that should be retained to either side of the current page.
mViewPager.setOffscreenPageLimit(1);
mViewPager.setCurrentItem(50);
FragmentPagerAdapter:
public class YourPagerAdapter extends FragmentPagerAdapter {
final int PAGE_COUNT = 100;
final int REAL_PAGE_COUNT = 3;
public YourPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
while (position > REAL_PAGE_COUNT - 1) {
position = position - REAL_PAGE_COUNT ;
}
switch (position) {
case 0:
return FirstFragment.newInstance(position);
case 1:
return SecondFragment.newInstance(position);
case 2:
return ThirdFragment.newInstance(position);
}
return null;
}
#Override
public int getCount() {
return PAGE_COUNT;
}
}
Implement the getItem(int position) like this:
public Fragment getItem(int position)
{
switch(position)
{
case 0:
Fragment A = new A();
return A;
case 1:
Fragment B = new B();
return B;
same for C....
}
}
you can also have a look at here: SimpleViewPager.. download the source and understand it. Hope it helps.