5

I have 4 fragments in a viewpager. And FragmentStatePagerAdapter inside the activity where my viewpager is. FragmentStatePagerAdapter's newInstance() method takes as parameter layout id, therefore each fragment has it's own layout:

ViewPager pager;

public class MainActivity extends FragmentActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);     
        pager = (ViewPager) findViewById(R.id.viewPager);
        pager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));}

    private static class MyPagerAdapter extends FragmentStatePagerAdapter {
       FragmentManager fm;
       SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

        public MyPagerAdapter(FragmentManager fm) {
            super(fm);
            this.fm = fm;
        }

        @Override
        public Fragment getItem(int pos) {
        if(pos!=0){
            pos = pos%4;
        }
            Fragment frag;
            switch(pos) {
            case 0: frag = FirstFragment.newInstance(R.layout.fire_room_wall1);
                    return frag;
            case 1: frag = FirstFragment.newInstance(R.layout.fire_room_wall2);
                    return frag;
            case 2: frag = FirstFragment.newInstance(R.layout.fire_room_wall3);
                    return frag;
            case 3: frag = FirstFragment.newInstance(R.layout.fire_room_wall3);
                    return frag;
            default: return FirstFragment.newInstance(R.layout.fire_room_wall1);
            }
        }

        @Override
        public int getCount() {
            return 444444;
        }     

             @Override
                public Object instantiateItem(ViewGroup container, int position) {
                    Fragment fragment = (Fragment) 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 Fragment getRegisteredFragment(int position) {
                    return registeredFragments.get(position);
                }
    }
}

Here is FirstFragment:

public class FirstFragment extends Fragment{

     @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            int layout = getArguments().getInt("layout");
            View v = inflater.inflate(layout, container, false);    
            return v;
        }

        public static FirstFragment newInstance(int layout) {

            FirstFragment f = new FirstFragment();
            Bundle b = new Bundle();
            b.putInt("layout", layout);
            f.setArguments(b);
            return f;
        }
}

And activity_main.xml:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >
<FrameLayout android:layout_width="match_parent"
    android:layout_height="match_parent">
<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/viewPager"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    />
<com.example.viewpagerfragement.CustomView android:id="@+id/customView"
        android:layout_width="fill_parent" android:layout_height="fill_parent" />
</FrameLayout>
</RelativeLayout>

and one of the newInstance parameters fire_room_wall1.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
     android:background="@drawable/wall_nobrainjar"
     android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/first" >

//some other child views

     <ImageView
        android:id="@+id/zoomed_image"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="gone"/>

//some other child views

</RelativeLayout>

Now at some point in my program I want to change background image of the current fragment's layout. And I attempted to do it like this:

  int i = pager.getCurrentItem();
    Fragment page = ((MyPagerAdapter)pager.getAdapter()).getRegisteredFragment(i);
    ((BitmapDrawable)page.getView().getBackground()).getBitmap().recycle();
    page.getView().setBackgroundResource(R.drawable.wall3_nopaperball);

But the line "((BitmapDrawable)page.getView().getBackground()).getBitmap().recycle();" returns NullPointerException because page.getView().getBackground() is null. What I'm doing wrong or how can I change background image? P.S. for some reason page.getView()'s type is NoSaveStateFrameLayout, not RelativeLayout as one would expect

Nazerke
  • 2,098
  • 7
  • 37
  • 57
  • page.getView() is null too? CAn you please me explain why do you do .getBitmap().recycle(); if you want to change it? – phemt.latd Jan 17 '14 at 08:42
  • pls checkout my answer.. – Kailash Dabhi Jan 17 '14 at 08:55
  • @phemt.latd I'm recycling the old background image, making sure that it doesn't take up memory. And then, in the next line, I'm putting new image at the background. – Nazerke Jan 17 '14 at 10:23
  • Another question, page.getView() is null? or is object and .getBackground() is null? If i remember something the best way to get the fragment page is: viewPager.getChildAt(int x) – phemt.latd Jan 17 '14 at 10:42
  • page.getView() is not null, but returns something rather weird: an Object with type "NoSaveStateFrameLayout" – Nazerke Jan 17 '14 at 10:45

2 Answers2

0

Solution:--

actually your problem is you are recycling bitmap i.e you are freeing that bitmap and the pixel data so, obviously you will get the null .!

It is already mentioned in android api documents:-- enter image description here

You just need to do this:--

int i = pager.getCurrentItem();
Fragment page = ((MyPagerAdapter)pager.getAdapter()).getRegisteredFragment(i);
Bitmap bitmapNeedTobeRecycled = ((BitmapDrawable)page.getView().getBackground()).getBitmap()
page.getView().setBackgroundResource(R.drawable.wall3_nopaperball);
bitmapNeedTobeRecycled.recycle();

It works then..Enjoy..!

Kailash Dabhi
  • 3,473
  • 1
  • 29
  • 47
  • This line "Bitmap bitmapNeedTobeRecycled = ((BitmapDrawable)page.getView().getBackground()).getBitmap()" is giving null pointer exception, because "page.getView().getBackground()" is null. – Nazerke Jan 17 '14 at 10:21
  • The question is, then, how to get background image of the fragment in my situation, looking at the layout I provided (RelativeLayout) – Nazerke Jan 17 '14 at 10:26
0

First of all do not keep a list of fragments in your adapter. Pager adapters should be served with data through custom method and then your getItem() should use it to create Fragments. Instead randomising layouts in getItem() do it on your item list(see code below) before you pass it to adapter. Do not override initialiseItem() or destroyItem(). Also you do not have to keep reference to FragmentManager. You can implement your adapter like this:

Create custom class to holds your data.

public class MyItem {
    private int layout;
    private int bitmap;

    public MyItem(layout, bitmap) {
        this.layout = layout;
        this.bitmap = bitmap;
    }

    //implement getters and setters
}

Next implement adapter like this:

public static class MyPagerAdapter extends FragmentStatePagerAdapter {
    private List<MyItem> items = new ArrayList<MyItem>(0);

    public void setItems(List<MyItem> items) {
        this.items = items;
    }

    @Override
    public Fragment getItem(int pos) {
        MyItem item = items.get(pos);
        return FirstFragment.newInstance(item.getLayout(), item.getBitmap());
    }

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

    public void updateItemBitmap(int position, int bitmap) {
        MyItem myItem = items.get(position);
        myItem.setBitmap(bitmap);
        notifyDataSetChanged();
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }
}

Then in getItem() method you initialise your fragments based on that data. Next create method for updating items which is updating provided position and then invalidate adapter. Don't forget to implement getItemPosition(), it makes ViewPager to invalidate all items, also those which are already created.

Finally, you can update it in your activity like this:

int i = pager.getCurrentItem();
((MyPagerAdapter)pager.getAdapter()).updateItemBitmap(i,R.drawable.wall3_nopaperball);

Your fragment implementation would look like this:

public class FirstFragment extends Fragment{

     @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            int layout = getArguments().getInt("layout");
            int bitmap = getArguments().getInt("bitmap");
            View v = inflater.inflate(layout, container, false);
            v.setBackgroundResource(bitmap);    
            return v;
        }

        public static FirstFragment newInstance(int layout, int bitmap) {

            FirstFragment f = new FirstFragment();
            Bundle b = new Bundle();
            b.putInt("layout", layout);
            b.putInt("bitmap",bitmap);
            f.setArguments(b);
            return f;
        }
}
Damian Petla
  • 8,963
  • 5
  • 44
  • 47