Can I use view pager with views (not with fragments)

82,495

Solution 1

You need to override these two methods rather than getItem():

@Override
public Object instantiateItem(ViewGroup collection, int position) {
    View v = layoutInflater.inflate(...);
    ...
    collection.addView(v,0);
    return v;
}

@Override
public void destroyItem(ViewGroup collection, int position, Object view) {
    collection.removeView((View) view);
}

Solution 2

Use this example

You can use a single XML layout nesting the children views.

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/page_one"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
                        <TextView
                        android:text="PAGE ONE IN"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:textColor="#fff"
                        android:textSize="24dp"/>
            </LinearLayout>

            <LinearLayout
                android:id="@+id/page_two"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
                        <TextView
                        android:text="PAGE TWO IN"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:textColor="#fff"
                        android:textSize="24dp"/>
            </LinearLayout>

    </android.support.v4.view.ViewPager>
</LinearLayout>

BUT... you need handle this with an adapter also. Here we return the finded view ID without inflate any other layout.

class WizardPagerAdapter extends PagerAdapter {

    public Object instantiateItem(ViewGroup collection, int position) {

        int resId = 0;
        switch (position) {
        case 0:
            resId = R.id.page_one;
            break;
        case 1:
            resId = R.id.page_two;
            break;
        }
        return findViewById(resId);
    }

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

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == arg1;
    }

    @Override public void destroyItem(ViewGroup container, int position, Object object) {
        // No super
    }
}

// Set the ViewPager adapter

WizardPagerAdapter adapter = new WizardPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);

Solution 3

We have build a very simple subclass of the ViewPager that we use sometimes.

/**
 * View pager used for a finite, low number of pages, where there is no need for
 * optimization.
 */
public class StaticViewPager extends ViewPager {

    /**
     * Initialize the view.
     *
     * @param context
     *            The application context.
     */
    public StaticViewPager(final Context context) {
        super(context);
    }

    /**
     * Initialize the view.
     *
     * @param context
     *            The application context.
     * @param attrs
     *            The requested attributes.
     */
    public StaticViewPager(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        // Make sure all are loaded at once
        final int childrenCount = getChildCount();
        setOffscreenPageLimit(childrenCount - 1);

        // Attach the adapter
        setAdapter(new PagerAdapter() {

            @Override
            public Object instantiateItem(final ViewGroup container, final int position) {
                return container.getChildAt(position);
            }

            @Override
            public boolean isViewFromObject(final View arg0, final Object arg1) {
                return arg0 == arg1;

            }

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

            @Override
            public void destroyItem(final View container, final int position, final Object object) {}
        });
    }

}

This class does not need a adapter as it will load the views from the layout. In order to use it your projects, just use it instead of the android.support.v4.view.ViewPager.

All the fancy stuff will still work, but you do not need to be bothered with the adapters.

Solution 4

Based on the previous answers, I made the following class to achieve that in a proper and clearest way (I hope):

public class MyViewPagerAdapter extends PagerAdapter {

    ArrayList<ViewGroup> views;
    LayoutInflater inflater;

    public MyViewPagerAdapter(ActionBarActivity ctx){
        inflater = LayoutInflater.from(ctx);
        //instantiate your views list
        views = new ArrayList<ViewGroup>(5);
    }

    /**
     * To be called by onStop
     * Clean the memory
     */
    public void release(){
     views.clear();
        views = null;
    }

    /**
     * Return the number of views available.
     */
    @Override
    public int getCount() {
        return 5;
    }

    /**
     * Create the page for the given position. The adapter is responsible
     * for adding the view to the container given here, although it only
     * must ensure this is done by the time it returns from
     * {@link #finishUpdate(ViewGroup)}.
     *
     * @param container The containing View in which the page will be shown.
     * @param position The page position to be instantiated.
     * @return Returns an Object representing the new page. This does not
     *         need to be a View, but can be some other container of
     *         the page.  ,container
     */
    public Object instantiateItem(ViewGroup container, int position) {
        ViewGroup currentView;
        Log.e("MyViewPagerAdapter", "instantiateItem for " + position);
        if(views.size()>position&&views.get(position) != null){
            Log.e("MyViewPagerAdapter",
                  "instantiateItem views.get(position) " +
                  views.get(position));
            currentView = views.get(position);
        }
        else{
            Log.e("MyViewPagerAdapter", "instantiateItem need to create the View");
            int rootLayout = R.layout.view_screen;
            currentView = (ViewGroup) inflater.inflate(rootLayout, container, false);

            ((TextView)currentView.findViewById(R.id.txvTitle)).setText("My Views " + position);
            ((TextView)currentView.findViewById(R.id.btnButton)).setText("Button");
            ((ImageView)currentView.findViewById(R.id.imvPicture)).setBackgroundColor(0xFF00FF00);
        }
        container.addView(currentView);
        return currentView;
    }

    /**
     * Remove a page for the given position. The adapter is responsible
     * for removing the view from its container, although it only must ensure
     * this is done by the time it returns from {@link #finishUpdate(ViewGroup)}.
     *
     * @param container The containing View from which the page will be removed.
     * @param position The page position to be removed.
     * @param object The same object that was returned by
     * {@link #instantiateItem(View, int)}.
     */
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View)object);

    }

    /**
     * Determines whether a page View is associated with a specific key object
     * as returned by {@link #instantiateItem(ViewGroup, int)}. This method is
     * required for a PagerAdapter to function properly.
     *
     * @param view   Page View to check for association with <code>object</code>
     * @param object Object to check for association with <code>view</code>
     * @return true if <code>view</code> is associated with the key object <code>object</code>
     */
    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view==((View)object);
    }
}

And you have to set it in your activity:

public class ActivityWithViewsPaged extends ActionBarActivity {

    /**
     * The page Adapter: Manage the list of views (in fact here, its fragments)
     * And send them to the ViewPager
     */
    private MyViewPagerAdapter pagerAdapter;

    /**
     * The ViewPager is a ViewGroup that manage the swipe from left
     * to right to left.
     * Like a listView with a gesture listener...
     */
    private ViewPager viewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_views);

        // Find the viewPager
        viewPager = (ViewPager) super.findViewById(R.id.viewpager);

        // Instantiate the PageAdapter
        pagerAdapter = new MyViewPagerAdapter(this);

        // Affectation de l'adapter au ViewPager
        viewPager.setAdapter(pagerAdapter);
        viewPager.setClipToPadding(false);
        viewPager.setPageMargin(12);

        // Add animation when the page are swiped
        // this instanciation only works with honeyComb and more
        // if you want it all version use AnimatorProxy of the nineoldAndroid lib
        //@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            viewPager.setPageTransformer(true, new PageTransformer());
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        pagerAdapter.release();
    }

Where the XML files are obvious view_screen.xml:

<xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/screen"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

 <TextView
        android:id="@+id/txvTitle"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:shadowColor="#FF00FF"
        android:shadowDx="10"
        android:shadowDy="10"
        android:shadowRadius="5"
        android:textSize="32dp"
        android:textStyle="italic"
        android:background="#FFFFF000"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFFF00F0">
        <TextView
            android:id="@+id/txvLeft"
            android:layout_width="wrap_content"
            android:layout_gravity="left"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
        <TextView
            android:id="@+id/txvRight"
            android:layout_width="wrap_content"
            android:layout_gravity="right"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"/>
    </LinearLayout>
    <Button
        android:id="@+id/btnButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>
    <ImageView
        android:id="@+id/imvPicture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"/>
</LinearLayout>

And ActivtyMain has the following layout:

<?xml version="1.0" encoding="utf-8"?>

<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:paddingLeft="24dp"
    android:paddingRight="24dp"
    android:id="@+id/viewpager"
    android:background="#FF00F0F0">
</android.support.v4.view.ViewPager>

Big thanks to Brian and Nicholas for your answer, I hope I add some clearest information and hightlight some good practices for this feature.

Solution 5

I would like to add my solution here. Given that you don't need to use fragments, you can still create a PagerAdapter which attaches views instead of fragments to the ViewPager.

Extend PagerAdapter instead of FragmentPagerAdapter

public class CustomPagerAdapter extends PagerAdapter {

  private Context context;

  public CustomPagerAdapter(Context context) {
    super();
    this.context = context;
  }


  @Override
  public Object instantiateItem(ViewGroup collection, int position) {
    LayoutInflater inflater = LayoutInflater.from(context);
    View view = null;
    switch (position){
      case 0:
        view = MemoryView.getView(context, collection);
        break;
      case 1:
        view = NetworkView.getView(context, collection);
        break;
      case 2:
        view = CpuView.getView(context, collection);
        break;
    }

    collection.addView(view);
    return view;
  }

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

  @Override
  public boolean isViewFromObject(View view, Object object) {
    return view==object;
  }

  @Override
  public void destroyItem(ViewGroup collection, int position, Object view) {
    collection.removeView((View) view);
  }
}

Now you need to define three classes which will return the views to be inflated in the viewpager. Similar to CpuView you will have MemoryView and NetworkView classes. Each of them will inflate their respective layouts.

public class CpuView {

public static View getView(Context context, ViewGroup collection) {

    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context
        .LAYOUT_INFLATER_SERVICE);
    return inflater.inflate(R.layout.debugger_cpu_layout, collection, false);
  }
}

And finally a layout which will be inflated in each of the views

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000000"
        android:text="CPU"/>
</LinearLayout>

P.S.: The reason I wrote this answer is because all the solutions provided here seems to be working fine, but they are inflating the layouts in the PagerAdapter class itself. For large projects it becomes difficult to maintain if their is a lot of code related to the layouts inflated. Now in this example all the views have separate classes and separate layouts. So the project can be easily maintained.

Share:
82,495

Related videos on Youtube

ranjith
Author by

ranjith

Updated on July 08, 2022

Comments

  • ranjith
    ranjith almost 2 years

    I am using ViewPager for swiping between Fragments, but can I use ViewPager to swipe between Views simple XML layout?

    This is my page Adapter for the ViewPager which is used to swipe between Fragments:

    import java.util.List;
    
    import com.app.name.fragments.TipsFragment;
    
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentPagerAdapter;
    import android.support.v4.app.FragmentTransaction;
    import android.view.ViewGroup;
    
    public class PageAdapter extends FragmentPagerAdapter {
    
        /**
         *
         */
        List<Fragment> fragments;
        public PageAdapter(FragmentManager fm,List<Fragment> frags) {
            super(fm);
            fragments = frags;
    
        }
    
        @Override
        public Fragment getItem(int arg0) {
            // TODO Auto-generated method stub
            return TipsFragment.newInstance(0, 0);
        }
    
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return 4;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            FragmentManager manager = ((Fragment) object).getFragmentManager();
            FragmentTransaction trans = manager.beginTransaction();
            trans.remove((Fragment) object);
            trans.commit();
    
            super.destroyItem(container, position, object);
        }
    
    }
    

    And this is my tip fragment:

    public class TipsFragment extends Fragment
    {
        public static TipsFragment newInstance(int image,int content)
        {
            TipsFragment fragment = new TipsFragment();
            return fragment;
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.tip_layout, null);
            return view;
        }
    }
    

    How can I modify my code to work with Views instead of Fragment?

    • Zar E Ahmer
      Zar E Ahmer about 8 years
      also have a look at this example stackoverflow.com/a/37916222/3496570
    • Eftekhari
      Eftekhari over 7 years
      Why no using fragments? What will we achieve or lose if we use or don't use fragments?
    • Harshil Pansare
      Harshil Pansare almost 7 years
      @Eftekhari Fragments => Complex LifeCycle => More Bugs => Chaos
    • Eftekhari
      Eftekhari almost 7 years
      @HarshilPansare Yes, I went through all of these disasters after I asked this questions in February and I will not use fragments in my projects anymore. I had no choice but to clean all the fragments from the ViewPager on onDestroy thus on onResume activity there will be no need to retrieve all 3 fragments that may no longer available. Just wanted to mention one of the problems.
    • Harshil Pansare
      Harshil Pansare almost 7 years
      Cheers to fragments-free life!
  • ranjith
    ranjith almost 11 years
    can u add the full code for fragment page adapter... because i over ride this methods as u said.... but... the app got crash...
  • ranjith
    ranjith almost 11 years
    hey frnd... the findViewById() method is not defined here
  • ranjith
    ranjith almost 11 years
    nice one.. help me a lot... i extended PageAdapter instead of FragmentPageAdapter........ now its work fine.....
  • ruX
    ruX over 10 years
    @user1672337 findViewById is accessible from Activity class. So make internal (non-static) class in your Actvitiy. Or you have to pass activity instance to adapter
  • Bitcoin Cash - ADA enthusiast
    Bitcoin Cash - ADA enthusiast over 10 years
    For a complete working example, check out the code found on this question: stackoverflow.com/q/7263291
  • McLan
    McLan about 10 years
    my app doesn't have fragments, how can i start my activity everytime I swipe a page ?
  • Nathan Walters
    Nathan Walters about 10 years
    This doesn't appear to work if the ViewPager has more than 2 child views. For example, if I have three RelativeLayouts in my ViewPager and then try to swipe to the third page, the third page shows up as blank. Any idea what gives?
  • Mikhail
    Mikhail about 10 years
    @NathanWalters, I had the same issue today, have resolved it increasing OffscreenPageLimit property of ViewPager. Probably it worth to update an answer with this information.
  • Nathan Walters
    Nathan Walters about 10 years
    @Mikhail that worked perfectly, thank you so much! This should definitely be added to the answer.
  • Roel
    Roel almost 10 years
    I can find nothing on that link that has to do with this answer.
  • Amit Gupta
    Amit Gupta over 9 years
    Out of curiosity, why addView is required. The fragment adapter just asked for it return the view.
  • Beloo
    Beloo over 9 years
    @Nathan Walters Beware of outofmemory error this solution could lead. If view pager have many pages and a big setOffscreenPageLimit value an exception could be thrown especially on low memory devices. I faced this on a typical guide viewpager with a several images. So i think a dynamic view creation/destroying or a FragmentPagerAdapter should are used in this case. developer.android.com/reference/android/support/v4/view/… also warns about that
  • Dori
    Dori almost 9 years
    you dont need to cast to ViewPager at all as you are dealing with the ViewGroup interface
  • themightyjon
    themightyjon almost 9 years
    @AmitGupta you're required to add the view to the container because you may not be returning the view here. instantiateItem must return an object associated with that view, which can be a different object if you like; it's a key. Whatever you return, you just make sure that your implementation of isViewFromObject can match the two up, key to view. However, the most common implementation is to just return the view as the key as well. FragmentPageAdapter handles all the key-to-view stuff for you and thus just asks you to create a fragment instead.
  • Iman Akbari
    Iman Akbari over 8 years
    Frankly nothing but this works for me. The OffscreenPageLimit trick that @Mikhail suggested is also a must do for this to work. thank you both.
  • ehehhh
    ehehhh over 8 years
    This almost worked for me, but I had to change onAttachedToWindow() to onFinishInflate().
  • htafoya
    htafoya about 8 years
    Not very good, you are saving all the created views. It would be better if it only saved the visible views. (similar to convertview in listview)
  • Aksiom
    Aksiom about 8 years
    you can use return collection.findViewById(resId); If you dont want to pass an activity instance
  • Volodymyr Kulyk
    Volodymyr Kulyk over 7 years
    Please add this code @Override public void destroyItem(ViewGroup container, int position, Object object) {}
  • Biraj Zalavadia
    Biraj Zalavadia over 7 years
    @Eftekhari Viewpager provides these two ways, you can choose any of them. Generally when there is only informative things and no user interaction you can use view else go with Fragments if there is so many user interactions and more code to manage.
  • Sabo
    Sabo over 7 years
    @Eftekhari It is easier without fragments and you write less code, which means that is easier to read and understand later on and less prone to bugs
  • Denny
    Denny over 7 years
    IMO, activities are easier to use
  • hemanth kumar
    hemanth kumar over 5 years
    @BirajZalavadia this was working when the compile sdk version is 15, but when it is set to 23, i get classcast exception saying the container cannot be cast to fragment. Could you tell me what can i change to make it work?
  • Biraj Zalavadia
    Biraj Zalavadia over 5 years
    @hemanthkumar you need to use latest(at least 23.x) supporting library/jar for viewpager.
  • Ahmed Elsayed
    Ahmed Elsayed about 3 years
    Please how to make the height of the viewPager is equal to the heighst item in Pager Adapter ?
  • Swathi
    Swathi over 2 years
    Here text color is given as white and parent has no background, so not able to see the output, thought it was not working. Just remove the text color you will see your view pager, it will work