Android - MVP - Fragments, Presenter and ViewPager - android

I have an Activity1 that contains several fragments through a ViewPager.
Each fragment has a Presenter. For some reason when I rotate the screen, the fragment is recreated and lose the relationship with the presenter
I'm doing the following
open activity 1 (contains viewPager with fragment)
open activity 2
rotate the screen
Press back button
Apparently the fragment is recreated and I get the following error
Caused by: java.lang.NullPointerException: Attempt to invoke
interface method 'void ....Presenter1.subscribe()' on a null object
reference at ....Fragment1.onResume(Fragment1.java:72)
My Activity1 contains 2 views (layout and layout-normal-land)
The code of activity1 is as follows
#Override
protected void onCreate(Bundle savedInstanceState) {
...
final ViewPager viewPager = (ViewPager) findViewById(R.id.htab_viewpager);
TabLayout tabLayout = (TabLayout) findViewById(R.id.htab_tabs);
...
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
the code of the ViewPagerAdapter class is the following:
private class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<String> mFragmentTitleList = Arrays.asList("Fragment1", "Fragment2");
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
if (position == 0){
Fragment1 fragment = Fragment1.newInstance();
new Presenter1(fragment);
return fragment;
}else if (position == 1){
Fragment2 fragment = Fragment2.newInstance();
new Presenter2(fragment);
return fragment;
}
return null;
}
#Override
public int getCount() {
return mFragmentTitleList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
The code of each Fragment is the following
public class Fragment1 extends Fragment {
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
}
#Override
public void onResume() {
super.onResume();
mPresenter.subscribe();
}
#Override
public void onPause() {
super.onPause();
mPresenter.unsubscribe();
}
#Override
public void setPresenter(#NonNull Presenter1 presenter) {
mPresenter = checkNotNull(presenter);
}
....
}
The code of each presener
public class Presenter1 {
private final Fragment1 view;
public PerfilAlumnoCalificacionesPresenter(Fragment1 view ....) {
....
this.view = view
this.view.setPresenter(this);
}
....
}
How can i fix this? I do not understand why the fragment is recreated if it contains setRetainInstance (true)
What is the best way to initialize the Presenter so that the Fragments do not lose the relation?
thanks, sorry for my english

Related

FragmentStatePagerAdapter items don't load

In my main activity I have multiple Fragments that you can navigate through a bottom navigation (Replace a FrameLayout container in main activity XML)
One of those Fragments contains a ViewPager that should show multiple Fragments (MyFragment) using a FragmentStatePagerAdapter (MyAdapter).
First time I open that Fragment it works just fine but when I navigate to a different one and then come back the first two Fragments in the Pager are grey and unpopulated. Anyone got an idea what the problem might be?
The Adapter is set up like this:
This is called in onFragmentViewCreated
private void setUpHeaderPager() {
featuredAdapter = new MyAdapter(getActivity().getSupportFragmentManager());
myPager.setAdapter(myAdapter);
pageIndicatorView.setViewPager(myPager);
pageIndicatorView.setAnimationType(AnimationType.WORM);
compositeSubscription.add(apiService.getMyPOJOs()
.subscribeOn(networkScheduler)
.observeOn(uiScheduler)
.subscribe(new Action1<List<MyPOJO>>() {
#Override
public void call(List<MyPOJO> myPOJOs) {
myAdapter.setItems(myPOJOs);
pageIndicatorView.setCount(myPOJOs.size());
myPager.setCurrentItem(0);
pageIndicatorView.setSelection(0);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
}));
}
And this is what the Adapter looks like:
private class MyAdapter extends FragmentStatePagerAdapter {
private List<SomePOJO> items = new ArrayList<>(0);
public void setItems(#Nonnull List<Collection> items) {
this.items = items;
notifyDataSetChanged();
}
FeaturedReleasesAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(items.get(position));
}
#Override
public int getCount() {
return items.size();
}
}
Just in case the relevant code of MyFragment:
public class FeaturedReleaseFragment extends BaseFragment {
private MyPOJO someObject = null;
// Bind some views
public static MyFragment newInstance(MyPOJO someObject) {
MyFragment fragment = new MyFragment();
fragment.someObject = someObject;
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_my_fragment, container, false);
ButterKnife.bind(this, rootView);
populate();
return rootView;
}
private void populate() {
if (MyPOJO != null) {
// set some text and load images
}
}
onFragmentViewCreated is called just fine, the adapters getItem is not...
It may be how you're navigating between the bottom navigation fragments and subsequently whats happening with their lifecycle.
If you are using .replace on the FragmentTransaction, try using .show and .hide instead.
Something like the following might do it:
public void navigateToFragment( Fragment frag ) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.hide(currentFragment);
if ( !fragment.isAdded() ) {
transaction.add(R.id.container,fragment)
} else if ( !fragment.isHidden() ) {
transaction.show(fragment);
}
transation.commit();
}

getItem in FragmentStatePagerAdapter never called when instantiating from a Fragment

I'm using a Fragment on my Activity that will have a picture "swiper". I've created a FragmentStatePagerAdapter which I'm trying to call from this Fragment and for some reason my getItem() method is never being called.
I've searched and found that I need to use getChildFragmentManager() when instantiating my FragmentStatePagerAdapter because I'm calling it from a Fragment. I've tried this as well as getSupportFragmentManager() with no luck from either.
Any ideas?
here's my Fragment that I'm using on my MainActivity with the method that shoudl setup the FragmentStatePagerAdapter:
public class PhotoFlipper extends Fragment {
// properties and fields
// primitive types
String currentImage;
int friendShareType = 0;
ImageView share;
ViewPager pager;
PhotoFlipperItemAdapter pagerAdapter;
LatLng itemPosition;
Activity activity;
FragmentActivity fragActivity;
public ArrayList<MarkerModel> picsAndVids;
// end properties and fields
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View photoFlipperFragmentView = inflater.inflate(R.layout.photo_flipper_layout, container, false);
InitializeFragment(photoFlipperFragmentView);
return photoFlipperFragmentView;
}
private void InitializeFragment(View photoFlipperFragmentView) {
activity = getActivity();
fragActivity = (FragmentActivity)activity;
pager = (ViewPager)photoFlipperFragmentView.findViewById(R.id.photoflipper_pager);
share = (ImageView)photoFlipperFragmentView.findViewById(R.id.photoflipper_share);
share.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
IPictureFlipper iPictureFlipper = (IPictureFlipper)activity;
iPictureFlipper.ShareVideoOrImage(friendShareType, itemPosition, currentImage);
}
});
}
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) {
pager.setVisibility(View.GONE);
}
}
public void SetTappedPicture(int index) {
pagerAdapter = new PhotoFlipperItemAdapter(getChildFragmentManager(), picsAndVids, picsAndVids.size());
pager.setAdapter(pagerAdapter);
pager.setCurrentItem(index);
pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
}
});
}
}
And here's my FragmentStatePagerAdapter:
public class PhotoFlipperItemAdapter extends FragmentStatePagerAdapter {
ArrayList<MarkerModel> _markers;
int NUM_PAGES;
String TAG = "home";
public PhotoFlipperItemAdapter(FragmentManager fm, ArrayList<MarkerModel> markers, int numpages){
super(fm);
Log.d(TAG,"in adapter in constructor");
_markers = markers;
NUM_PAGES = numpages;
}
#Override
public Fragment getItem(int position) {
Log.d(TAG,"in adapter in get item");
Fragment fragment = PhotoFlipperItemFragment.create(position, _markers);
return fragment;
}
#Override
public int getCount() {
Log.d(TAG,"in adapter in get count: "+NUM_PAGES);
return NUM_PAGES;
}
}
logcat is showing me that NUM_PAGES is > 0 so that's not my issue. I see my log in the constructor and in getCount() but nothing in getItem().
TIA
I got this sorted out. I was hiding my fragment in my InitializeActivity() right after I added it to my FragmentManager so it was never fully building the Fragment with the ViewPager etc.
I'm going to have to come up with a different solution for hiding/showing the fragment but that was my problem.

Delete fragment in secure way from FragmentPagerAdapter

I am trying to implement a Gallery with a FragmentPagerAdapter on a ViewPager. Each fragment has a "delete" button which deletes the file.
Then I am trying to remove this fragment and scroll to next fragment when a callback of a AsyncTask is called.
In summary, when the delete button is touched first, delete the file in a remote FTP server and then remove the local file.
This is the Activity container of the ViewPager
public class MediaViewerActivity extends FragmentActivity implements OnFTPResult{
(...)
protected void onCreate(Bundle savedInstanceState) {
ArrayList<String> multimedia;
viewPager = (ViewPager) findViewById(R.id.pager);
multimedia = getFiles();
MediaViewerAdapter adapter;
adapter = new MediaViewerAdapter(getSupportFragmentManager(), MediaViewerActivity.this, multimedia);
viewPager.setOffscreenPageLimit(2);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(position);
(...)
}
#Override
public void onActionPerformed(String result) {
switch (result) {
case "Done delete":
//remove the fragment and local file
break;
}
}
}
This is the code of the adapter for the ViewPager
public class MediaViewerAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener{
(...)
public MediaViewerAdapter(FragmentManager fm, Context _ctx,ArrayList<String> imagePaths) {
super(fm);
manager = fm;
_imagePaths = imagePaths;
ctx = _ctx;
}
}
(...)
#Override
public Fragment getItem(int position) {
Fragment fragment;
String[] fileExtensionCaching = _imagePaths.get(position).split("\\.");
String currentExtension = fileExtensionCaching[fileExtensionCaching.length - 1];
Bundle args = new Bundle();
args.putString("path", _imagePaths.get(position));
if((currentExtension.equals("pdf")))
fragment = FragmentPDFViewer.newInstance(args);
else
if((currentExtension.equals("jpg") || currentExtension.equals("png") || currentExtension.equals("gif"))) {
fragment = FragmentImageViewer.newInstance(args);
}
else{
fragment = FragmentVideoPlayer.newInstance(args);
}
return fragment;
}
#Override
public int getCount() {
return _imagePaths.size();
}
(...)
}
And this is a slice of the AsyncTask class
#Override
protected void onPostExecute(String response) {
((OnFTPResult)ctx).onActionPerformed(response);
}
Which would be the best safety way to remove the current Fragment and then scroll to next (If there is)?
Thanks

Fragments in viewpager

I have viewpager with some fragments and i set to each fragment my model class according which UI of fragment should be edited. That model class i set to fragment at constructor before i add that fragment to FragmentStatePagerAdapter. My problem is that after rotating screen or when system kills my application and i restore it, my fragment are recreated by system and my model class instance is lost. Even if I set new FragmentStatePagerAdapter, fragments created by system are visible. Is there any right way of achive that i create new instances of my fragments and place them into viewpager? I tried so many thinks like removing fragments from FragmentManager, adding Adapter to viewpager again, change FragmentStatePagerAdapter only to FragmentPagerAdapter and nothing helped.
My activity class with loading model classes in loader from some resource:
List<Fragment> visibleFragment = new ArrayList<Fragment>();
for (Screen screen : wizard.screens) {
if (screen.visible) {
visibleFragment.add(new ScreenUI(screen));
}
}
viewPager.setAdapter(null);
viewPager.setAdapter(new WizardPagerAdapter(getSupportFragmentManager(), visibleFragment));
My FragmentStatePagerAdapter
public class WizardPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments;
private final int count;
public WizardPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
mFragments = fragments;
count = mFragments.size();
}
#Override
public Fragment getItem(int index) {
return mFragments.get(index);
}
#Override
public int getCount() {
return count;
}
}
try add in Manifest android:configChanges="orientation|keyboardHidden|screenSize" to activity.
OR something like that
public class ViewPagerTaskAdapter extends FragmentStatePagerAdapter {
Map<Integer, FragmentViewTask> fragmentsTask = new HashMap<Integer, FragmentViewTask>();
#Override
public Fragment getItem(int arg0) {
FragmentViewTask myFragment = FragmentViewTask.newInstance(arg0,
tasks.get(arg0));
fragmentsTask.put(arg0, myFragment);
return myFragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
fragmentsTask.remove(position);
}
public FragmentViewTask getFragment(int key) {
return fragmentsTask.get(key);
}
#Override
public int getCount() {
if (tasks != null)
return tasks.size();
return 0;
}
}
Have you tried using onRetainInstance(true) on your Fragments?
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}

Android ViewPager, switching from fragment to another fragment

I have a viewpager with 2 pages, on each fragment i put a button to switching fragment,
but if i change orientation switching doesn't work.
For switching fragment I using my OnChangePageButtonClick interface
Why is this happening?
ViewPager Activity:
public class ViewPagerMusic extends FragmentActivity implements OnChangePageButtonClick {
private ViewPager vp;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_pager_music);
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(super.getSupportFragmentManager(), fragments);
vp = (ViewPager)findViewById(R.id.viewpager);
vp.setAdapter(viewPagerAdapter);
}
private class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position) {
case 0:
MainPage mainPage = new MainPage();
mainPage.setOnChengeButtonListener(ViewPagerMusic.this);
return mainPage;
case 1:
PlaylistPage playlistPage = new PlaylistPage();
playlistPage.setOnChengeButtonListener(ViewPagerMusic.this);
return playlistPage;
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
#Override
public void selectPage(int page) {
vp.setCurrentItem(page);
}
}
my Frgamnets:
public class MainPage extends Fragment {
public MainPage() {
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.music_main_page, container, false);
ImageButton btnPlaylist = (ImageButton)v.findViewById(R.id.btnGoPlaylist2);
btnPlaylist.setOnClickListener(onButtonListener);
return v;
}
private OnClickListener onButtonListener = new OnClickListener() {
#Override
public void onClick(View view) {
onChangePageButtonClick.selectPage(1);
}
};
public void setOnBackButtonListener(OnChangePageButtonClick onChangePageButtonClick) {
this.onChangePageButtonClick = onChangePageButtonClick;
}
private OnChangePageButtonClick onChangePageButtonClick;
}
Playlist fragment is similar to MainPage fragment.
Your fragments are storing a reference to the ViewPagerMusic activity. However, after rotation, the activity is destroyed and recreated, so the fragments now contain a reference to the old activity.
Instead of storing a reference to the activity, you can call the activity's method from the fragment like this:
((ViewPagerMusic) getActivity()).selectPage(1);
just create this method in your view pager
#Override
public void selectPage(int page) {
vp.setCurrentItem(page);
}
and then call this in your onclick method
((ViewPagerMusic) getActivity()).selectPage(1);

Categories

Resources