Refreshing the layout of a Fragment in ViewPager - android

I have an activity which uses ViewPager with 4 fragments. I am using FragmentPagerAdapter to populate the pages.
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FragmentA()
case 1:
return new ProfileFragment();
case 2:
return new FragmentC();
case 3:
return new FragmentD();
}
return null;
}
#Override
public int getCount() {
return 4;
}
}
One of these fragments shows the details of User (ProfileFragment in the above code) with following criteria:
If the user isn't logged in, show login layout
If the user is logged in, show profile layout
Hence, when user opens the app for the first time, the login layout will be shown and when user logs in, the fragment needs to be refreshed to show the profile layout.
The approach I was planning to take was that when user logs in, navigate him to FragmentA and destroy the existing ProfileFragment. The onCreateView method of ProfileFragment handles which layout to show based on login state.
public class ProfileFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
int layout = userLoggedIn() ? R.layout.fragment_user_login : R.layout.fragment_user_profile;
return inflater.inflate(layout, container, false);
}
// Handler for login
public void loginUser() {
((MyActivity) getActivity()).loginUser();
}
}
public class MyActivity extends AppCompatActivity {
private SectionsPagerAdapter mSectionsPagerAdapter;
public void loginUser() {
// 1. Set login details as needed
...
// 2. Navigate to FragmentA
...
// 3. Destroy ProfileFragment
mSectionsPagerAdapter.destroyItem(container, 1, object); // How to get `object` here?
}
}
I have two questions regarding this:
The method destroyItem takes Object object returned from instantiateItem. How to get this value while destroying? Do I need to overwrite instantiateItem and maintain a Map<Integer, Object> from position to object?
Is there a better way to update the view of the fragment without destroying the fragment completely?

I did something like that before using FragmentStatePagerAdapter, you can call notifyDataSetChanged the getItemPosition is called for each existing fragment if you call super.getItemPosition it returns POSITION_UNCHANGED when means the fragment has not changed if you return POSITION_NONE will cause the fragment at that position to be reloaded. you can use that in the getItem method to provide the profile fragment.
in this solution you just need to call setLoggedIn
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
boolean isLoggedIn = false;
boolean userHasJustLoggedIn = false;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
public void setIsLoggedIn() {
isLoggedIn = true;
userHasJustLoggedIn = true;
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FragmentA()
case 1:
if (isLoggedIn) {
return new ProfileFragment();
} else {
return new LoginFragment();
}
case 2:
return new FragmentC();
case 3:
return new FragmentD();
}
return null;
}
#Override
public int getItemPosition(Object object) {
Fragment fragment = (Fragment) object;
if (fragment instanceof LoginFragment && userHasJustLoggedIn) {
userHasJustLoggedIn = false;
return POSITION_NONE;
}
return super.getItemPosition(object);
}
#Override
public int getCount() {
return 4;
}
}

Related

In tab layout activity my fragment does not refresh while swiping

I'm using fragmentpageradapter to inflate fragment , this is my coding.
i'm adding cart item in one fragment and showing it to other fragment and all fragment comes in tab layout.My problem is cart item is not refreshing on other fragment while swiping fragment.
public class ProfileDetailePagerAdapter extends FragmentPagerAdapter
{
int TabCount;
HashMap<Integer, String> mFragmentTags;
FragmentManager mFragmentManager;
private boolean isPagingEnabled = true;
public ProfileDetailePagerAdapter(FragmentManager fm, int TabCount) {
super(fm);
this.TabCount = TabCount;
mFragmentTags = new HashMap<Integer, String>();
mFragmentManager = fm;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
MenuFragment tab1 = new MenuFragment();
return tab1;
case 1:
SelectMember_fragment tab2 = new SelectMember_fragment();
return tab2;
case 2:
ViewCart_fragment tab3 = new ViewCart_fragment();
return tab3;
/* case 3:
PaymentMode_fragment tab4=new PaymentMode_fragment();
return tab4;*/
}
return null;
}
#Override
public int getCount() {
return TabCount;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
// record the fragment tag here.
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
}
Call FragmentStatePagerAdapter
You should use addOnTabSelectedListener
This method is used to add a listener that will be invoked when tab
selection changes.
tabLayoutOBJ.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
public void onTabReselected(TabLayout.Tab tab) {
}
public void onTabSelected(TabLayout.Tab tab){
String getTAB = (String)tab.getText();
// Do your WORK
}
public void onTabUnselected(TabLayout.Tab tab){
}
});
The reason behind it is the implementation of viewpager. There is a property called offset in ViewPager to make ready fragments next to it up to offset(int) value.
Default value is 1. For example, you are in 1st position, 0th and 2nd positions are also created.
On the other hand if you are in 0th position 1st is the only created fragment. If you swipe to right (going 1st) then 2nd is going to be created.
I assume you are changing a shared object from 1st fragment and want to notify 2nd fragment with changed data. To solve it you need to use listener. The elegant way to solve it is adding observer pattern to your shared object.
Good luck
Emre

SectionsPageAdapter with FragmentPageAdapter

First let me try to reproduce my Problem/Question.
My Activity is a TabbedActivity with 3 Tabs, done with a SectionPageAdapter and a FragmentPageAdapter.
On these 3 Tabs the user is input Data. With just swiping the user can change the tab. After the User has filled in all data, he must click on a FloatingActionButton to save the data.
Now there is my Problem i donĀ“t know if i can access these 3 Fragments with the filled in data ?
Can i load all 3 Fragments with the filled in Data, read the data and save it or must i handle the PageChangeListenEvent and saved the Data before i change the Page?
Code:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return TravelInfoActivity.PlaceholderFragment.newInstance(position + 1);
}
#Override
public int getCount() {
return maxTabs;
}
}
public static class PlaceholderFragment extends Fragment {
private static final String ARG_FRAGMENT_ID = "fragment_id";
private static int REQUEST_CODE_PLACE = 1;
private View currentRootView;
private View currentClickedView;
#SuppressLint("UseSparseArrays")
private HashMap<Integer, View> hMap = new HashMap<>();
public PlaceholderFragment() {
}
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static TravelInfoActivity.PlaceholderFragment newInstance(int sectionNumber) {
TravelInfoActivity.PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
int fragmentLayout;
switch (sectionNumber) {
case 1:
fragmentLayout = R.layout.fragment_accommodation;
break;
case 2:
fragmentLayout = R.layout.fragment_visited_places;
break;
case 3:
fragmentLayout = R.layout.fragment_additional_info;
break;
default:
fragmentLayout = R.layout.fragment_accommodation;
}
args.putInt(ARG_FRAGMENT_ID, fragmentLayout);
fragment.setArguments(args);
return fragment;
}
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
final View rootView = inflater.inflate(getArguments().getInt(ARG_FRAGMENT_ID), container, false);
currentRootView = rootView;
switch (getArguments().getInt(ARG_FRAGMENT_ID)) {
// switch handling here, not relevant
}
return rootView;
}
Guys i have done it refreshed my brain ;)
Just save the instance from the Fragment to an array and you can use it later.
I made this in the SectionPageAdapter and ovverided the mehtod instantiateItem().
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
private SparseArray<Fragment> registeredFragments = new SparseArray<>();
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
Integer fragmentId = fragment.getArguments().getInt("fragment_id");
registeredFragments.put(fragmentToPageMap.get(fragmentId), fragment);
return fragment;
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
public boolean registeredFragmentExists(int position) {
if(registeredFragments.size() >= position) {
if(registeredFragments.get(position) != null) {
return true;
}
}
return false;
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return TravelInfoActivity.PlaceholderFragment.newInstance(position);
}
#Override
public int getCount() {
return maxTabs;
}
}
Later you can access the fragments rootView with
mSectionsPagerAdapter.getRegisteredFragment(position);
fragment.getActivity().findViewById(id);

Reload fragments inside ViewPager which inside another fragment inViewPager

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

Communicate between Activity and ViewPager Fragments

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;
}
}

Navigating between Fragments while using a ViewPager

I've looked at so many questions here that I don't even know exactly what I'm looking for.
I have a simple app that uses a ViewPager. It has 3 tabs and there is a fragment for each tab. The first fragment contains a ListView. I want to be able to click on an element in the ListView and have that bring me to a different fragment.
So basically I want to remove the fragment that contained the ListView once an element is clicked and add in a new fragment. I've tried to do this in a few different ways with none working.
The last thing I tried was to edit the TabsPageAdapter once an element was clicked which pretty much works except when I press the back button it exits the app. Also it doesn't seem like the cleanest way of doing this.
TabsPagerAdapter
public class TabsPagerAdapter extends FragmentStatePagerAdapter {
SherlockFragment mf;
TalkingPointsFragment tpf;
ContactFragment gf;
int mode = 0;
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
mf = new CanvassFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
public TabsPagerAdapter(FragmentManager fm, int mode)
{
super(fm);
if(mode == 0)
{
mf = new CanvassFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
else if(mode == 1)
{
mf = new ContactFragment();
tpf = new TalkingPointsFragment();
gf = new ContactFragment();
}
}
#Override
public SherlockFragment getItem(int index) {
switch (index) {
case 0:
return mf;
case 1:
return tpf;
case 2:
return gf;
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object)
{
}
}
The onclick code:
ViewPager viewp = (ViewPager) getActivity().findViewById(R.id.pager);
TabsPagerAdapter mAdapter = new TabsPagerAdapter(getActivity().getSupportFragmentManager(),1);
viewp.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
layout_main.xml
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:actionBarTabStyle="#drawable/actionbar_tab_indicator">
I FragmentStatePagerAdapter too and when a user selects map from the ActionBar, I add the GoogleMapsFragment on top of the FragmentStatePagerAdapter:
// create a new map
mapsFragment = GoogleMapFragment.newInstance();
// Then we add it using a FragmentTransaction.
FragmentTransaction fragmentTransaction = getSupportFragmentManager()
.beginTransaction();
fragmentTransaction.add(android.R.id.content, mapsFragment,
FRAGMENT_MAP_TAG);
fragmentTransaction.commit();
For your case you would probably need to add it to the backstack too, which I have not because in my app the user has to navigate back using the ActionBar.
Thought this approach could work for you too when a user selects an item from your list.
Of course this has the disadvantage of not being able to use the FragmentStatePagerAdapter until the user navigates back. So I am not sure wether that would be acceptable for your app.
Ok, so this took a bit more code that I imagined. Hope you get the idea:
public class MainClass extends FragmentActivity implements CanvassCallback {
// save a single reference to ViewPager and TabsPagerAdapter
private ViewPager mViewPager;
private TabsPagerAdapter mAdapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mViewPager = (ViewPager) findViewById(R.id.pager);
this.mAdapter = new TabsPagerAdapter(getSupportFragmentManager(), this);
mViewPager.setAdapter(mAdapter);
...
}
// from the CanvassCallback interface
public void itemSelected() {
mAdapter.canvassSelected();
mAdapter.notifyDataSetChanged();
}
#Override
public void onBackPressed() {
if (mViewPager.getCurrentItem() == 0 && mAdapter.isCanvassSelected() {
mAdapter.canvassSelected();
mAdapter.notifyDataSetChanged();
}
else {
super.onBackPressed();
}
}
}
Mockup of your CanvassFragment showing the callback
public class CanvassFragment extends SherlockFragment {
public interface CanvassCallback {
public void itemSelected();
}
private CanvassCallback canvassCallback;
public void setCanvassCallback(CanvassCallback canvassCallback) {
this.canvassCallback = canvassCallback;
}
...
// The onClick of your item
private void onClick() {
// notify your activity that an item was selected
canvassCallback.itemSelected();
}
}
The registeredFragments are not strictly needed but I think it provides some value if your need to call methoods on your Fragment from activity.
public class TabsPagerAdapter extends FragmentStatePagerAdapter {
// see upvoted answer from http://stackoverflow.com/questions/8785221/retrieve-a-fragment-from-a-viewpager
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
private boolean canvassSelected = false;
private CanvassCallback canvassCallback;
public TabsPagerAdapter(FragmentManager fm, CanvassCallback canvassCallback) {
super(fm);
this.canvassCallback = canvassCallback;
}
public void canvassSelected() {
canvassSelected = !canvassSelected;
}
public boolean isCanvassSelected() {
return canvassSelected;
}
#Override
public SherlockFragment getItem(int index) {
switch (index) {
case 0:
if (canvassSelected)
return new ContactFragment();
CanvassFragment canvassFragment = new CanvassFragment();
// this ensures that your Activity gets notified when an item is clicked
canvassFragment.setCanvassCallback(canvassCallback);
return canvassFragment;
case 1:
return new TalkingPointsFragment();
case 2:
return new ContactFragment();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Log.d(TAG, "instantiateItem " + position);
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.d(TAG, "destroyItem " + position);
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
}

Categories

Resources