To move elements in Fragment when user scroll the page - android

I have ViewPager with 3 Fragments. I just want to know is it possible to move elements (ImageView, TextView, e.t.c) in Fragment when user start to scroll the page and go to another page? What the best way to do that? Here below my code that I used before. I used it in my MainActivity. Only after test I understand that I am on the wrong way cause these code will work every time when user scroll. So is it possible to set it in Fragment?
My code that i used before in MainActivity:
#Override
public void transformPage(View page, float position) {
// transformation here
int pageWidth = page.getWidth();
ImageView mImageViewCloud = (ImageView) getView().findViewById(R.id.cloud);
ImageView mImageButterfly = (ImageView) getView().findViewById(R.id.butterfly);
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
page.setAlpha(0);
} else if (position <= 1) { // [-1,1]
if(mImageViewCloud!=null)mImageViewCloud.setTranslationX((float) (-(1 - position) * 0.5 * pageWidth));
if(mImageButterfly!=null)mImageButterfly.setTranslationX((float) ((1 - position) * 1.5 * pageWidth));
// The 0.5, 1.5, 1.7 values you see here are what makes the view move in a different speed.
// The bigger the number, the faster the view will translate.
// The result float is preceded by a minus because the views travel in the opposite direction of the movement.
} else { // (1,+Infinity]
// This page is way off-screen to the right.
page.setAlpha(0);
}
}
Any help is appreciated. Thanks!
EDIT:
AdapterFragment.java
public abstract class AdapterFragment extends Fragment {
SparseArray<AdapterFragment> registeredFragments = new SparseArray<AdapterFragment>();
#Override
public Object instantiateItem(ViewGroup container, int position) {
AdapterFragment fragment = (AdapterFragment) 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 void pass(int farFarAway) {
for(int i = 0, nSize = registeredFragments.size(); i < nSize; i++)
registeredFragments.valueAt(i).pass(farFarAway);
}
final int pageWidth = ViewPager.getMeasuredWidth(); //after ViewPager drawn, probably its also screen width
final int scrollRange = (AdapterFragment.getCount()-1)*pageWidth;
ViewPager.OnPageChangeListener OPCL = new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
float farFarAway=(float)(position*pageWidth+positionOffsetPixels), scrollToFactor = farFarAway/scrollRange;
AdapterFragment.pass(farFarAway);
}
#Override
public void onPageScrollStateChanged(int state) {
}
};
}

have you tried ViewPager.OnPageChangeListener? there is a method in this interface onPageScrolled(int position, float positionOffset, int positionOffsetPixels), last two variables might be useful for your purposes - pass move offset to all Fragments, inside of them move Views
edit:
final int pageWidth=mainViewPager.getMeasuredWidth(); //after ViewPager drawn, probably its also screen width
final int scrollRange = (viewPagerCustomAdapter.getCount()-1)*pageWidth;
OnPageChangeListener onpcl = new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
float farFarAway=(float)(position*pageWidth+positionOffsetPixels),
scrollToFactor = farFarAway/scrollRange;
viewPagerCustomAdapter.pass(farFarAway);
}
#Override
public void onPageScrollStateChanged(int state) {
}
};
viewPagerCustomAdapter is an adapter dispatching farFarAway - how much distance ViewPager is moved starting left edge of 0 position Fragment. how to dispatch moved value to currently existing Fragments? - check this and create for loop over all registered fragments inside adapter's pass(int farFarAway). inside each Fragment you may calculate proper value for setTranslationX. you may also pass and use scrollToFactor (0.0-1.0) and position, these might be useful. e.g. position might determine if you are on current or next Fragment. note that default ViewPager memory is set to one, so there can't be more than 3 registered Fragments at once (current+both next)
edit 2:
create new class and extend all your Fragments used in your adapter
public abstract class AdapterFragment extends Fragment{
public abstract pass(int farFarAway);
}
add these lines to your adapter:
SparseArray<AdapterFragment> registeredFragments = new SparseArray<AdapterFragment>();
#Override
public Object instantiateItem(ViewGroup container, int position) {
AdapterFragment fragment = (AdapterFragment) 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 void pass(int farFarAway) {
for(int i = 0, nsize = registeredFragments.size(); i < nsize; i++)
registeredFragments.valueAt(i).pass(farFarAway);
}
set above OnPageChangeListener for your ViewPager. In this way every Fragment gets current scroll position. so an example of use in one of your Fragments
public void pass(int farFarAway){
imageView.setTranslationX(farFarAway/10); //sample value 10, farFarAway is too big value for moving any View
}
your ImageView should move when while swiping ViewPager, adjust as you want. This whole post is almost full solution, should be useful...
PS. code not tested, might be some syntax or other bugs

Try reading about Fragment shared elements. Perhaps this is what you are looking for.

Related

Change RecyclerView data with Fragment

I am working on a project with TabLayout.I have 20 tabs. I use one RecyclerView Adapter and Fragment. When I click any tabs I want recyclerview and fragment to change data. How can I do that in TabLayout's getItem function? Thanks.
I think you want to add an OnPageChangeListener on your ViewPager. You will be able to detect when you change your active tab.
I would suggest something like this :
private int lastPosition = -1;
viewPager.addOnPageChangeListener(new OnPageChangeListener() {
public void onPageScrollStateChanged(int state) {}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
public void onPageSelected(int position) {
if(position != lastPosition){
lastPosition = position;
// refresh recyclerview and fragment
}
}
});

Editing the current page on view pager

I am working on an viewpager with previous and next page. What I need is to increase the text of the current page, after loading the page. But while I am trying, text on the next page is changing (I know that next and previous page load on viewpager). How can I get the focused on one selected page?
What I have done is,
viewPager.setClipChildren(false);
unfortunately it doesn't work..
I recommed using position parameter in activity and you can update text based on the position recieved.
This example is for Fragments though
#Override
public Fragment getItem(int position) {
return PageFragment.newInstance(position);
}
And in fragment you recieve this position as an argument:
public static PageFragment newInstance(int position) {
Bundle args = new Bundle();
args.putInt("position", position);
PageFragment page = new PageFragment();
page.setArguments(args);
return page;
}
In onCreateView you update your textview based on your position.
If you want to update TextView something like this:
TextView tv = (TextView) view.findViewById(R.id.yourId);
int position = getArguments().getInt("position");
tv.setText("This is page number: " + position);
i didn't really understand what you need but i think you may use this:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
//Edit
if(position==int requiredPosition)
{
// resize your text
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});

Android ViewPager - How to bring current page to the top?

I have a ViewPager/FragmentPagerAdapter combo with a negative page margin so that the pages slightly overlap each other. Problem is, the center page is not on top. The page to the left of it obscures it. I would like it so that the center page is always displayed on top. Is there an elegant way to do this?
The z-order of the pages are determined by the order of their creation, so the latter page will be resting on top of the page created earlier (refer first image below).
In normal case, this shouldn't be any problem as the pages are arrange side-by-side with no overlapping region, so the z-order doesn't really matters. However, in the case of multiple visible page with negative margin(overlapping), the selected page might be blocked partially by the page beside it.
One way to overcome this is to keep a reference of the page during instantiation and use it to change it's z-order with view.bringToFront().
To put this in code:
public class MyActivity extends Activity {
private ViewPager vp = null;
private SparseArray<View> viewCollection = new SparseArray<View>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
vp = (ViewPager)findViewById(R.id.viewpager);
vp.setPageMargin(-20);
vp.setOffscreenPageLimit(3);
vp.setOnPageChangeListener(new ViewPager.OnPageChangeListener(){
#Override
public void onPageSelected(int pos) {
View view = viewCollection.get(pos); // ### Get target page reference
view.bringToFront(); // ### change z-order to the top
}
});
vp.setAdapter(new MyAdapter());
}
public class MyAdapter extends PagerAdapter {
#Override
public Object instantiateItem(ViewGroup container, int position) {
View pageLayout = getLayoutInflater().inflate(R.layout.page_layout, container, false);
final Integer pageId = position;
TextView pageText = (TextView)pageLayout.findViewById(R.id.page_id);
pageText.setText(String.valueOf(position));
pageText.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
vp.setCurrentItem(pageId); // ### select the clicked page
}
});
// paint gray shades onto the page background
pageLayout.setBackgroundColor(Color.argb(255, position*40, position*40, position*40));
viewCollection.put(position, pageLayout); // ### store the reference
container.addView(pageLayout);
return(pageLayout);
}
#Override
public int getCount() {
return 8; // any arbitrary number
}
#Override
public float getPageWidth(int position) {
return(0.33f);
}
#Override
public boolean isViewFromObject(View view, Object obj) {
return (view == obj);
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
viewCollection.remove(position); // ### remove the reference
}
}
}

Create an infinite ViewPager scrolling in one direction

I am a newbie in Android programming and this is my first question on this site. I have spent lots of time searching for this issue but haven't find an answer that suit my case.
I had two pages in ViewPager, one for the current, one for the next page. When the second fragment shows, I set its index to 0, remove the first one and create a new fragment as a replacement for the second. Here is my code.
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int pageSelected) {
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 1) {
currentQuestFr = nextQuestFr;
nextQuestFr = newQuest();
mPager.setCurrentItem(0, false);
mPagerAdapter.notifyDataSetChanged();
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
And the Adapter
private class QuestPagerAdapter extends FragmentStatePagerAdapter {
public QuestPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
if (position == 0) return currentQuestFr;
else return nextQuestFr;
}
#Override
public int getCount() {
return NUM_PAGES;
}
#Override
public int getItemPosition(Object object)
{
return POSITION_NONE;
}
}
The code works well, but notifyDataSetChanged() seems to refresh my View with no need of that. It annoys users a little. Is there any way to refresh the Adapter without redraw the view, or other way to reach the same purpose?
Thanks very much
Do you want it to work in a way that it should load all data initially and then manually redraw the view on some event? Then you can use
mPager.setOffscreenPageLimit(n)
where n is your number of pages in mPager. This will draw all the pages on loading.

Circular ViewPager which uses FragmentPagerAdapter

I would like to implement a ViewPager which uses Fragments and can be swiped in a curcular motion e.g. Page (A<-->B<-->C<-->A).
I have read a couple of posts on how this is done, e.g. returning a fake count of how many elements there are and setting the position at the start in the middle.
how to create circular viewpager?
These all seem to be based of a PagerAdapter. When I try to do a similar thing while extending FragmentPagerAdapter, as soon as I return a fakeCount of pages I get an exception when I Swipe through my Fragments, I only have 2 Fragments.
Exception: java.lang.IllegalStateException: Can't change tag of fragment.
I think this is caused as the FragmentManager thinks I am in position 2 but position 2 points to the fragment at position 0. Does anyone know how I can avoid this? I am thinking I should experiment with extending Fragmentmanager. Any examples or help with this would be greatly appreciated.
I know it is a bit late but this is how it worked for me:
I needed a circular swipe between 3 fragments, so I made those 3 and two more virtual to help me implement the page looping:
public static class FirstViewFragment extends Fragment {
// Empty Constructor
public FirstViewFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_1, container, false);
}
}
public static class SecondViewFragment extends Fragment {
// Empty Constructor
public SecondViewFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_2, container, false);
}
}
public static class ThirdViewFragment extends Fragment {
// Empty Constructor
public ThirdViewFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_3, container, false);
}
}
And two more virtual fragments that enabled me to swipe left from the first and right from the last. The first virtual inflates the same layout as the last actual and the last virtual the same layout as the first actual:
public static class StartVirtualFragment extends Fragment {
public StartVirtualFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_3, container, false);
}
}
public static class EndVirtualFragment extends Fragment {
public EndVirtualFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_landing_1, container, false);
}
}
My Adapter:
private class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
switch (i) {
case 0:
return new StartVirtualFragment();
case 1:
if (firstViewFragment == null) {
firstViewFragment = new FirstViewFragment();
}
return firstViewFragment;
case 2:
if (secondViewFragment == null) {
secondViewFragment = new SecondViewFragment();
}
return secondViewFragment;
case 3:
if (thirdViewFragment == null) {
thirdViewFragment = new ThirdViewFragment();
}
return thirdViewFragment;
case 4:
return new EndVirtualFragment();
}
return null;
}
#Override
public int getCount() {
return 5;
}
}
And my page listener I used the onPageScrollStateChanged to set the correct page and implement the loop:
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_DRAGGING) {
int pageCount = viewPager.getChildCount();
int currentItem = viewPager.getCurrentItem();
if (currentItem == 0) {
viewPager.setCurrentItem(pageCount - 2, false);
} else if (currentItem == pageCount - 1) {
viewPager.setCurrentItem(1, false);
}
}
}
});
And in the end:
viewPager.setCurrentItem(1);
Hope I helped
I have a project in the GitHub with some widgets I've created. Here it its:
https://github.com/CyberEagle/AndroidWidgets
In the following package, there are the adapters to be used with the CircularViewPager:
https://github.com/CyberEagle/AndroidWidgets/tree/master/src/main/java/br/com/cybereagle/androidwidgets/adapter
First, you will use CircularViewPager instead of ViewPager in your layout. The CircularViewPager is here: https://github.com/CyberEagle/AndroidWidgets/blob/master/src/main/java/br/com/cybereagle/androidwidgets/view/CircularViewPager.java
This ViewPager expects a WrapperCircularPagerAdapter, instead of a PagerAdapter. This wrapper is used to trick the ViewPager, making it to think there are a lot of items in the ViewPager, but it actually repeat your items to make the circular effect. So, instead of implementing either PagerAdapter, FragmentPagerAdapter or FragmentStatePagerAdapter, you will implement either CircularFragmentPagerAdapter, CircularFragmentStatePagerAdapter or CircularPagerAdapter. Then, you will wrap your adapter with the WrapperCircularPagerAdapter and set the wrapper in the CircularViewPager, instead of your adapter. Also, when it's time to notify dataset changed, you will call the notifyDatasetChanged() in the wrapper.
When implementing one of the circular adapter, you will notice that instead of implementing instantiateItem, you will have to implement instantiateVirtualItem. For the fragment's pager adapter, you will implement getVirtualItem instead of getItem. That is because I've created the concept of virtual items.
To make it clear, imagine a view pager with 4 items, giving that each item represents a music. When you go all the way to left, you will see the 4th item in the left of the first. Actually, it's a whole new item, but it's linked to the virtual item that represents the 4th music.
Another example: imagine there's only one music now. You will see the same music on the left and on the right. There're 3 items at a time, but only one virtual item.
So, as explained, the Wrapper is tricking the ViewPager, making it think that there are a lot of items. To make it more difficult for the user to reach one of the ends of the ViewPager (it'd take a long time anyway), everytime a change happens to the dataset, the ViewPager goes to the same virtual item, but to one of the real items near the middle.
One more important thing is that the CircularViewPager has the method setCurrentVirtualItem. This method calculates which real item is the nearest desired virtual item and then it uses the setCurrentItem to set it. You have also the option to use the getCurrentVirtualItem, that will return the index of the current virtual item. Notice that if you use getCurrentItem, you'll get a large index.
Well, this is it. I'm sorry for the lack of documentation of the project. I'm planning document it soon. I'm also planning to remove the need for the wrapper. Feel free to copy the code (respecting the Apache 2.0 license), to fork or even contribute to it.
**If you want to make 3 views visible at same time and make it circular**
public abstract class CircularPagerAdapter extends PagerAdapter{
private int count;
int[] pagePositionArray;
public static final int EXTRA_ITEM_EACH_SIDE = 2;
private ViewPager.OnPageChangeListener pageChangeListener;
private ViewPager viewPager;
public CircularPagerAdapter(final ViewPager pager, int originalCount ) {
super();
this.viewPager = pager;
count = originalCount + 2*EXTRA_ITEM_EACH_SIDE;
pager.setOffscreenPageLimit(count-2);
pagePositionArray = new int[count];
for (int i = 0; i < originalCount; i++) {
pagePositionArray[i + EXTRA_ITEM_EACH_SIDE] = i;
}
pagePositionArray[0] = originalCount - 2;
pagePositionArray[1] = originalCount -1;
pagePositionArray[count - 2] = 0;
pagePositionArray[count - 1] = 1;
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageSelected(final int position) {
if(pageChangeListener != null)
{
pageChangeListener.onPageSelected(pagePositionArray[position]);
}
pager.post(new Runnable() {
#Override
public void run() {
if (position == 1){
pager.setCurrentItem(count-3,false);
} else if (position == count-2){
pager.setCurrentItem(2,false);
}
}
});
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(pageChangeListener != null)
{
pageChangeListener.onPageScrolled(pagePositionArray[position],positionOffset,positionOffsetPixels);
}
}
public void onPageScrollStateChanged(int state) {
if(pageChangeListener != null)
{
pageChangeListener.onPageScrollStateChanged(state);
}
}
});
}
#Override
public int getCount() {
return count;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return false;
}
public abstract Object customInstantiateItem(ViewGroup container, int position);
public void setPageChangeListener(ViewPager.OnPageChangeListener pageChangeListener)
{
this.pageChangeListener = pageChangeListener;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
int pageId = pagePositionArray[position];
return customInstantiateItem(container,pageId);
}
#Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
public void setFirstItem()
{
viewPager.setCurrentItem(EXTRA_ITEM_EACH_SIDE - 1);
}
}

Categories

Resources