I have been looking for this answer a while, and i haven't found any good solution which would help me getting started in creating this. This is what i would like to do. Like we have view types in lists and we can show different layouts on different position in lists using those view types, i would like to do that also with viewpager. I'm showing some images from server in viewpager, so user can swipe them, but i would like to show user between those images on every 5 image ad. How can i accomplish that?
This is how my viewpager adapter looks:
private class JokesImagesAdapter extends FragmentPagerAdapter {
public JokesImagesAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
JokePageFragment fragment = new JokePageFragment();
fragment.setArguments(jokes.get(position).toBundle());
return fragment;
}
#Override
public int getCount() {
return jokes.size();
}
}
And i guess that probably i need to create one more fragment and call it here with implementation of ads.
What kind of ads you want to show in your app - Interstitial or Native ad?
If you want to just show an interstitial ad on every 5 swipes - just attach an OnPageChangeListener to your ViewPager and count the swipes.
If you want to show a native ad, you have to create a new fragment (as you thought) which will be showing the ad. You have to count and return the ad fragment in the getItem() method of your adapter. Another way to handle this is to use one fragment which will be notified whether to show or not an ad view or a normal view.
What I believe is that you should modify your jokes model a little, which could accept objects instead of just images(and your custom parameters) and then cast the object to the required datatype and then while populating jokes, you should add the ad object every 5th position, that would do the work.
Related
I'm using a ViewPager2 in my Android app to let the user scroll left and right through (currently) 2 pages. It seems fine, except for one little detail.
I'm using setOffscreenPageLimit(2) because I want both pages to be ready for scrolling straight away. However, it doesn't seem to do anything. When I scroll from page 1 to page 2 for the first time, the second page's layout doesn't appear until it's fully selected (i.e. the transition animation is finished). This looks quite jarring, and defeats the point of smooth scrolling with a ViewPager2 at all.
After page 2 loads for the first time, scrolling back and forth works properly with both pages being retained allowing a smooth transition.
Can anyone replicate this? This used to work just fine with ViewPager.
Here's how I set up my ViewPager2:
_viewPager2FragmentHolder = getActivity().findViewById(R.id.viewPager2TargetFragmentHolder);
_viewPager2FragmentHolder.setOffscreenPageLimit(2);
_viewPager2FragmentHolder.setAdapter(new TargetFragmentStateAdapter(this));
...and here's my adapter class:
public class TargetFragmentStateAdapter extends FragmentStateAdapter
{
public TargetFragmentStateAdapter(#NonNull Fragment fragment)
{
super(fragment);
}
#NonNull
#Override
public Fragment createFragment(int position)
{
switch (position)
{
case 0:
return new TargetCoordinatesFragment();
case 1:
return new TargetDirectionsFragment();
default:
return new Fragment();
}
}
#Override
public int getItemCount()
{
return 2;
}
}
From the official documentation: https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2#setOffscreenPageLimit(int)
Set the number of pages that should be retained to either side of the currently visible page(s). Pages beyond this limit will be recreated from the adapter when needed.
Therefore, you should set it to 1 to get what you want.
If offscreen pages seems to be 'loaded' only when selected, it means either the adapter or the pageSelectionCallback is doing somethiong wrong.
I have created pages in Android by using PagerAdapter. When I tried to get value in the third page from EditText, I got NullPointerException.
In the third fragment page, I have initialled that EditText already.
iName = (EditText) view.findViewById(R.id.name);
So does this problem occur while the third page havenĀ“t initialled?
A ViewPager, by default, keeps one fragment on each side in memory. You can change this by setting the offscreen page limit.
From the documentation:
Set the number of pages that should be retained to either side of the
current page in the view hierarchy in an idle state. Pages beyond this
limit will be recreated from the adapter when needed.
This is offered as an optimization. If you know in advance the number
of pages you will need to support or have lazy-loading mechanisms in
place on your pages, tweaking this setting can have benefits in
perceived smoothness of paging animations and interaction. If you have
a small number of pages (3-4) that you can keep active all at once,
less time will be spent in layout for newly created view subtrees as
the user pages back and forth.
You should keep this limit low, especially if your pages have complex
layouts. This setting defaults to 1.
Parameters limit How many pages will be kept offscreen in an idle
state.
Example usage: yourViewPager.setOffscreenPageLimit(2);
When you use the default implementation of setOffscreenPageLimit() it is only loading the one fragment which is to the right of it. For eg. when you are on index 1, it has index 2 loaded in memory but not index 0, so swiping left will have to generate a new fragment from scratch. To test this theory you might want to use setOffscreenPageLimit(2) and then try swiping index 1->0. This in no way is the optimal solution but will help clear out your doubts about the concept.
you can store the fragment state of all the pages in the pages adaper like this and get reference of it.
PagerAdapter
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragmentsList;
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment=new ScreenSlidePageFragment();
fragmentsList.add(fragment, position);
return fragment;
}
#Override
public int getCount() {
return NUM_PAGES;
}
public static List<Fragment> getFragmentsList(){
return fragmentsList;
}
}
In Activity:
List<Fragment> fragList=adapter.getFragmentsList()
View view = fragList.get(position).getView();
if (view !=null) {
view.findViewById(R.id.text_view).setText("Child Accessed :D");
}
I play arround with the SlidingTabsColors example from the android developers site. Where can I add a different content fragment? Now all tabs have the same fragment/layout. I tried to copy all important for the content fragment and renamed it, changed the fragment and the layout. But it dont works this way. Probably the ArrayList is not the best for different content?
I am not sure whether I got the question right but the fact is that there are fragments created in
SlidingTabsColorsFragment.java by the following method:
Fragment createFragment() {
return ContentFragment.newInstance(mTitle, mIndicatorColor, mDividerColor);
}
So I would suggest to simply change that method to something like this:
Fragment createFragment() {
return MyVeryOwnFragment.newInstance(mTitle, mIndicatorColor, mDividerColor);
}
Because there can only be Fragments displayed whose were created beforehand in this method. Then your own Fragment will be displayed in the content area.
You may also want to watch this video on the topic by the android developers youtube channel.
-------- Edit --------
Ok so the question was how to insert multiple different fragments:
Fragment createFragment() {
// Decide based on a class member which Fragment should be created.
Fragment frament;
if (mIndicatorColor == Color.Red) {
fragment = new MyRedFragment(mTitle, mDividerColor);
} else if (mIndicatorColor == Color.Blue) {
fragment = new MyBlueFragment(mTitle, mDividerColor);
}
return fragment;
}
You may want to introduce a different memeber than color. That could be an enum field.
I have a ViewPager that I shows more than one item. I used the solution of putting multiple items in one fragment and I created an Adapter that calculates how many items I can put per fragment based in the width of the screen.
The getItem of my FragmentPagerAdapter creates a range of items for each fragment, dividing the quantity of items for each fragment.
For example, I have 12 items and in the portrait orientation, I can put 3 items. The getItem will create fragments with the range of 0-2, 3-5, 6-8 and 9-11. In the landscape orientation, since the width of the screen is bigger, I can put, for example, 5 items. So, the ranges would be 0-4, 5-8 and 9-11.
With this requirement, I need to create new Fragments and destroy the old ones on orientation changes.
I created a solution, but it depends on the method isChangingConfigurations() of the Activity. But this method just exists for API level 11 and above. So I can't use it.
Basically, I I'm not allowing the Fragment to save its state and I'm removing it in the onPause if the configuration is changing. But since I don't have this method in old android versions, I need another solution.
Can anyone help me?
Another solution is:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
if (pagerAdapter != null) {
pagerAdapter.removeAllfragments();
}
super.onSaveInstanceState(savedInstanceState);
}
And the code for adapter:
public void removeAllfragments()
{
if ( mFragmentList != null ) {
for ( Fragment fragment : mFragmentList ) {
mFm.beginTransaction().remove(fragment).commit();
}
mFragmentList.clear();
notifyDataSetChanged();
}
}
mFragmentList should add fragments inside of:
#Override
public Fragment getItem(int position) {}
The easiest solution to your current approach is probably to ensure that getItem(int) returns different values for the landscape and portrait orientations. The id is used to generate the fragment tag, that, after an orientation change, is used to retrieve a detached fragment and reattach it. With different ids for the situation where respectively 3 items and 5 items are next to each other, a fragment with 3 items will never be reattached if there should be 5.
By default, getItemId(int) simply returns the supplied position of the element, like so:
public long getItemId(int position) {
return position;
}
In order to return different ids for your situations, you have various options. A straightforward one would be to do something smart with the indices of all items displayed for the given position. Alternatively, you could do a simple device orientation check and offset the id with the total number of items, or do something smart with a string and a hashcode etc. Just make sure you return the same id for the same fragment in the same orientation.
I have got a ViewPager which is populated by a FragmentPagerAdapter. I want to change from the first adapter two another. The problem is that all pages which were loaded before (while having the first adapter) are still the old ones.
I looked at the source code of FragmentPagerAdapter and guess the problem occurs because of the instatiateItem() implementation. Using the tag, which has position and conatiner id in it, the method checks if there is already a Fragment at the position. When there is a Fragment with this tag it is attached. The container id and position do not change when setting a new adapter so it finds the old Fragments.
Do you know a way to remove all old fragments?
I realized that the Fragment tag uses an id, obtained by getItemId() and not the position. To solve my issue I have overwritten getItemId() and use totally different ids in the different FragmentPagerAdapters.
#Override
public long getItemId(int position) {
return mPagerId*100+position;
}
mPagerId is an unique integer that is assigned in the constructor. If you have more than 100 pages you should replace 100 with 1000.