I have a view pager which I need to refresh it's components which are three fragments.
I want to remove the views and adding new views
The setadapter method throws
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
Although I remove all views from the pager before setting the new adapter
viewPager.removeAllViews();
viewPager.setAdapter(new SampleFragmentPagerAdapter(getChildFragmentManager()));
The Adapter code is as follows
public class SampleFragmentPagerAdapter extends FragmentStatePagerAdapter {
final int PAGE_COUNT = 3;
SettingRTL settingRTL = new SettingRTL();
private String tabTitles[] = new String[]{"News", "Articles", "Reports"};
private String tabTitlesA[] = new String[]{"الأخبار", "المقالات", "التقارير"};
public SampleFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return PromosFragment.newInstance(0, "Page # 1");
case 1:
return ArticlesFragment.newInstance(1, "Page # 2");
case 2:
return OtherFragment.newInstance(2, "Page # 3");
default:
return null;
}
}
#Override
public CharSequence getPageTitle(int position) {
if (settingRTL.getCurrentLang().equals("العربية")) {
return tabTitlesA[position];
}
return tabTitles[position];
}
Because you need to override in your SampleFragmentPagerAdapter:
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
You can override viewPager.addOnPageChangeListener() inside your Fragment.
and then do the needful to create newInstance of Fragment/Activity. whenever the page got changed you can perform your operation for creating new instance of Fragment.
Related
I have an activity, which includes ViewPager with three fragments. In the second fragment i have ViewPager with two different fragments too.
This my MainActivityPagerAdapter which connected with ViewPager in MainActivity:
public class MainActivityPagerAdapter extends FragmentStatePagerAdapter implements LoginView.LoginStateChangeListener {
private static final String TAG = MainActivityPagerAdapter.class.getName();
private MapFragment mapFragment = new MapFragment();
private UserProfileFragment userProfileFragment = new UserProfileFragment();
private CompanyProfileFragment CompanyProfileFragment = new DiveCenterProfileFragment();
private NotificationsFragment notificationsFragment = new notificationsFragment();
public MainActivityPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return mapFragment;
case 1:
return notificationsFragment;
case 2:
switch (MyApplication.getInstance().getSharedPreferenceHelper().getActiveUserType()) {
case DIVECENTER:
return companyProfileFragment;
case DIVER:
case INSTRUCTOR:
return userProfileFragment;
case NONE:
return userProfileFragment;
}
default:
return null;
}
}
#Override
public int getCount() {
return 3;
}
#Override
public int getItemPosition(Object object) {
Log.i(TAG, "getItemPosition");
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
return null;
}
}
This my pager adapter for ViewPager at the second fragment:
public class NotificationsPagerAdapter extends FragmentStatePagerAdapter {
private PersonalNotificationsFragment personalNotificationsFragment = new PersonalNotificationsFragment();
private ActivityNotificationsFragment activityNotificationsFragment = new ActivityNotificationsFragment();
public NotificationsPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return personalNotificationsFragment;
case 1:
return activityNotificationsFragment;
default:
return null;
}
}
#Override
public int getCount() {
return 2;
}
public void setPersonalNotificationsFragment(PersonalNotificationsFragment personalNotificationsFragment) {
this.personalNotificationsFragment = personalNotificationsFragment;
}
public void setActivityNotificationsFragment(ActivityNotificationsFragment activityNotificationsFragment) {
this.activityNotificationsFragment = activityNotificationsFragment;
}
#Override
public CharSequence getPageTitle(int position) {
return null;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
So, there are two user types in app:
Simple user
Company
When user changing his type i need to refresh all fragments in MainActivity including fragments inside second fragment. For this I'm using notifyDataSetChanged() for MainActivityViewPager, it work correctly, but fragments inside second fragment don't update(onCreateView don't called, and i see only empty views).
In what may be a problem?
Use getChildFragmentManager() for inner viewPager
Use getChildFragmentManager() instead of getFragmentManager() for changing fragments inside the viewPager
Here's the inner class that contains fragments and changes fragment views on swipe. This class is defined in an Activity.
private class ViewPagerAdapter extends FragmentPagerAdapter {
private int count;
ViewPagerAdapter(FragmentManager fm, int count) {
super(fm);
this.count = count;
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
return new FriendsListFragment();
case 1:
Bundle bundle = new Bundle();
bundle.putString(Keys.TAG, ChatListFragment.class.getSimpleName());
ChatListFragment chatListFragment = new ChatListFragment();
chatListFragment.setArguments(bundle);
return chatListFragment;
case 2:
return new MoreFragment();
default:
return null;
}
}
#Override
public int getCount() {
return count;
}
}
Here I would like to add a code that sets tags to each fragment, so that I can recognize which fragment I'm at.
I searched quite much, and I haven't found a proper answer for this question, so please help.
You can override the setPrimaryItem method of your FragmentPagerAdapter.
private class ViewPagerAdapter extends FragmentPagerAdapter {
protected int currentPosition = -1;
protected Fragment currentFragment;
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// ....
}
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
this.currentPosition = position;
if (object instanceof Fragment) {
this.currentFragment = (Fragment) object;
}
}
public int getCurrentPosition() {
return currentPosition;
}
public Fragment getCurrentFragment() {
return currentFragment;
}
}
Just use getCurrentPosition or getCurrentFragment methods to get information about current Fragment.
Like in the image below, I have an Activity with a ViewPager (with TabLayout) inside.
Now I want to refresh for example the text of an TextView in a ViewPager Fragment. How can I do this?
http://imgur.com/0kWQCfx
[Sorry for the image link, but I don't have enough reputations yet.]
I already tried something like this:
//In Activity
View root = getLayoutInflater().inflate(R.layout.fragment_home, null);
TextView textView = (TextView) root.findViewById(R.id.text_view);
textView.setText("test");
But I get a NullPointerException for the textView.
Edit 1:
I only need to access the components inside one Fragment, so I am not using an array inside the Adapter.
I changed my ViewPagerAdapter to this:
public class ViewPagerAdapter extends FragmentPagerAdapter {
public HomeFragment fragment;
...
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return fragment = new HomeFragment();
case 1:
...
default:
return null;
}
}
But I still get a NullPointerException here:
final Fragment root = viewPagerAdapter.fragment;
Log.d("testtest", String.valueOf(root == null));
Write this code in your activity where you want to change fragment value. Your fragment id is e.g. my_fragment
Fragment frag = getFragmentManager().findFragmentById(R.id.my_fragment)
((TextView) frag.getView().findViewById(R.id.text_view)).setText(s);
You can create your own Adapter:
private class YourAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener
{
Fragment screens[];
public CheckinHistoryAdapter(FragmentManager fm) {
super(fm);
screens = new Fragment[3];
screens[0] = new CoachFragment();
screens[1] = new LogingFragment();
screens[2] = new HistoryFragment();
}
#Override
public Fragment getItem(int index) {
if(index <= screens.length)
{
return screens[index];
}
return null;
}
#Override
public int getCount() {
return screens.length;
}
}
Use it like this
mViewPager.setAdapter(this.mPagerAdapter);
You can access fragment object with getItem method.
If you only need a reference to one Fragment, then just use the position parameter passed into instantiateItem(), and only assign your Fragment reference for the desired position:
public class ViewPagerAdapter extends FragmentPagerAdapter {
public HomeFragment fragment;
//...
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new HomeFragment(); //modified
case 1:
...
default:
return null;
}
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
if (position == 0) {
fragment = (HomeFragment) createdFragment;
}
return createdFragment;
}
}
I am using FragmentPagerAdapter and I need to refresh the content after user action.
To do this, I am calling notifyDataSetChanged() on the adapter but nothing seems happen. I would like to avoid using FragmentStatePagerAdapter, since it contains just 3 pages.
Here is the override methods of the adapter:
#Override
public int getCount() {
return 3;
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch (position) {
case PAGE_HOME:
fragment = HomeFragment.newInstance();
break;
case PAGE_CATEGORIES:
fragment = CategoriesFragment.newInstance();
break;
case PAGE_ACCOUNT:
if (account == null) {
fragment = AccountFragment.newInstance();
} else {
fragment = MyAccountFragment.newInstance();
}
break;
default:
fragment = null;
}
pageReferenceMap.put(position, fragment);
return fragment;
}
#Override
public CharSequence getPageTitle(int position) {
return null;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
pageReferenceMap.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
pageReferenceMap.remove(position);
}
Thanks.
I found out that I have to return a different id for that page position. So here is how I return a unique id based on the fragment I want to display and not based on the page position:
#Override
public long getItemId(int position) {
long id;
switch (position) {
case HOME_PAGE_POSITION:
//fall through
case CATEGORIES_PAGE_POSITION:
id = position;
break;
case ACCOUNT_PAGE_POSITION:
if (account == null) {
id = position;
} else {
id = position + PAGE_COUNT;
}
break;
default:
id = -1;
}
return id;
}
If you have more that 2 fragments per pages to display at a time, you can just return position+PAGE_COUNT*n. Pretty easy.
Hope this help.
notifyDataSetChanged method will send a call back to your adapter , and check for the getCount method following this call back the getItem method is called. Your content in the screen refresh only if the content reflect any change in the currently selected fragment.The point to note here is that This callback will not select your first fragment. It will stay in the currently selected fragment.
For example:-
The currently selected fragment is 2, and you have changed the contents of fragment number 1 and makes a notifyDataSetChanged callback, so in this case you will not see any change as fragment 2 is currently present in the screen.
I'm working with viewpager inside fragments. I'm using pagerAdapter to switch between two pages via XML. Here is the code:
public class DemoPagerAdapter extends FragmentPagerAdapter {
public DemoPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
Fragment f;
switch (index) {
case 0:
f = new FirstFragment();
break;
case 1:
f = new SecondFragment();
break;
}
return f;
}
#Override
public int getCount() {
return 2;
}
}
And I'm calling it like this:
public class DemoFragment1 extends Fragment {
private ViewPager viewPager;
private DemoPagerAdapter mAdapter;
public DemoFragment1() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.security, container, false);
mAdapter = new DemoPagerAdapter(getActivity().getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
showPages(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
return rootView;
}
Everything works fine. But if I move to DemoFragment2 and then come back to DemoFragment1, PagerAdapter is not called.
Your ViewPager is nested in a Fragment, you should use the ChildFragmentManager, modify:
mAdapter = new DemoPagerAdapter(getChildFragmentManager());
I also assume the ViewPager will keep the off screen fragment in memory, thus it might not call getItem(int index) again. Add
viewPager.setOffscreenPageLimit(0);
return rootView;
Well, I encontered this issue last week.
When I put the ViewPager in the fragment with a FragmentPagerAdapter, after I move forward to anoter Fragment and back, the pager' s view not exists.
I supposed is the FragmentPagerAdapter won' t retain its view in the ViewPager ' s host Fragment, Their life cycle is not the same. Though you put the VewPager' s host Fragment in the back stack, but the FragmentPagerAdapter' s fragment not. And Then, I gave up FragmentPagerAdapter and tried android.support.v4.view.PagerAdapter, (note, it wont' t use fragment to inject view!), everything is fine!
below is the sample:
private final PagerAdapter coverPager = new PagerAdapter() {
private LayoutInflater mLayoutInflater;
#Override
public int getCount() {
return 2;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
if (mLayoutInflater == null) {
mLayoutInflater = LayoutInflater.from(getActivity());
}
switch (position) {
case 0:
View frontPage = mLayoutInflater.inflate(R.layout.profile_cover, container, false);
ImageView avatar = (ImageView) frontPage.findViewById(R.id.avatar);
Picasso.with(getActivity())
.load(mAvatarUrl)
.placeholder(R.drawable.error)
.error(R.drawable.error)
.into(avatar);
TextView screenName = (TextView) frontPage.findViewById(R.id.screen_name);
screenName.setText("#" + mScreenName);
TextView remark = (TextView) frontPage.findViewById(R.id.remark);
remark.setText(TextUtils.isEmpty(mRemark) ? mScreenName : mRemark);
if (mVerified) {
frontPage.findViewById(R.id.verified).setVisibility(View.VISIBLE);
}
container.addView(frontPage);
return frontPage;
case 1:
View introPage = mLayoutInflater.inflate(R.layout.profile_intro, container, false);
CatnutUtils.setText(introPage, R.id.description, TextUtils.isEmpty(mDescription)
? getString(R.string.no_description) : mDescription);
CatnutUtils.setText(introPage, R.id.location, mLocation);
CatnutUtils.setText(introPage, R.id.profile_url, Constants.WEIBO_DOMAIN + mProfileUrl);
container.addView(introPage);
return introPage;
default:
return null;
}
}
};
and then in your onViewCreate or OnCreateView, put mViewPager.setAdapter(coverPager);
the instantiateItem is the place to put the page' s view, similar like the FragmentPagerAdapter!
It' s just like put the FragmentAdapter' s view int the ViewPager' s host Fragment, not the other fragment!
Hope it' s helpful!