Can I use view pager with views (not with fragments)
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?
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.