Replacing a fragment in a view pager - android

I am trying find a solution simular this https://stackoverflow.com/a/13925130/1634451 but the poster didn't explain what R.id.products_list_linear is. I tried to set R.id.products_list_linear as the id of my linearlayout for the fragment i am trying to replace. But that just overlays the 2 fragments on top of each other. I am pretty sure that i need to get the id of the container in the viewpager but i don't know how to get this id.
Edit:
To clarify i am trying to replace a fragment in a view pager with another my code looks almost exactly like the answer I posted but when I make R.id.products_list_linear the I'd of the fragment I am trying to replace layout. The fragment I am replacing just gets overlaid on the one I am trying to replace

Replace Fragments Within a View Pager
Try this customized ViewPager Adapter,..
import java.util.List;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.view.ViewGroup;
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragments=null;
private FragmentManager fragmentManager=null;
public ViewPagerAdapter(FragmentManager fragmentManager,List<Fragment> fragments) {
super(fragmentManager);
this.fragments=fragments;
this.fragmentManager=fragmentManager;
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object)
{
super.setPrimaryItem(container,0,object);
}
#Override
public void notifyDataSetChanged()
{
super.notifyDataSetChanged();
}
#Override
public void destroyItem(ViewGroup collection, int position, Object view) {
fragmentManager.executePendingTransactions();
fragmentManager.saveFragmentInstanceState(fragments.get(position));
}
public void replaceItem(int position,Fragment fragment)
{
fragments.set(position, fragment);
this.notifyDataSetChanged();
}
}
call it from activity with fragment as arguments,
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment2.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment3.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment4.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment5.class.getName()));
mPagerAdapter=new ViewPagerAdapter(getFragmentManager(), fragments);
When you replace view/fragment inside of viewPager then use,
mPagerAdapter.replaceItem(2,Fragment.instantiate(this, Fragment5.class.getName()));

I ended up going with a different approach that worked out nicely for me. I used a ViewSwitcher for the fragment in the viewpager.

Related

Duplicate Fragments with RecyclerView appearing in ViewPager after swiping

Fragment with RecyclerView is having problem inside ViewPager. Duplicate RecyclerView appears overlapped when the fragments gets recreated after swiping.
I am using ViewPager, Fragment, FragmentStatePagerAdapter
Pic 1 : For the first time there RecyclerView is fine.
Pic 2 : After swiping multiple fragments and then swiping back to same fragment then duplicate fragments gets overlapped while scrolling.
using FragmentStatePagerAdapter
class SlidingFragmentPagerAdapter extends FragmentStatePagerAdapter {
SparseArray<FragmentMain> registeredFragments = new SparseArray<FragmentMain>();
FragmentManager fm;
public SlidingFragmentPagerAdapter(FragmentManager fm) {
super(fm);
this.fm = fm;
}
#Override
public Fragment getItem(int position) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1 * (FragmentViewPager.MAX_PAGE - position - 1));
return FragmentMain.newInstance(position + "", cal.getTime());
}
#Override
public int getCount() {
return MAX_PAGE;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
FragmentMain fragment = (FragmentMain) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public FragmentMain getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
}
Using ViewPager
mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
rootView.findViewById(R.id.img_floating_btn2);
mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
mSlidingTabLayout.setDistributeEvenly(true);
adapter = new SlidingFragmentPagerAdapter(getFragmentManager());
mViewPager.setAdapter(adapter);
mViewPager.setCurrentItem(MAX_PAGE);
mSlidingTabLayout.setViewPager(mViewPager);
Note
I tried using mViewPager.setOffscreenPageLimit(adapter.getCount()); buti have more than 50 fragments in ViewPager
I had the same problem, I had a ViewPager with a RootFragment containing other fragments. I added these fragments to the RootFragment with:
getFragmentManager().beginTransaction().add(containerLayout.getId(), ItemFragment.newInstance()).commit();
I fixed it by changing it to replace instead of add.
getFragmentManager().beginTransaction().replace(containerLayout.getId(), ItemFragment.newInstance()).commit();
Even when the container is empty you have to use replace instead of add!

android viewpager with fragments using FragmentPageAdapter dynamic add, remove

I have an application with tabhost and viewpager.
I have one tab, fragment as fixed.
I have to add tab,fragments dynamically on click radio button in radiogroup from the first fragment.
MyPagerAdapter class:
import java.util.ArrayList;
import java.util.List;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
public class MyViewPageAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments = new ArrayList<Fragment>();
FragmentManager fm;
public List<Fragment> getFragments() {
return fragments;
}
public void setFragments(List<Fragment> fragments) {
this.fragments = fragments;
}
public MyViewPageAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fm = fm;
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
public void addFragment(Fragment fragment) {
this.fragments.add(fragment);
notifyDataSetChanged();
}
public void removeFragments() {
try {
List<Fragment> newList = new ArrayList<Fragment>();
Fragment general = fragments.get(0);
newList.add(general);
this.fragments.clear();
this.fragments = new ArrayList<Fragment>();
this.fragments = newList;
notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
}
public void removeItemFromFrament(ViewGroup container, int position, Object object) {
try {
try {
container.removeViewAt(position);
((ViewPager) container).setCurrentItem(position - 1);
} catch (Exception e) {
e.printStackTrace();
}
notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
}
}
MyPagerFragment class:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MyViewPageFragment extends Fragment {
private static View mView;
public static final MyViewPageFragment newInstance(int layoutId) {
MyViewPageFragment f = new MyViewPageFragment();
Bundle b = new Bundle();
b.putInt("layout", layoutId);
f.setArguments(b);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
int layout = getArguments().getInt("layout");
try {
mView = inflater.inflate(layout, container, false);
} catch (Exception e) {
}
return mView;
}
}
Im trying to add fragments to adapter like this:
Fragment fragment = MyViewPageFragment.newInstance(R.layout.tab_content_layout);
((MyViewPageAdapter) mViewPager.getAdapter()).addFragment(fragment);
My Problem:
On first time, the pages are added properly, after remove all views(except first fragment) and trying to add another set of tabs, fragments the views are not visible and the getSupportFragmentmanager.getFragments().size() is incorrect.
I tried to remove the views from viewpager with fragment manager
FragmentManager manager = ((Fragment) object).getFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove((Fragment) object);
trans.commit();
After adding tabs, fragments on pagescrolled the getSupportFragmentManager().getFragments.get(arg0) returns null.
Note:
I have to maintain the state of the first fragment, so FragmentStatePagerAdapter and getItemPosition return POSITION_NONE is not acceptable.
In the same way that you don't specifically call removeView() on a ListView from a ListAdapter to get rid of an item, you don't directly remove a Fragment with FragmentManager from a FragmentPagerAdapter.
You have to override getItemPosition(), but you have to provide a little more intelligent implementation than just returning POSITION_NONE all the time.
If your first fragment never changes, then always return POSITION_UNCHANGED when that fragment is passed to getItemPosition()
Think about it as a conversation between you and the ViewPager/FragmentPagerAdapter. Here's an example:
You: "I need to change the fragments around." (call notifyDataSetChanged)
Adapter: "Heads up, I'm going to change everything around now." (callback startUpdate)
Adapter: "How many tabs do you want?" (callback getCount())
You: Three. (return '3')
Adapter: "Okay, what do you want me to do with this first one?" (callback getItemPosition)
You: "I want to keep that one right where it is." (return POSITION_UNCHANGED)
Adapter: "Great, what about this second one?" (callback getItemPosition)
You: "I want to move it to the third tab." (return '2')
Adapter: "All right, what about the third one?" (callback getItemPosition)
You: "I want to get rid of that one completely." (return POSITION_NONE)
Adapter: "Okay, I have a space for the second tab, what fragment do you want me to use?" (callback getItem)
You: "This one." (return fragment for position == 1)
Adapter: "I'm done changing everything, kthxbye." (callback finishUpdate)
I might not have the order of calls exactly right, but the basic idea is there.
If you need to maintain the state of the fragments that are kicked out, you could override getItem() and destroyItem() to help you with that:
private FragmentManager mFragmentManager;
public MyPagerAdapter(FragmentManager fm) {
super(fm);
mFragmentManager = fm;
}
#Override
public Fragment getItem(int position) {
// figure out which fragment you want at this position
Fragment fragment = new MyFragment();
// retrieve your savedState from wherever you put it
fragment.setInitialSavedState(savedState);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment)
// save your fragment state somewhere here
super.destroyItem(container, position, object);
}
Your fragments will need to implement saveState() and restoreState() for this to work.
I tried similar requirement as yours with a FragmentStatePagerAdapter which worked fine. May be it could help you.
To add the new fragment:
SIZE = SIZE +1;
adapter.instantiateItem(viewPager, SIZE-1);
adapter.notifyDataSetChanged();
In getItem(), for the "position" create and return the new fragment (I used a switch statement here)
Update the getCount() method to return the new SIZE.
Let me know if you would like me to share the code.

How to add Fragment to first position in FragmentPagerAdapter

I am using FragmentPagerAdapter and a ViewPager to add custom Fragments EDIT: from my MainActivity (also sending a bunch of extra data based on a JSON response via bundle) and using swiping motions to move to the next Fragments in the List.
public class MyPagerAdapter extends FragmentPagerAdapter implements Serializable {
public List<Fragment> fragments;
public FragmentManager fm;
public MyPagerAdapter(FragmentManager fm) {
super(fm);
this.fm = fm;
this.fragments = new ArrayList<Fragment>();
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
}
Everything is working fine as long as I'm adding new Fragments by
using
MyPagerAdapter pageAdapter = new MyPagerAdapter(getSupportFragmentManager());
pager = (ViewPager)findViewById(R.id.myViewPager);
pageAdapter.fragments.add(new CustomFragment());
pager.setAdapter(pageAdapter);
But I can't find a proper way to add Fragments to the beginning of the List and swipe back.
I've tried both
pageAdapter.fragments.add(0, new CustomFragment());
as well as changing the FragmentPagerAdapters List to LinkedList and using
pageAdapter.fragments.addFirst(new CustomFragment());
and then refreshing the adapter by using
pageAdapter.notifyDataSetChanged();
and i keep getting the following exception:
java.lang.IllegalStateException: Can't change tag of fragment CustomFragment{2ead9520 #10 id=0x7f0a0002 android:switcher:2131361794:10}: was android:switcher:2131361794:10 now android:switcher:2131361794:11
The key methods no one has talked about yet is public int getItemPosition(Object) which is used to remap fragments to pages after they move and public long getItemId(int position) which must be overridden by a pager adapter that reorders fragments. The default implementation uses the position of the page as the id, so reordering confuses the FragmentPagerAdapter.
(I am leaving out the Serializable interface as it is irrelevant for the purposes of answering the question - How to reorder fragments in a FragmentPagerAdapter).
public class MyPagerAdapter extends FragmentPagerAdapter {
public List<Fragment> fragments;
public FragmentManager fm;
public MyPagerAdapter(FragmentManager fm) {
super(fm);
this.fm = fm;
this.fragments = new ArrayList<Fragment>();
}
void addFragmentAtPosition(int position, Fragment f) {
if(position == fragments.size())
fragments.add(f);
else
fragments.add(position, f);
notifyDataSetChanged();
}
void removeFragmentAtPosition(int position) {
Fragment f = fragments.remove(position);
if(f != null)
fm.beginTransaction().remove(f).commit();
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public int getItemPosition(Object object){
/*
* called when the fragments are reordered to get the
* changes.
*/
int idx = fragments.indexOf(object);
return idx < 0 ? POSITION_NONE : idx;
}
#Override
public long getItemId(int position) {
/*
* map to a position independent ID, because this
* adapter reorders fragments
*/
return System.identityHashCode(fragments.get(position));
}
}
The key additions are the overrides of public int getItemPosition(Object) and public long getItemId(int). These allow the FragmentPagerAdapter to reposition the existing fragments and to identify the existing active fragments in the FragmentManager cache correctly.
You should not create and add fragments this way. Instead just instantiate the fragments in getItem and the adapter will take care of using them. just do this:
#Override
public Fragment getItem(int position) {
Fragment fragment = new CustomFragment();
fragments.add(fragment)
return fragment
}
I would suggest you don't keep a list of references to fragments since it is not necessary and you risk to create memory leaks.
What i would do is create the fragment only when required like this :
#Override
public Fragment getItem(int position) {
Fragment fragment = new MyFragment();
return fragment;
}
To solve your problem you should create the fragment based on your needs, for example if you have fragments of different class instances like for example one instance of MyFragment another one of YourFragment and so on, just keep a list which says which kind of fragment occupy that position.
For example:
myListMap = new HashMap<Integer, Integer>();
myListMap.put(position, type);
and then create the fragment on the fly:
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
int type = ...find fragment type in that position ....
if(type == MYFRAGMENTTYPE) {
fragment = new MyFragment();
}
return fragment;
}
Don't know if you still need the answer, but I was trying to do something similar and just found the solution :)
You are receiving that exception because of your getItem function. You are returning the fragment in the position that the function receives, and this position is always the last position of the array because that would correspond to the last added fragment in a "typical" usage.
In your case, you want to add a new Fragment in the first position, so your getItem will return twice the same fragment and throw the exception.
To avoid this you need to create a public function into your Adapter and the index of the fragment you are adding, and then return this specific fragment.
PS.: I'm developing only in Kotlin for about 6 months now, so it can have some typos.
public int newFragmentIndex = 0;
public List<Fragment> fragments;
...
public void addFragmentAt(int index, Fragment fragment) {
newFragmentIndex = index;
pageAdapter.fragments.add(index, fragment);
notifyDataSetChanged();
}
public void addFragment(Fragment fragment) {
newFragmentIndex = fragments.size();
pageAdapter.fragments.add(fragment);
notifyDataSetChanged();
}
...
#Override
public Fragment getItem(int position) {
return fragments.get(newFragmentIndex);
}
To call this from your Activity, change your pageAdapter.fragments.add(new CustomFragment());
to
pageAdapter.fragments.addFragment(new CustomFragment());
And
pageAdapter.fragments.add(0, new CustomFragment());
to
pageAdapter.fragments.addFragmentAt(0, new CustomFragment());
Hope this helps you with your problem!

android - FragmentStatePagerAdapter fragment not visible

I have implemented view pager using the FragmentStatePagerAdapter. I have used 3 fragments in the view. The view pager is showing all 3 tabs with views as it should be , now my issue is that the third fragment is not visible but i am able to scroll till third tab and when i scroll back i see the second fragment but again the first fragment vanishes and all i am left is with second fragment.
mPager = (ViewPager)mContext.findViewById(R.id.viewPager);
mPager.setOnPageChangeListener(this);
mPagerAdapter = new MyPagerAdapter(mContext.getSupportFragmentManager(),mContext);
mPager.setAdapter(mPagerAdapter);
adapter code is
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private int NUM_PAGES = 3;
List<Fragment> listFragment = new ArrayList<Fragment>();
private MyActivity mContext;
public MyPagerAdapter(FragmentManager fragmentManager, MyActivity mActivity) {
super(fragmentManager);
this.mContext = mActivity;
listFragment.add(new FirstFragment());
listFragment.add(new SecondFragment());
listFragment.add(new ThirdFragment());
}
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
Log.v("fragment position",""+position);
return listFragment.get(position);
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
I am stuck in this for a while, i would really appreciate some help.Thank you.
It is advised to use FragmentPagerAdapter instead of FragmentStatePagerAdapter, because of very nature of those two. FragmentStatePagerAdapter is being that is able to scroll through infinite views. In case of fixed amount of them, like your case you should use FragmentPagerAdapter.
If you happen to have context issues later override following method:
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
mFragments.remove(position);
mFragments.add(position, fragment);
return fragment;
}
Where mFragments is your list.

How to repeat the fragments (like a loop)?

i am developing an app which has a few fragments and i have set them up using an adapter which extends fragmentpager adapter, and i want the fragments to repeat when any of the end is reached. for e.g if i have set up 4 fragments and then when the user reaches the 4th fragment ,instead of blocking the view,the view should shift to the first fragment ...
i have been able to set up the fragments but don't know how to achieve this loop thing...
here is the code for the adapter
package sourcecode.jazzplayer;
import java.util.List;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.ListFragment;
public class ViewPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
private static String[] titles = new String[] {"Songs", "My Playlists", "Artists","Albums"};
public ViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
#Override
public String getPageTitle( int position )
{
return titles[ position ];
}
}
and here is the code how i have set up the fragments
package sourcecode.jazzplayer;
import java.util.List;
import java.util.Vector;
import sourcecode.jazzplayer.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
public class MyMusic extends FragmentActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mymusic);
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, FragmentSongs.class.getName()));
fragments.add(Fragment.instantiate(this, FragmentArtists.class.getName()));
fragments.add(Fragment.instantiate(this, FragmentPlaylists.class.getName()));
fragments.add(Fragment.instantiate(this, FragmentAlbums.class.getName()));
ViewPagerAdapter adapter = new ViewPagerAdapter(super.getSupportFragmentManager(), fragments);
ViewPager pager = (ViewPager)super.findViewById(R.id.viewpager);
pager.setAdapter(adapter);
pager.setOffscreenPageLimit(4);
pager.setCurrentItem(0);
}
}
Check the Infinite Viewpager project on Github.
It is very simple with a FragmentPagerAdapter:
#Override
public Fragment getItem(int position) {
return this.fragments.get(position % 4);
}
#Override
public long getItemId(int position) {
return position % 4;
}
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
And when initializing the ViewPager set the initial page to a very high value, so you can move the pages both ways. For 4 pages, 0x40000000 is roughly half of Integer.MAX_VALUE and is divisible by 4 (id is 0).
I just pushed my recent code to GitHub LoopingViewPager, I hope this will help you.
This ViewPager can work with views and with fragments, only small code modifications are needed in your adapter and xml files.

Categories

Resources