I need to design a ViewPager which able to pass childs with fixed width (e.g childs with 700dp width), Unfortunately the current version of ViewPager will automatically makes all childrens width to MATCH_PARENT, is there any way to add this functionality to ViewPager?
My ViewPager layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/some_id"
android:layout_width="match_parent"
android:layout_height="300dp"
android:overScrollMode="never" />
</LinearLayout>
ViewPager childs layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/banner_main_layout_container"
android:layout_width="700dp"
android:layout_height="match_parent"
android:background="#fff"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="some images"/>
</LinearLayout>
Thanks in Advance...
It is possible to scale the pages within the ViewPager with FragmentPagerAdapter.getPageWidth. You will need a custom FragmentPagerAdapter. If you return a number between 0 and 1, the pages are scaled down, width > 1 scales pages up accordingly. But this is not really good, because you can't scroll the image within the up-scaled page.
If you wrap the ImageView in a HorizontalScrollView, things are a bit better, you can scroll the images within pages, but the swipe gesture between pages is caught by the HorizontalScrollView if you are not very fast. See this video.
So the solution is truly to use a custom HorizontalScrollView (see InterceptingHorizontalScrollView) which disallows intercepting the onTouch event, but also allows it when the User scrolls to the end (See overidden onOverScrolled). See this video or the image below for the difference.
EDIT You don't need to override onInterceptTouchEvent, because HorizontalScrollView intercepts them by default (so scrolling the image has higher priority than paging.)
Finally, here's all the code:
MainActivity.java
public class MainActivity extends Activity {
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set up the ViewPager
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setPageMargin(30);
mViewPager.setAdapter(new ImagePagerAdapter(getFragmentManager()));
}
private class ImagePagerAdapter extends FragmentPagerAdapter {
public ImagePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
switch(i) {
case 0:
return ImageFragment.newInstance(R.drawable.forest1);
case 1:
return ImageFragment.newInstance(R.drawable.forest2);
case 2:
return ImageFragment.newInstance(R.drawable.forest3);
default:
return ImageFragment.newInstance(R.drawable.ic_launcher);
}
}
#Override
public int getCount() {
return 3;
}
#Override
public float getPageWidth(int position)
{
// Here it is possible to scale page width
return super.getPageWidth(position);
}
}
}
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">
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
ImageFragment.java
public class ImageFragment extends Fragment {
private static final String ARG_PARAM1 = "image_resid";
private int mImageResId;
public static ImageFragment newInstance(int image_resid) {
ImageFragment fragment = new ImageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PARAM1, image_resid);
fragment.setArguments(args);
return fragment;
}
public ImageFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mImageResId = getArguments().getInt(ARG_PARAM1);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_image, container, false);
ImageView imageView = (ImageView)v.findViewById(R.id.imageView);
imageView.setImageResource(mImageResId);
return v;
}
}
fragment_image.xml
<com.gyebro.viewpagertest.InterceptingHorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="600dp"
android:layout_height="match_parent"
tools:context="com.gyebro.viewpagertest.ImageFragment">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:id="#+id/imageView"
android:src="#drawable/forest1" />
</com.gyebro.viewpagertest.InterceptingHorizontalScrollView>
InterceptingHorizontalScrollView.java
public class InterceptingHorizontalScrollView extends HorizontalScrollView {
public InterceptingHorizontalScrollView(Context context) {
super(context);
}
public InterceptingHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InterceptingHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/*#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (getParent() != null) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
}
return super.onInterceptTouchEvent(ev);
}*/
#Override
protected void onOverScrolled (int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX,scrollY,clampedX,clampedY);
// if clampedX == true, we've reached the end of the HorizontalScrollView so
// allow parent to intercept
if(clampedX) {
Log.d("InterceptingHorizontalScrollView", "Reached the end, allowing interception");
getParent().requestDisallowInterceptTouchEvent(false);
}
}
}
What you really want here is a HorizontalScrollView inside of a ViewPager. This requires custom touch handling, so you'll want to use something like this class: InterceptingHorizontalScrollView
To make InterceptingHorizontalScrollView work in a ViewPager, you'll have to override onOverScrolled:
#Override
protected void onOverScrolled (int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX,scrollY,clampedX,clampedY);
if(clampedX) {
getParent().requestDisallowInterceptTouchEvent(false);
}
}
Thanks to Gyebro for this tip.^
Your ViewPager child layout would look like this:
<com.tumblr.widget.InterceptingHorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="#+id/banner_main_layout_container"
android:layout_width="700dp"
android:layout_height="match_parent"
android:background="#fff"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="some images"/>
</LinearLayout>
</com.tumblr.widget.InterceptingHorizontalScrollView>
You can either override PagerAdapter´s getWidth method and if this does not help, look at this:
http://commonsware.com/blog/2012/08/20/multiple-view-viewpager-options.html
and most importantly try this example, it works great!
Just go along the whole example.
The ViewPager children will/should always match it's parent width.
Furthermore it sounds like a bad idea to use a 700dp width ImageView. What would that look like in portrait mode?
If you don't want to make the ViewPager itself smaller, i.e. you want the ImageViews to be swiped from the absolute side of the screen, you have to make the items appear smaller.
That imitation could be done by creating 2 additional LinearLayouts to act as spacers. Then it would appear as if your item has a specific width.
Here's an example (with a TextView):
<LinearLayout 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"
android:orientation="horizontal">
<LinearLayout
android:background="#FFF"
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<TextView
android:layout_weight="10"
android:background="#333"
android:textColor="#FFF"
android:textSize="50sp"
android:gravity="center"
android:text="HELLO"
android:layout_width="0dp"
android:layout_height="match_parent" />
<LinearLayout
android:background="#FFF"
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</LinearLayout>
And that would look like this:
Set the page margin of the view pager to a negative value. This will force the pages to push into the the view pager. Be warned, it will also cause overlap so you'll see part of the next/previous element in the view pager.
Related
I am able to implement TabLayout with ViewPager nicely but when the content inside the ViewPager is being scrolled (the deceleration animation), I cannot swipe left and right to change to other fragments inside ViewPager. I have to wait until the scrolling animation stops and then swipe.
Below is some of my code snippets.
I have a simple activity_main.xml layout like below.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbarHome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"/>
<LinearLayout
android:id="#+id/mainContainerLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
</LinearLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/btmNavView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
app:menu="#menu/menu_main" />
</LinearLayout>
I then inflate a fragment containing TabLayout and ViewPager into the LinearLayout in the middle of the activity_main.xml. This layout can be seen below:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.pchatanan.sontana.custom_views.AppTabLayout
android:id="#+id/contactTabLayout"
app:tabMode="fixed"
android:layout_height="wrap_content"
android:layout_width="match_parent"
style="#style/AppTabLayout"/>
<android.support.v4.view.ViewPager
android:id="#+id/contactViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
The logic inside my fragment is below:
fragmentArray = new Fragment[]{new SimpleFragment(), new SimpleFragment(), new SimpleFragment()};
titleArray = new String[]{"fragment1", "fragment2", "fragment3"};
contactPagerAdapter = new AppPagerAdapter(getChildFragmentManager(), fragmentArray, titleArray);
contactViewPager.setAdapter(contactPagerAdapter);
contactViewPager.setOffscreenPageLimit(fragmentArray.length - 1);
contactTabLayout.setupWithViewPager(contactViewPager);
Use this instead defualt ViewPager.
Detect when ScrollView or RecyclerView is scrolling. Then call
viewPager.setPagingEnabled(pagingEnable);
If pagingEnable
public class CustomViewPager extends ViewPager {
private boolean isPagingEnabled = true;
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent event) {
return this.isPagingEnabled && super.onTouchEvent(event);
}
public boolean onInterceptTouchEvent(MotionEvent event) {
return this.isPagingEnabled && super.onInterceptTouchEvent(event);
}
public void setPagingEnabled(boolean b) {
this.isPagingEnabled = b;
}
}
Update:
You can do reverse to like you can disable ScrollView scroll when ViewPager is swiping
mScrollView.requestDisallowInterceptTouchEvent(true);
Similar to what suggested by #Khemraj, the problem is the touch is not intercepted by ViewPager. It is only intercepted by the ScrollView. To fix this, we should allow ViewPage to intercept the touch when scrolling:
scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View view, int i, int i1, int i2, int i3) {
viewPager.requestDisallowInterceptTouchEvent(false);
}
});
A better implementation is to make use of CallBackListener onto the fragment containing the scrollView.
I am building an Android app that needs a "Card Deck" UI widget.
This is a request for an activity/layout example that can do the following:
1) Vertical Swipe Support:
List a deck of cards that can be vertically scrolled/swiped. StackView does this, but I'm open to any solution that works well (e.g., some CardUI project)
2) Horizontal Swipe Support:
For any card, there are 2 dictionary definitions:
If we do a left swipe - then we can see definition A.
If we do a right swipe, we see definition B
Horizontal swipe update does not update the entire screen layout, just the card that was swiped. So if I swipe Card2 to the right to see A2, I still have Card1 behind A2
Example:
[A1][Card1][B1]
[A2][Card2][B2]
[A3][Card3][B3]
I did see this related post here , the answers there provide some hints and reference info.. but unfortunately, I am still trying to figure it out.
You have two possible approaches: take some open source project and adapt it to your needs, or otherwise, build your card swipe as a image slider from a detailed tutorial.
For the first option, have a look in Github, where you will find several small projects that do deck of cards with features usually on vertical or horizontal scrolling. I guess you may find interesting the following projects:
CardDeck: Deck of Cards for Android
DeckPicker: A complete android anki droid project with some additional feature of Card Preview within browser screen. In Preview screen card will be shown like review mode on card browser window.
In the end if the additional changes you made looks nice, it can be worth to submit a patch to the original project.
For the second alternative, for the case you prefer to implement it from scratch, then take simple steps, growing your project into more specific/complex details customizing it at your will. A fullscreen image slider would fit the bill, which would comprise the activity for the view page:
activity_fullscreen_view.xml
<?xml version="1.0" encoding="utf-8"?>
<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="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
And a Java class with a full screen viewer:
public class FullScreenImageAdapter extends PagerAdapter {
private Activity _activity;
private ArrayList<String> _imagePaths;
private LayoutInflater inflater;
// constructor
public FullScreenImageAdapter(Activity activity,
ArrayList<String> imagePaths) {
this._activity = activity;
this._imagePaths = imagePaths;
}
#Override
public int getCount() {
return this._imagePaths.size();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((RelativeLayout) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imgDisplay;
Button btnClose;
inflater = (LayoutInflater) _activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View viewLayout = inflater.inflate(R.layout.layout_fullscreen_image, container,
false);
imgDisplay = (ImageView) viewLayout.findViewById(R.id.imgDisplay);
btnClose = (Button) viewLayout.findViewById(R.id.btnClose);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(_imagePaths.get(position), options);
imgDisplay.setImageBitmap(bitmap);
// close button click event
btnClose.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
_activity.finish();
}
});
((ViewPager) container).addView(viewLayout);
return viewLayout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((RelativeLayout) object);
}
}
Then you achieve the image sliding horizontally, such as:
And to add vertical motion, just include additional vertical layout:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Swipe Demo"
android:gravity="center"
android:layout_margin="10dip" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:layout_margin="10dip"
android:id="#+id/image_place_holder"/>
</LinearLayout>
</LinearLayout>
Which will allow you to slide things vertically:
At the end of the day, what you want to have is a grid, such as vertical and horizontal scrolling together. For that you have to combine both vertical and horizontal sliding into a table layout:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent">
<TableLayout
android:id="#+id/amortization"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TableRow
android:background="#ffff00">
<TextView
android:text="#string/amortization_1"
android:padding="3dip"/>
<TextView
android:text="#string/amortization_2"
android:padding="3dip"/>
<TextView
android:text="#string/amortization_3"
android:padding="3dip"/>
<TextView
android:text="#string/amortization_4"
android:padding="3dip"/>
<TextView
android:text="#string/amortization_5"
android:padding="3dip"/>
<TextView
android:text="#string/amortization_6"
android:padding="3dip"/>
<TextView
android:text="#string/amortization_7"
android:padding="3dip"/>
</TableRow>
</TableLayout>
</HorizontalScrollView>
</ScrollView>
Taking such steps and combining it, will allow you to achieve the effect you want.
Try this sample code-
public class Main extends Activity {
int position=0;
LinearLayout full;
Intent intent;
public Integer[] images= {
R.drawable.image1, R.drawable.image2,
R.drawable.image3, R.drawable.image4,
R.drawable.image5, R.drawable.image6
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
full = (LinearLayout) findViewById(R.id.full);
changeImage();
ActivitySwipeDetector activitySwipeDetector = new ActivitySwipeDetector(this);
full.setOnTouchListener(activitySwipeDetector);
}
private void changeImage(){
full.setBackgroundResource(images[position]);
}
public class ActivitySwipeDetector implements View.OnTouchListener {
static final String logTag = "ActivitySwipeDetector";
static final int MIN_DISTANCE = 100;
private float downX, upX;
Activity activity;
public ActivitySwipeDetector(Activity activity){
this.activity = activity;
}
public void onRightToLeftSwipe(){
Log.i(logTag, "RightToLeftSwipe!");
if(position < images.length - 1){
position++;
changeImage();
}
}
public void onLeftToRightSwipe(){
Log.i(logTag, "LeftToRightSwipe!");
if(position > 0){
position--;
changeImage();
}
}
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN: {
downX = event.getX();
return true;
}
case MotionEvent.ACTION_UP: {
upX = event.getX();
float deltaX = downX - upX;
// swipe horizontal?
if(Math.abs(deltaX) > MIN_DISTANCE){
// left or right
if(deltaX < 0) { this.onLeftToRightSwipe(); return true; }
if(deltaX > 0) { this.onRightToLeftSwipe(); return true; }
}
else {
Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE);
return false; // We don't consume the event
}
return true;
}
}
return false;
}
}
#Override
protected void onPause() {
super.onPause();
}
#Override
public void onResume()
{
super.onResume();
}
#Override
protected void onStop() {
super.onStop();
}
#Override
public void onBackPressed() {
super.onBackPressed();
}
}
Xml-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/full"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:orientation="vertical" />
UPDATED : Can I use the following layout to implement 3 textviews in Viewpager :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="view 1"/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="view 2"/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="view 3"/>
</android.support.v4.view.ViewPager>
</LinearLayout>
I want to implement the ViewPager for these 3 views. and i want to have viewpager and those 3 views in single xml file. Each page contains each textview. I have seen some examples but each page was implement using separate xml layout file.
How can I implement the viewpager for these 3 views in a single xml file. If possible please provide me a sample code or 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(View 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 == ((View) arg1);
}
}
// Set the ViewPager adapter
WizardPagerAdapter adapter = new WizardPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);
My question is... Some Guru here can teach me if is possible that the ViewPager read the children from XML and auto build the pages without use the instantiateItem()?
Update
This answer will show optimised and managed way to set ViewPager pages inside layout.
Step 1
First make two pages Layouts XML layout_page_1.xml and layout_page_2.xml.
Step 2
Now include all pages layout in your parent layout ViewPager.
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/page_one"
layout="#layout/layout_page_1" />
<include
android:id="#+id/page_two"
layout="#layout/layout_page_2" />
</androidx.viewpager.widget.ViewPager>
Step 3
Now from your code, set adapter in simple steps
// find views by id
ViewPager viewPager = findViewById(R.id.viewpager);
CommonPagerAdapter adapter = new CommonPagerAdapter();
// insert page ids
adapter.insertViewId(R.id.page_one);
adapter.insertViewId(R.id.page_two);
// attach adapter to viewpager
viewPager.setAdapter(adapter);
That's All! You need only a common adapter for all ViewPagers.
CommonPagerAdapter.java class
public class CommonPagerAdapter extends PagerAdapter {
private List<Integer> pageIds = new ArrayList<>();
public void insertViewId(#IdRes int pageId) {
pageIds.add(pageId);
}
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
return container.findViewById(pageIds.get(position));
}
#Override
public void destroyItem(#NonNull ViewGroup container, int position, #NonNull Object object) {
container.removeView((View) object);
}
#Override
public int getCount() {
return pageIds.size();
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object object) {
return view == object;
}
}
Important Note
It is better to use Fragments instead of Views. You should create Fragments and use FragmentStatePagerAdapter to maintain stack, lifecycle and states. You can see #this answer for complete code of using FragmentStatePagerAdapter.
thats impossible .you can do it like these
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
then create seperate xml file and do it like this.for eg:i am gonna use the xml file name is ios_frag.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="50sp"/>
</LinearLayout>
Just if anyone faces the same problem, I've solved it with an adapter which extracts the page views from an arbitrary viewgroup that you pass to it.
package com.packagename;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public class ViewGroupPagerAdapter extends PagerAdapter {
public ViewGroupPagerAdapter(ViewGroup viewGroup) {
while (viewGroup.getChildCount() > 0) {
views.add(viewGroup.getChildAt(0));
viewGroup.removeViewAt(0);
}
}
private List<View> views = new ArrayList<View>();
#Override
public Object instantiateItem(ViewGroup parent, int position) {
View view = views.get(position);
ViewPager.LayoutParams lp = new ViewPager.LayoutParams();
lp.width = ViewPager.LayoutParams.FILL_PARENT;
lp.height = ViewPager.LayoutParams.FILL_PARENT;
view.setLayoutParams(lp);
parent.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup parent, int position, Object object) {
View view = (View) object;
parent.removeView(view);
}
#Override
public int getCount() {
return views.size();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
Usage:
ViewPager viewPager=findViewById(...); // Retrieve the view pager
viewPager.setAdapter(new ViewGroupPagerAdapter(findViewById(R.id.id_of_your_view_group_where_you_store_the_pages)));
You can subclass ViewPager and override onInflate.
Moreover, you will get a preview of the first ViewPager page in the Layout Editor.
All views are kept in memory so for performance reasons you should only use this with a small number of pages.
#Override protected void onFinishInflate() {
int childCount = getChildCount();
final List<View> pages = new ArrayList<>(childCount);
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
Class<?> clazz = child.getClass();
if (clazz.getAnnotation(DecorView.class) == null) {
pages.add(child);
}
}
for (View page : pages) {
removeView(page);
}
setAdapter(new PagerAdapter() {
#Override public int getCount() {
return pages.size();
}
#Override public Object instantiateItem(ViewGroup container, int position) {
container.addView(pages.get(position));
return position;
}
#Override public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(pages.get((Integer)object));
}
#Override public boolean isViewFromObject(View view, Object object) {
return pages.get((Integer)object) == view;
}
});
super.onFinishInflate();
}
viewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
viewPager.setAdapter(new MyPagerAdapter(rootView));
viewPager.setOffscreenPageLimit(3); //or whatever you like
it caches it and shows perfectly.
How can I implement the viewpager for these 3 views in a single xml file.
Sorry, but that is not possible.
add this in your xml layout file
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
create 3 Fragments and add each TextView for every fragment. Then create an adapter that subclassed PagerAdapter
A good example can be found here
Simply : add a ViewPager in your file and put inside your 3 TextView...
After trying out the Gallery and Horizontal Scroll View, I found that the View Pager does what I need but with one minor thing missing. Can the View Pager have multiple views per page?
I know that View Pager shows only 1 view/page per swipe. I was wondering if I can limit my views width so my 2nd view following it will show?
For example: I have 3 views and I want the screen to show view 1 and part of view 2 so the user knows there is more content so they can swipe to view 2.
|view 1|view 2|view 3|
|screen |
I discovered that a perhaps even simpler solution through specifying a negative margin for the ViewPager. I've created the MultiViewPager project on GitHub, which you may want to take a look at:
https://github.com/Pixplicity/MultiViewPager
Although MultiViewPager expects a child view for specifying the dimension, the principle revolves around setting the page margin:
ViewPager.setPageMargin(
getResources().getDimensionPixelOffset(R.dimen.viewpager_margin));
I then specified this dimension in my dimens.xml:
<dimen name="viewpager_margin">-64dp</dimen>
To compensate for overlapping pages, each page's content view has the opposite margin:
android:layout_marginLeft="#dimen/viewpager_margin_fix"
android:layout_marginRight="#dimen/viewpager_margin_fix"
Again in dimens.xml:
<dimen name="viewpager_margin_fix">32dp</dimen>
(Note that the viewpager_margin_fix dimension is half that of the absolute viewpager_margin dimension.)
We implemented this in the Dutch newspaper app De Telegraaf Krant:
Mark Murphy has an interesting blog post addressing precisely this problem. Although I ended up using my own solution in this thread, it's worthwhile looking at Dave Smith's code, which Mark references in the blog post:
https://gist.github.com/8cbe094bb7a783e37ad1/
Warning! Before you take this approach, beware of some very serious issues with this approach, mentioned both at the end of this post and in the comments below.
You'll end up with this:
It effectively works by wrapping a ViewPager into a subclass of FrameLayout, setting it to a specific size, and calling setClipChildren(false). This inhibits Android from clipping the views that exceed beyond the boundaries of the ViewPager, and visually accomplishes what you want.
In XML, it's very simple:
<com.example.pagercontainer.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#CCC">
<android.support.v4.view.ViewPager
android:layout_width="150dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal" />
</com.example.pagercontainer.PagerContainer>
Add in a little code for handling touch events from outside of the ViewPager and invalidating the display when scrolling, and you're done.
That being said, and while this works great in general, I did notice that there is an edge-case that isn't solved with this fairly simple construction: when calling setCurrentPage() on the ViewPager. The only way I could find to resolve this was by subclassing ViewPager itself and having its invalidate() function also invalidate the PagerContainer.
It is possible to show more than one page on the same screen.
One of the ways is by overriding the getPageWidth() method in the PAgerAdapter. getPageWidth() returns a float number between 0 and 1 indicating how much width of the Viewpager should the page occupy. By default it is set to 1. So, you can change this to the width you wish.
You can read more about this here & github project.
This is how I got it:
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
android:clipToPadding="false"
android:gravity="center"
android:paddingLeft="36dp"
android:paddingRight="36dp"/>
and in activity,i use this :
markPager.setPageMargin(64);
hope it helps!
I had the same problem with the only difference that i needed to show 3 pages at once (previous, current and next pages). After a really long research for the best solution i think i found it.
The solution is a mix of few of the answers here:
As #Paul Lammertsma's answer pointed out - Dave Smith's code in Mark Murphy's blog is the basis for the solution. The only problem for me was that the ViewPager was only on the top part of the screen due to the size they give it in the xml file:
android:layout_width="150dp"
android:layout_height="100dp"
Which wasn't good for my purpose since i was looking for something that will spread all over the screen. So i changed it to wrap the content as you can see here:
<com.example.nutrino_assignment.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CCC">
<android.support.v4.view.ViewPager
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</com.example.nutrino_assignment.PagerContainer>
Now I lost all the effect of what the tutorial was trying to do. Using #andro's answer i was able to show more then 1 page at a time: exactly 2! The current and the next.
Did so by overriding as follow:
#Override
public float getPageWidth(int position) {
return(0.9f);
}
That was almost what i needed... (even though i think its enough for what you were asking), but for others who might need something like what i was needed:
For the last part of the solution i used the idea in this answer, again by #Paul Lammertsma.
In Dave Smith's code you will find in the onCreate method this line:
//A little space between pages
pager.setPageMargin(15);
which i replaced with:
//A little space between pages
pager.setPageMargin(-64);
now on the first page looks:
|view 1|view 2|view 3|
|screen |
while on the 2nd it looks like:
|view 1|view 2|view 3|
|screen |
Hope it will help someone! I wasted like 2 days on it...
Good luck.
viewPager.setPageMargin(-18);// adjust accordingly ,-means less gap
in imageadapter
private class ImagePagerAdapter2 extends PagerAdapter {
private int[] mImages = new int[] {
R.drawable.add1,
R.drawable.add3,
R.drawable.add4,
R.drawable.add2,
};
#Override
public float getPageWidth(int position) {
return .3f;
}
adjust return value...lesser means more image......0.3 means atleast 3 images at a time.
LayoutParams lp = new LayoutParams(width,height);
viewpager.setLayoutParams(lp);
In xml file using this code(Main Activity)
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="130dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="vertical"
android:weightSum="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="130dp">
<com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="fill_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="100dip"
android:layout_height="100dip"/>
</com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer>
</RelativeLayout>
</LinearLayout>
Main activity xml file add this code
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="130dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="vertical"
android:weightSum="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="130dp">
<com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer
android:id="#+id/pager_container"
android:layout_width="match_parent"
android:layout_height="fill_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="100dip"
android:layout_height="100dip"/>
</com.wonderla.wonderla.muthootpathanamthitta.activity_muthootpathanm.PagerContainer>
</RelativeLayout>
</LinearLayout>
Main Activity code
public class MainActivity extends Activity{
final Integer[] XMEN2= {R.mipmap.bookticket,R.mipmap.safty,R.mipmap.privacy};
private ArrayList<Integer> XMENArray2 = new ArrayList<Integer>();
PagerContainer mContainer;
int currentPage2 = 0;
private static int NUM_PAGES2 = 0;
ViewPager mPager2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initData2();}
private void initViews() {
mPager2 = (ViewPager)findViewById(R.id.viewpager);
mContainer = (PagerContainer)findViewById(R.id.pager_container);
mPager2.setOffscreenPageLimit(5);
mPager2.setPageMargin(15);
mPager2.setClipChildren(false);
}
private void initData2() {
for(int i=0;i<XMEN2.length;i++)
XMENArray2.add(XMEN2[i]);
mPager2.setAdapter(new Sliding_Adaptertwo(getActivity(),XMENArray2));
NUM_PAGES2 =XMEN2.length;
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage2 == NUM_PAGES2) {
currentPage2= 0;
}mPager2.setCurrentItem(currentPage2++, true);
}
};
Timer swipeTimer = new Timer();
swipeTimer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(Update);
}
}, 3000, 3000);
}
}
Pager View pagercontainer class
import android.content.Context;
import android.graphics.Point;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
public class PagerContainer extends FrameLayout implements ViewPager.OnPageChangeListener {
private ViewPager mPager;
boolean mNeedsRedraw = false;
public PagerContainer(Context context) {
super(context);
init();
}
public PagerContainer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PagerContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
//Disable clipping of children so non-selected pages are visible
setClipChildren(false);
//Child clipping doesn't work with hardware acceleration in Android 3.x/4.x
//You need to set this value here if using hardware acceleration in an
// application targeted at these releases.
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
try {
mPager = (ViewPager) getChildAt(0);
mPager.setOnPageChangeListener(this);
} catch (Exception e) {
throw new IllegalStateException("The root child of PagerContainer must be a ViewPager");
}
}
public ViewPager getViewPager() {
return mPager;
}
private Point mCenter = new Point();
private Point mInitialTouch = new Point();
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenter.x = w / 2;
mCenter.y = h / 2;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
//We capture any touches not already handled by the ViewPager
// to implement scrolling from a touch outside the pager bounds.
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialTouch.x = (int)ev.getX();
mInitialTouch.y = (int)ev.getY();
default:
ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
break;
}
return mPager.dispatchTouchEvent(ev);
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Force the container to redraw on scrolling.
//Without this the outer pages render initially and then stay static
if (mNeedsRedraw) invalidate();
}
#Override
public void onPageSelected(int position) { }
#Override
public void onPageScrollStateChanged(int state) {
mNeedsRedraw = (state != ViewPager.SCROLL_STATE_IDLE);
}
}
and its Adapter
public class Sliding_Adaptertwo extends PagerAdapter {
private ArrayList<Integer> IMAGES;
private LayoutInflater inflater;
private Context context;
public Sliding_Adaptertwo(Context context, ArrayList<Integer> IMAGES) {
this.context = context;
this.IMAGES=IMAGES;
inflater = LayoutInflater.from(context);
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public int getCount() {
return IMAGES.size();
}
#Override
public Object instantiateItem(ViewGroup view, int position) {
View imageLayout = inflater.inflate(R.layout.sliding_layout, view, false);
assert imageLayout != null;
final ImageView imageView = (ImageView) imageLayout
.findViewById(R.id.image);
imageView.setImageResource(IMAGES.get(position));
view.addView(imageLayout, 0);
return imageLayout;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
#Override
public Parcelable saveState() {
return null;
}
}
xml file of adapter class
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="fill_parent"
>
<ImageView
android:id="#+id/image"
android:layout_width="90dp"
android:layout_height="90dp"
android:adjustViewBounds="true"
android:layout_gravity="center"
android:scaleType="fitXY"
android:src="#drawable/ad1"
/>
</FrameLayout>
it works fine
I want to create some scroll view using Horizontal View Pager. Left view must has full screen width, but right only a quarter of width (it will be a vertical panel like in Dolphin browser). It's possible to do that? I changed android:layout_width in right layout, but it didn't work.
My code:
public class TestActivity extends FragmentActivity {
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_view);
MyPagerAdapter adapter = new MyPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.panelPager);
pager.setAdapter(adapter);
pager.setCurrentItem(0);
}
}
main_view.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/panelPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
MyPagerAdapter.java
public class MyPagerAdapter extends PagerAdapter {
#Override
public int getCount() {
return 2;
}
#Override
public Object instantiateItem(final View collection, final int position) {
LayoutInflater inflater =
(LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
int resId = 0;
switch (position) {
case 0:
resId = R.layout.left;
break;
case 1:
resId = R.layout.right;
break;
}
View view = inflater.inflate(resId, null);
((ViewPager) collection).addView(view, 0);
return view;
}
#Override
public void destroyItem(final View arg0, final int arg1, final Object arg2) {
((ViewPager) arg0).removeView((View) arg2);
}
#Override
public boolean isViewFromObject(final View arg0, final Object arg1) {
return arg0 == ((View) arg1);
}
left.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="LEFT" />
</LinearLayout>
right.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="50dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#color/light_blue" >
<TextView
android:id="#+id/textView2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="RIGHT"/>
</LinearLayout>
Check Murphy's answer on this question. You need to override PagerAdapter's getPageWidth() method on your PagerAdapter class, like this for example:
#Override
public float getPageWidth(int page) {
if(page==0) {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return (float)LEFT_FRAGMENT_PIXEL_WIDTH / size.x;
}
else
return super.getPageWidth(page);
}
Looking at the source for ViewPager, this isn't something it's designed to do; it uses its own width to calculate scroll distances etc, with the clear assumption that all children will have the same width as the ViewPager itself.
For your specific case there may be a hacky workaround, though. You can specify a margin between adjacent pages, and this margin can be negative. This may give the result you want, provided the Z-ordering of the ViewPager's children is appropriate. Give it a try, and see whether it does what you need.
Adrian is exactly right. ViewPager isn't designed to show a portion of the next post as a preview/teaser but it can do it. In my ViewPagerCursorAdapter extends PagerAdapter class I run this:
public Object instantiateItem(View collection, final int position) {
cursor.moveToPosition(position);
View newView = getPageView(cursor, collection);
if (cursor.getCount() > 1) {
((ViewPager)collection).setPageMargin(-overlapMargin);
if (! cursor.isLast()) { newView.setPadding(0, 0, overlapMargin, 0); }
}
((ViewPager) collection).addView(newView);
return newView; //returns the object reference as the tag for identification.
}
You will run into a strange z overlapping issue. The trick is to either apply the background only to ViewPager's background or to apply it to the view inside the newView you just set the padding for. Looks good and works great.