0

I have a parentfragment with a fagmentpageradapter and a viewpager. Each page shows another fragment which contains an imageview. To avoid outofmemoryerrors i want only the currently shown fragment to load its image in the imageview. So i have to load the imageview in the currently shown fragment und recycle the images in the not shown fragments.

Parentfragment:

public class DatenFragment extends Fragment {

    private List<Data> mData;
    private ViewPaver mViewPager;
    private MyFragmentPagerAdapter mAdapter;

    public DatenFragment() {}

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_daten, container, false);

        mViewPager = (MyViewPager) rootView.findViewById(R.id.view_pager);
        mViewPager.setOnPageChangeListener(this);

        mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager(),mData);

        mViewPager.setAdapter(mAdapter);

        return rootView;
    }

    @Override
    public void onPageSelected(int position) {
        mAdapter.setCurrentPosition(position);
        mAdapter.loadFotos();
    }
}

Fragmentpageradapter:

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    private int mPos = -1;
    private List<Data> mData;

    public MyFragmentPagerAdapter(FragmentManager fm, List<Data> data) {
        super(fm);
        mData = data;
    }

    public void loadFotos(){
        EingabeFragment currentFragment;
        currentFragment = (EingabeFragment) getItem(mPos);
        currentFragment.loadFoto();
        currentFragment = (EingabeFragment) getItem(mPos-1);
        currentFragment.unloadFoto();
        currentFragment = (EingabeFragment) getItem(mPos+1);
        currentFragment.unloadFoto();
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Fragment getItem(int position) {
        return EingabeFragment.createFrom(mData.get(position),position);
    }

    public void setCurrentPosition(int position) { mPos = position; }

}

Fragment shown by the viewpager:

public class EingabeFragment extends Fragment {

    private int mPos;
    private Data mData;
    private ImageView mImageView;

    public EingabeFragment() { }

    public void loadFoto(){
        if(mData.hasFoto()) {
            mImageView.setImageBitmap(Utils.loadFoto(mData.getFoto()));
            Log.d("DEBUG", "Foto loaded for " + mData.getId());
        }
    }
    public void unloadFoto(){
        if(mData.hasFoto()) {
            Drawable drawable = mImageView.getDrawable();
            if (drawable instanceof BitmapDrawable) {
                BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
                Bitmap bitmap = bitmapDrawable.getBitmap();
                bitmap.recycle();
                Log.d("DEBUG","Foto unloaded for " + mData.getId());
            } 
        } 
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_eingabe, container, false);

        mImageView = (ImageView) rootView.findViewById(R.id.image_view);

        return rootView;
    }

    public int getPosition() { return mPos; }

    static public EingabeFragment createFrom(Data data, int fragmentPosition){
        EingabeFragment eingabeFragment = new EingabeFragment();
        eingabeFragment.mData = data;
        eingabeFragment.mPos = fragmentPosition;
        return eingabeFragment;
    }

}

In the loadFotos() function of the adapter, the getItem() function returns a new Instance of the fragment which is shown. So the view is not created yet and mImageView is null. I need a way of getting access to the fragments which is shown in the pager to load the image in this fragment.

OnPageSelected from my ViewPager:

@Override
public void onPageSelected(int position) {

    EingabeFragment f = (EingabeFragment) mAdapter.getItem(mAdapter.getCurrentPosition());
    f.onPauseFragment();

    EingabeFragment f2 = (EingabeFragment)  mAdapter.getItem(position);
    f2.onResumeFragment();

    mAdapter.setCurrentPosition(position);
}

Eingabefragment:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_eingabe, container, false);
    mRootView = rootView;

    imageViewFoto = (ImageView) rootView.findViewById(R.id.imageView_foto);

    //Load other UI...

    mInitialized = true;

    return rootView;
}

@Override
public void onPauseFragment() {
    Log.d("DEBUG",mMessung.getId().toString() + " Pause");
    if(mInitialized) {
        unloadFoto();
        Log.d("DEBUG",mMessung.getId().toString() + " Unloaded Foto");
    } else {
        Log.d("DEBUG",mMessung.getId().toString() + " View is null");
    }
}

@Override
public void onResumeFragment() {
    Log.d("DEBUG",mMessung.getId().toString() + " Resume");
    if(mInitialized) {
        loadFoto();
        Log.d("DEBUG",mMessung.getId().toString() + " Unloaded Foto");
    } else {
        Log.d("DEBUG",mMessung.getId().toString() + " View is null");
    }
}

Now my log is giving me the following information after swipe right and then left:

104 Pasue
104 View is null
105 Resume
105 View is null
105 Pause
105 View is null
104 Resume 
104 View is null

Pause and Resume are called for the right fragments, but mImageView is null

Olli
  • 658
  • 5
  • 26

2 Answers2

1

there is some work around,

If your Fragment extend android.support.v4.app.Fragment

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        //fragment is visible do what ever you want to do..
    }else {
       // handle here
    }
}

or for an other solution you may follow this this link, make an interface and callback so you could now which fragment is in onResume().

For the second solution, onResume() first time call before onCreateView so you need to make a boolean check in your fragment something like lodingFirstTime = true, set it globally, in your load method check

(!lodingFirstTime) {
  //loadfoto method
}

and in onCreateView of your first fragment

(lodingFirstTime) {
      lodingFirstTime = false;
      //loadfoto method
    }
Junaid Hafeez
  • 1,618
  • 1
  • 16
  • 25
  • there was little mistake in `(!isVisibleToUser)`, kindly recheck. – Junaid Hafeez Jan 27 '17 at 11:22
  • open parentfragment: 3 times is the setUserVisibleHint called: - visible is false - mImageView is null 1 time is the setUserVisibleHint called: - visible is true - mImageView is null No image is shown on the current fragment swipe to the next fragment, the image is visible before the function is called 1 time is the setUserVisibleHint called: - visible is false - mImageView is not null 1 time the setUserVisibleHint called: - visible is true - imageview is not null => loading the image when i swipe to the next i get the outofmemoryerror – Olli Jan 27 '17 at 11:26
  • sorry for some inconvinence, since it is called before onCreateView in Fragment, you need to handle it with little trick. see this thread http://stackoverflow.com/questions/24161160/setuservisiblehint-called-before-oncreateview-in-fragment or you may follow the link I mentioned in answer. – Junaid Hafeez Jan 27 '17 at 11:44
  • trying the solution from the link, will give feedback after i tried – Olli Jan 27 '17 at 11:45
  • tried the solution from your first link. Now the load function is called before the fragment is shown and the unloadfunction is called when its no more visible for the user. But the problem now is, that the mImageView reference is null when the load / unloadfunction is called. – Olli Jan 27 '17 at 12:16
  • you can place a null check and can if imageView is null, cast it first. – Junaid Hafeez Jan 27 '17 at 12:21
  • In my onCreateView() i store the imageview in a variable. If the imageView is null when the loadFoto() is called it means the onCreateView() has not been called. If the onCreateView() has not been called i have no reference to a view where i can get the imageview from or am i missing something? – Olli Jan 27 '17 at 12:32
  • I've added the boolean-check. But everytime loadFoto() or unloadFoto() is called, onCreateView() has not been called and there is no chance of getting the imageView. – Olli Jan 27 '17 at 13:04
0

Implement ViewPager.OnPageChangeListener in your pager and load images in your fragment on onPageSelected() method of OnPageChangeListener

    @Override
    public void onPageSelected(int position) {
        //show image
        ((Your Fragment) mFragments.get(position)).loadImage();
    }
Anandu R
  • 56
  • 5
  • same solution like the link in the answere of junaid hafeez, but in his answere the loadImage() function is called through an interface. i implemented it and now the load / unload functions are called correctly. unload is called after the fragment is no more visible and load is called when the fragment is visible. But the problem now is that my mImageView is null. – Olli Jan 27 '17 at 12:14