Shared Element Transition + Fragment + RecyclerView + ViewPager - android

I am implementing a gallery app, which has a Fragment that holds a RecyclerView with images, onClick of an image I go to ViewPager to cycle through images.
For now, I am trying to implement just the entry animation like in this video. The problem is the animation just doesn't work, I am obviously missing something (just showing code which is relevant to transitions):
ViewPager:
public class ViewPagerFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_viewpager, container, false);
Transition transition = TransitionInflater.from(getContext()).inflateTransition(R.transition.fragment_transition);
setSharedElementEnterTransition(transition);
postponeEnterTransition();
return view;
}
GridAdapter:
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setPhotoImage(new File(mArrayOfPhotos.get(position).photo));
ViewCompat.setTransitionName(holder.photoImage, mPhotoObjects.get(position).photo);
}
#Override
public void onClick(View view) {
// pass an image view that is being clicked...
// ...via listener to MainActivity from where ViewPagerFragment is started
mListener.onImageSelected(photoImage, getAdapterPosition());
}
}
In MainActivity I instantiate ViewPagerFragment in onClick:
#Override
public void onImageSelected(View view, int clickedImagePosition) {
ViewPagerFragment fragment = new ViewPagerFragment();
Bundle bundle = new Bundle();
bundle.putInt(ViewPagerFragment.KEY_IMAGE_INDEX, clickedImagePosition);
fragment.setArguments(bundle);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// view is an image view that was clicked
fragmentTransaction.addSharedElement(view, ViewCompat.getTransitionName(view));
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.replace(R.id.fragment_placeholder, fragment, VIEWPAGER_FRAGMENT);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
And finally in ImageFragment(which is a single image in ViewPager) I have:
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
mFullImage.setTransitionName(transitionName);
Glide.with(getActivity())
.load(myImage)
.apply(new RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL))
.listener(new RequestListener<Drawable>() {
#Override
public boolean onLoadFailed(#Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
getParentFragment().startPostponedEnterTransition();
return false;
}
#Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
getParentFragment().startPostponedEnterTransition();
return false;
}
})
.into(mFullImage);
return view;
}
Transition name is not the problem, it is unique, and it is passed normally between all the fragments. I have followed this article and read many others, but it feels like a small bit is missing in my code.
EDIT:
I was able to localize the problem I think. The transition worked several times randomly, apparently, it happens because images are not ready when I am making the transition. I tried to call postponeEnterTransition() in MainActivity onCreate but still doesn't work. Images are passed to the ImageFragment through the Bundle. Still looking.
EDIT:
I believe I found the problem, I have a fragment, that has a TabLayout inside it, which has 2 fragments, one displaying all photos, and one displaying photos that are tagged as favorite. And they both use the same RecyclerView. The shared element transition works only if the photo is in one section at the same time, once I tag a photo as favorite it appears in all photos as well as favorite photos and the transition no longer works. It probably happens because the transition is no longer unique. Is there a way to solve this? Considering the fact that both fragments use the same RecyclerView, and the same adapter class.

I finally got it to work, the problem was in transition names. The transitions are also working with the support Transition library.
At first, the transition was not working at all, so I used a method that I found here to postpone the transition, it's a good link that explains transitions:
private void scheduleStartPostponedTransition(final View sharedElement) {
sharedElement.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
sharedElement.getViewTreeObserver().removeOnPreDrawListener(this);
getParentFragment().startPostponedEnterTransition();
return true;
}
});
}
In my case, I have nested fragments, a ViewPagerFragment that contains a ViewPager and manages the fragments (ImageFragment) using ImageAdapter. I call postponeEnterTransition() from the ViewPagerFragment and that is why I am using getParentFragment().
I used the method scheduleStartPostoponedTransition in Glide onLoadFailed and onResourceReady().
The main problem was in my implementation. I have a fragment that contains a TabLayout with 2 tabs, each containing a fragment with a RecyclerView. One fragment shows all photos and other shows favorite photos. The transitions were working fine (after some alteration that I mentioned above) if a photo was only in one tab, but as soon as I marked it as favorite, it wouldn't work in either tab. Which means that two tabs had a photo with the same transition name.
My solution was to pass an integer to the GridAdapter depending on which fragment was shown (1 for all photos and 2 for favorite photos) and set it as a transition name:
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setPhotoImage(new File(mArrayOfPhotos.get(position).photo));
ViewCompat.setTransitionName(holder.photoImage, mPhotoObjects.get(position).photo + fragmentNumber);
}
}
And then pass this number to the ImageFragment and set it there too. After that, it started to work because every photo now had a different transition name.
Also if you are using a support version of transitions make sure you have the support library version 27.0.0 or above, as they fixed the transitions in this version.

Related

PagerAdapter and ViewPager start a new fragment over the current visible view

I have a ViewPager with a PagerAdapter that is called from another fragment, a main fragment if you will.
My PagerAdapter is showing the user images and the user can swipe between them using ViewPager.
I've registered an onClick listener for every image and it seems to work as expected, at least it returns the correct position if I debug the position from instantiateItem.
The problem is that this onClick is supposed to start a new fragment, like an overlay over the image that the user clicked on but it doesn't start the fragment over the current image that the user clicked on for some reason.
Example of what I mean:
On page one onClick it starts the fragment at the correct position, (overlaying the current visible view).
On page two onClick it starts the fragment on page 1 (not in the visible view and not where the onClick fired).
On page three onClick it starts the fragment on page 2 (not in the visible view and not where the onClick fired).
Code:
MainFragment.java
public class MainFragment extends Fragment implements AdapterView.OnItemSelectedListener {
ViewPager viewPager;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
viewPager = (ViewPager) getView().findViewById(R.id.viewPager);
swipeAdapter = new SwipeAdapter(getActivity(), getContext(), stringFetchedName, stringFetchedUserID, stringFetchedUsername, stringFetchedRating, stringFetchedDescription, stringFetchedDate);
viewPager.setAdapter(swipeAdapter);
}
}
SwipeAdapter.java
public class SwipeAdapter extends PagerAdapter {
FragmentActivity activity;
Context context;
String names[], userid[], username[], rating[], description[], date[];
LayoutInflater layoutInflater;
ImageView imageView;
public View itemView;
public SwipeAdapter(FragmentActivity activity, Context context, String names[], String userid[], String username[], String rating[], String description[], String date[]) {
this.activity = activity;
this.context = context;
this.names = names;
this.userid = userid;
this.username = username;
this.rating = rating;
this.description = description;
this.date = date;
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return names.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((View) object);
}
#Override
public Object instantiateItem(final ViewGroup container, final int position) {
final View itemView = layoutInflater.inflate(R.layout.imageswap, container, false);
imageView = itemView.findViewById(R.id.imageView);
container.addView(itemView);
imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (itemView.findViewById(R.id.userText).isShown()) {
showImageOverlay(v);
} else {
hideImageOverlay(v);
}
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
public void showImageOverlay(View view) {
Fragment fragment = new ImageOverlayFragment().newInstance("0", "0");
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.imageSwap, fragment, IMAGE_OVERLAY);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
public void hideImageOverlay(View view) {
Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(IMAGE_OVERLAY);
if(fragment != null) {
activity.getSupportFragmentManager().beginTransaction().remove(fragment).commit();
}
}
}
How can I make sure that the new fragment ImageOverlayFragment I start is started on top of the image that was clicked, like how could I take the position from instantiateItem into account etc? or what should I do?
If I debug Log.d("debug", "Clicked position:" + position); inside the imageView OnClickListener it gives me the correct position but when I call showImageOverlay(v), it seems like it always starts the fragment on the view to the left of my currently visual view?
Note:
I'm passing a FragmentActivity to the SwipeAdapter from the MainFragment which is why I can access getSupportFragmentManager().
Update:
I tried to use FragmentStatePagerAdapter instead of PagerAdapter as suggested by SO and the result is still the same. The onClick event is sending the correct position and I can toggle the visibility of visible elements within the onClick event etc but I still cannot start the new fragment over the view of where the user clicked. The new fragment still starts to the left, and not in the view of where the user clicked. Problem still remains.
fragmentTransaction.replace(R.id.imageSwap, fragment, IMAGE_OVERLAY);
You are using your activity's TransactionManager to start you overlay fragment. In the above code, what I am guessing is that you are giving the ID of the container viewgroup of , parent of ImageView in its layout file. In a ViewAdapter not the currently visible fragment is initialized or view is inflated, but adjacent fragments/views also. That means your view hierarchy from activity root contains more than one layout with the ID you provide for fragment transaction. When activity searches its view hierarchy, it will return the first view it reaches with given ID. In your case, when you are not on the first page of adapter, this first matching view will be inside the leftmost page inside ViewPager. ViewPager - by default - uses one view on left and one view on right of the visible page in created state. That is why you see your fragment is instantiated on the page before the one you want it to be.
As a solution, you can consider using fragments inside Pager instead of plain views. Every fragment has a 'childFragmentManager' that u can use to start your new fragment upon click.
I hope this helps.
I had a similar issue with FragmentStatePagerAdapter, and what worked had much to do with two of the adapter methods:
instantiateItem - "Create the page for the given position."
setPrimaryItem - "Called to inform the adapter of which item is currently considered to be the "primary", that is the one show to the user as the current page."
Because of the way the adapter works, where it instantiates the fragments before they are displayed, the instantiateItem method is called for fragments or slides that are not in view (apart from the first item). If you can override setPrimaryItem, and then leave the UI/view set up in instantiateItem, and then the rest of the logic (listener with the show/hide overlay) in setPrimaryItem, you can make sure the listener and the fragment are set to the currently active view/fragment.
Let me know if that makes sense to you

How to display images in Android and change it by swipe right/left?

I want to add an introduction to my Android application, to inform the user about how the app works. This intro will be displayed only, if the preferred settings intro will be false. So in this intro, there will be 3 images and at the end, there will be a page, with some text and two buttons, to enable the user to access the application, by making a login. The change between each image, will be made with a swipe movement, (so right to left +, left to right -). How Can I do ?
This can be done via the use of Fragments and ViewPager and FragmentPagerAdapter. Look at this documentation:
FragmentPagerAdapter: http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html
ViewPager:
http://developer.android.com/reference/android/support/v4/view/ViewPager.html
You can have one fragment that is instantiated based on the id in the ViewPager, and that id will indicate which image to show in your image fragment. So for three images, you instantiate a new fragment that sets the image in the fragment based on the current page in the FragmentPagerAdapter. The second fragment can be one for the login buttons and text you want at the end.
Ex for adapter defined in your FragmentActivity (or AppCompatActivity)
public class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
if(position < 3)
return ImageFragment.newInstance(position);
else
return new LoginFragment();
}
}
Ex for the image fragment for the various images in your introduction:
public static class ImageFragment extends Fragment{
private int mPosition;
public ImageFragment(){
}
public static ImageFragment newInstance(int pos){
ImageFragment frag = new ImageFragment();
Bundle args = new Bundle();
args.putInt("pos", pos);
frag.setArguments(args);
return frag;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPosition = getArguments().getInt("pos");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_image, container, false);
ImageView backgroundView = (ImageView) v.findViewById(R.id.background_image);
switch(mPosition){
case 0:
//set background view image 1
case 1:
//set background view image 2
default:
//set background view image 3
}
return v;
}
}
I would recommend using a ViewPager. Check out this tutorial from the Developer Guide
http://developer.android.com/training/animation/screen-slide.html
If you want to add functionality to each of these pages instead of having just images then perhaps you can implement a fragmentStatePagerAdapter and then put all the functionality in each fragment. Here is a tutorial to implement one.
http://www.truiton.com/2013/05/android-fragmentstatepageradapter-example/
I think we can do it by using recycler view itself.
Using PagerSnapHelper layout manager in recycler view, we can implement swipe to change images.
recyclerView.setLayoutManager(new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, false));
// add pager behavior
PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

Dynamically Add and Remove Fragments From FragmentPagerAdapter

I have a FragmentPagerAdapter for a viewPager Which initially has only one Fragment in it. I want to dynamically add a new Fragment to the adapter when user swipes from right to left, and dynamically remove a Fragment when user swipes from left to right.I have tried to use this library https://github.com/commonsguy/cwac-pager but in that library we have an option to add and remove fragments on button clicks. I have tried to add a OnPageChangeListener to the viewpager but the callback methods ( onPageScrolled and onPageScrollStateChanged) are being called more than once which results in addition of more than one fragment to the FragmentPagerAdapter. So please shed some light on how to do this.
#dora: i think in your case FragmentStatePagerAdapter will help you. I have mentioned its use below as per my understanding.I hope it will help you in taking decision.
There are two ways to implement ViewPager:
• FragmentStatePagerAdapter
• FragmentPagerAdapter
FragmentStatePagerAdapter class consumes less memory, because it destroys fragments, as soon as they are not visible to user, keeping only saved state of that fragment
FragmentPagerAdapter: when there are less number of fragments. But using AndroidFragmentPagerAdapter for large number of fragments would result choppy and laggy UX.
Number of page hold by a viewPager?
The number of items that any ViewPager will keep hold of is set by the setOffscreenPageLimit() method. The default value for the offscreen page limit is 3. This means ViewPager will track the currently visible page, one to the left, and one to the right. The number of tracked pages is always centered around the currently visible page.
Please follow this link for code: http://www.truiton.com/2013/05/android-fragmentpageradapter-example/
I know this post is old, but I struggled to figure this out so I'll answer it anyway.
You want to use FragmentStatePagerAdapter and override getItemPosition(). Create a list of stuff you want to pass down to the fragment, call notifyDataSetChanged(), and you're all set!
Here's the adapter:
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
List<String> mKeyList = new ArrayList<>();
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return PlaceholderFragment.newInstance(mKeyList.get(position));
}
#Override
public int getCount() {
return mKeyList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return "SCOUT " + (getCount() - position);
}
public void add(int position, String key) {
mKeyList.add(position, key);
notifyDataSetChanged();
}
}
And here's the fragment:
public static class PlaceholderFragment extends Fragment {
private static final String ARG_SCOUT_KEY = "scout_key";
public static PlaceholderFragment newInstance(String key) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putString(ARG_SCOUT_KEY, key);
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.current_scout_fragment, container, false);
//getArguments().getString(ARG_SCOUT_KEY));
return rootView;
}
}
I want to dynamically add a new Fragment to the adapter when user swipes from right to left, and remove dynamically remove a Fragment when user swipes from left to right.
AFAIK, that will not be supported by any PagerAdadpter. It certainly will not be supported by ArrayPagerAdapter. The page needs to exist, otherwise you cannot swipe to it. You cannot swipe first, then add the page later.
Moreover, I have never found a use case for your proposed pattern that could not be handled by having the page be in the adapter, but not populating the page (i.e., whatever the expensive work is that you appear to be trying to avoid) until the swipe begins.

How can make my ViewPager load only one page at a time ie setOffscreenPageLimit(0);

I understand the lowest number I can give setOffscreenPageLimit(int) is 1. but I need to load one page at a time because memory problems.
Am i going to have to use the old style tabhost etc? or is there a way/hack I can make my viewPager load one page at a time?
My Adapter extends BaseAdapter with the ViewHolder patern.
I was having the same problem and I found the solution for it:
Steps:
1) First Download the CustomViewPager Class from this link.
2) Use that class as mentioned below:
In Java:
CustomViewPager mViewPager;
mViewPager = (CustomViewPager) findViewById(R.id.swipePager);
mViewPager.setOffscreenPageLimit(0);
In XML:
<com.yourpackagename.CustomViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swipePager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Now only one page will be loaded at once.
P.S: As per the question's requirement, I have posted the solution for Viewpager. I haven't tried the same with TabLayout yet. If I will find any solution for that I will update the answer.
In this file, KeyEventCompat is used it may not found by the android studio because KeyEnentCompat class was deprecated in API level 26.0.0 so you need to replace KeyEventCompat to event for more details you can view
https://developer.android.com/sdk/support_api_diff/26.0.0-alpha1/changes/android.support.v4.view.KeyEventCompat
As far as I know, that is not possible when using the ViewPager. At least not, when you want your pages to be swipe-able.
The explaination therefore is very simple:
When you swipe between two pages, there is a Point when both pages need to be visible, since you cannot swipe between two things when one of those does not even exist at that point.
See this question for more: ViewPager.setOffscreenPageLimit(0) doesn't work as expected
CommonsWare provided a good explaination in the comments of his answer.
but I need to load one page at a time because memory problems.
That presumes that you are getting OutOfMemoryErrors.
Am i going to have to use the old style tabhost etc?
Yes, or FragmentTabHost, or action bar tabs.
or is there a way/hack I can make my viewPager load one page at a time?
No, for the simple reason that ViewPager needs more than one page at a time for the sliding animation. You can see this by using a ViewPager and swiping.
Or, you can work on fixing your perceived memory problems. Assuming this app is the same one that you reported on earlier today, you are only using 7MB of heap space. That will only result in OutOfMemoryErrors if your remaining heap is highly fragmented. There are strategies for memory management (e.g., inBitmap on BitmapOptions for creating bitmaps from external sources) that help address such fragmentation concerns.
My Adapter extends BaseAdapter with the ViewHolder patern.
BaseAdapter is for use with AdapterView, not ViewPager.
I have an Answer for this. The above said method setUserVisibleHint() is deprecated and you can use setMaxLifecycle() method. For loading only the visible fragment you have to set the behaviour to BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT in the viewpager adapter. ie; in the Constructor. And for handling the fragment use onResume() method in the fragment.
In this way you can load only one fragment at a time in the viewpager.
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);
}
}
In Kotlin:
class MyAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT )
Also use with FragmentPagerAdapter (now deprecated) in same way
By using this method you can load one page at time in tab layout with view pager`
#Override
public void onResume() {
super.onResume();
if (getUserVisibleHint() && !isVisible) {
Log.e("~~onResume: ", "::onLatestResume");
//your code
}
isVisible = true;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && isVisible) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//your code
}
}, 500);
}
}
`
Override the setUserVisibleHint and add postDelayed like below in your every fragments.
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
if (isVisibleToUser)
Handler().postDelayed({
if (activity != null) {
// Do you stuff here
}
}, 200)
super.setUserVisibleHint(isVisibleToUser)
}
I can manage by this way and its working fine now for me.
First, copy in the SmartFragmentStatePagerAdapter.java which provides the intelligent caching of registered fragments within our ViewPager. It does so by overriding the instantiateItem() method and caching any created fragments internally. This solves the common problem of needing to access the current item within the ViewPager.
Now, we want to extend from SmartFragmentStatePagerAdapter copied above when declaring our adapter so we can take advantage of the better memory management of the state pager:
public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
// Sparse array to keep track of registered fragments in memory
private SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
public SmartFragmentStatePagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Register the fragment when the item is instantiated
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
// Unregister when the item is inactive
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
// Returns the fragment for the position (if instantiated)
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
}
// Extend from SmartFragmentStatePagerAdapter now instead for more dynamic ViewPager items
public static class MyPagerAdapter extends SmartFragmentStatePagerAdapter {
private static int NUM_ITEMS = 3;
public MyPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Returns total number of pages
#Override
public int getCount() {
return NUM_ITEMS;
}
// Returns the fragment to display for that page
#Override
public Fragment getItem(int position) {
switch (position) {
case 0: // Fragment # 0 - This will show FirstFragment
return FirstFragment.newInstance(0, "Page # 1");
case 1: // Fragment # 0 - This will show FirstFragment different title
return FirstFragment.newInstance(1, "Page # 2");
case 2: // Fragment # 1 - This will show SecondFragment
return SecondFragment.newInstance(2, "Page # 3");
default:
return null;
}
}
// Returns the page title for the top indicator
#Override
public CharSequence getPageTitle(int position) {
return "Page " + position;
}
}
You actually don't need a custom ViewPager.
I had the same issue and I did like this.
Keep the setOffscreenPageLimit() as 1.
Use fragment's onResume and onPause lifecycle methods.
Initialize and free-up memories on these lifecycle methods.
I know this is an old post, but I stumbled upon this issue and found a good fix if your loading fragments. Simply, check if the user is seeing the fragment or not by overriding the setUserVisibleHint(). After that load the data.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
getData(1, getBaseUrl(), getLink());
}
}

android viewpager inside another viewpager

I'm trying to have a viewpager with few views (let's say 5?) and in one of the views (let's say 3rd view?) I have to put another viewpager so after searching for a while, I've come up with some questions which I couldn't get the exact answer for my situtation.
How is it possible to implement a viewpager inside a fragment of another viewpager?
If it's possible to do the above task, how's the touch function will be handled for viewpagers ? Isn't changing views for the inner viewpager gonna affect the outer one?
I don't really need a code kinda solution but more like a concept kinda solution, so I'm not asking for anyone to do my coding, but I've been quiet away from android and I rather to hear the new concepts from the pros.
thanks in advance.
Edit:
Ok I managed to do it at some point but another problem I'm facing now.
here is the call to the fragment which contains my viewpager:
Fragment fragment = new Fragment();
switch(position)
{
case 0: fragment = new CalendarFragment();break;
case 1: fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, 2);
fragment.setArguments(args);break;
}
return fragment;
and this is the oncreate of calendarFragment class:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View cal = inflater.inflate(R.layout.calendar, container, false);
viewPager = (ViewPager) cal.findViewById(R.id.calendar_pager);
viewPager.setOnTouchListener( new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN && v instanceof ViewGroup) {
((ViewGroup) v).requestDisallowInterceptTouchEvent(true);
}
return false;
}
});
viewPager.setAdapter(createCalendarAdaptor());
viewPager.setCurrentItem(MONTHS_LIMIT / 2);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
//updateResetButtonState();
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
});
return cal;
}
and I get the calendar in the first fragment and it works just fine and I can easily scroll to next page, and then I can easily move back to the first page, but when I move to 3rd page and then I go back to first page which is my other viewpager it cause an error:
java.lang.IllegalStateException: Recursive entry to executePendingTransactions
I'm not quiet sure why is it happening, I've done some searching and it appears it's because I'm using a viewpager inside a fragment...
Any help would be appreciated.

Categories

Resources