I came across a strange issue in my project.
I have implemented infinite viewpager adapter using FragmentStatePagerAdapter
Code for same is:
public class DashBoardAdapter extends FragmentStatePagerAdapter {
ArrayList<AbstractFragment> fragments;
private int MAXFRAGMENTS=0;
public DashBoardAdapter(FragmentManager fm, ArrayList<AbstractFragment> fragments) {
super(fm);
this.fragments = fragments;
MAXFRAGMENTS=fragments.size();
}
#Override
public AbstractFragment getItem(int position) {
return fragments.get(position%MAXFRAGMENTS);
}
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
#Override
public CharSequence getPageTitle(int position) {
return fragments.get(position%MAXFRAGMENTS).getTitle();
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
int virtualPosition = position % MAXFRAGMENTS;
return super.instantiateItem(container, virtualPosition);
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
int virtualPosition = position % MAXFRAGMENTS;
super.destroyItem(container, virtualPosition, object);
}
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
MAXFRAGMENTS = fragments.size();
}
}
And the Main Activitycode(Function only) is something like this:
public void initializeViewPager()
{
verticalViewPager = (VerticalViewPager) findViewById(R.id.verticalviewpager);
enabledPages=new ArrayList<>();
enabledPages.addAll(getEnabledPages(false));
adapter=new DashBoardAdapter(getSupportFragmentManager(),enabledPages);
verticalViewPager.setAdapter(adapter);
verticalViewPager.setPageTransformer(true, new PageTransformer());
verticalViewPager.setCurrentItem(999+position);
verticalViewPager.setOffscreenPageLimit(1);
verticalViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(adapter!=null)
title.setText(adapter.getPageTitle(position));
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
private ArrayList<AbstractFragment> getEnabledPages(boolean a)
{
ArrayList<AbstractFragment> fragments=new ArrayList<>();
fragments.add(FragmentAttendance.newInstance(fragments.size()));
fragments.add(FragmentCompensation.newInstance(fragments.size()));
if(a)
fragments.add(FragmentTimeOff.newInstance(fragments.size()));
if(a)
fragments.add(FragmentCalendar.newInstance(fragments.size()));
if(a)
fragments.add(FragmentPeople.newInstance(fragments.size()));
if(a)
fragments.add(FragmentProvisions.newInstance(fragments.size()));
position = fragments.size()-(999+position)%fragments.size();
return fragments;
}
Layout of activity contains just a Viewpager.
And the Fragments, I have used is just copy of below fragment with different names
public class FragmentAttendance extends AbstractFragment {
public static final String ARG_SECTION_NUMBER = "section_number";
String title ="Testing";
public static FragmentAttendance newInstance(int sectionNumber) {
FragmentAttendance fragment = new FragmentAttendance();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public FragmentAttendance() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.card_attendance, container, false);
return rootView;
}
#Override
public String getTitle() {
return title;
}
}
Layout of fragment has just LinearLayout with a TextView.
The code is running superb if I am adding fragments more than three fragments. Howsoever as soon as the number of added fragments gets below four. My code Works in strange behavior.
Some of errors are Fragment already initialised
Other are Forward scrolling shows fragement View Howsoever when I scroll backward No view appears.
The working code can be tested by passing true to getEnabledPages(true);
And the non working code can be tested by passing false to getEnabledPages(false);
Please someone help me in this strange behaviour.
Related
Good morning,
I have a Fragment which its layout has a ViewPager, I provide a custom PageAdapter to show two child layouts. One layout is to fill with personal data and another layout is to fill with Address data by the user. On my fragment I want to retrieve both layouts data and fill a object Pessoa(Person) with an attribute Endereco (Address). So far my ViewPager is working and both layout are displayed, however when I try to get the fields on both layouts, either on onCreateView or onResume fragment's method, using :
viewpager.getChildAt(0).findViewById(R.id.txt1)
I got an NullPointerException, but if I add an OnPageChangeListener this works, I not sure if it's the best approach. Is There a best place to retrieve a reference to the viewpager child's layout?
Ps. I don't want to use two other Fragments in order to display each of the layouts.
PageAdapter
public class ViewPagerCadastroPessoaAdapter extends PagerAdapter {
private Context context;
private int[] views = {R.layout.layout_cadastro_dados_pessoais,R.layout.layout_cadastro_endereco };
public ViewPagerCadastroPessoaAdapter(Context context) {
this.context = context;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = LayoutInflater.from(this.context);
View layout = inflater.inflate(views[position], container, false);
container.addView(layout, position);
return layout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0:
return "Dados Pessoais";
case 1:
return "Endereço";
}
return super.getPageTitle(position);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}}
ViewPager's holder Fragment
public class CadastroPessoaFragment extends Fragment {
private ViewPager viewPagerCadastroPessoa;
private TabLayout tabLayoutCadastroPessoa;
public CadastroPessoaFragment() {
}
public static CadastroPessoaFragment newInstance() {
return new CadastroPessoaFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_cadastro_pessoa, container, false);
tabLayoutCadastroPessoa = root.findViewById(R.id.tabLayoutCadastroPessoa);
viewPagerCadastroPessoa = root.findViewById(R.id.viewPagerCadastroPessoa);
viewPagerCadastroPessoa.setOffscreenPageLimit(2);
viewPagerCadastroPessoa.setAdapter(new ViewPagerCadastroPessoaAdapter(getContext()));
tabLayoutCadastroPessoa.setupWithViewPager(viewPagerCadastroPessoa);
int cor = ContextCompat.getColor(getContext(), android.R.color.white);
tabLayoutCadastroPessoa.setTabTextColors(cor, cor);
//get a null pointer
viewPagerCadastroPessoa.getChildAt(0).findViewById(R.id.txt1);
viewPagerCadastroPessoa.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
//works fine
viewPagerCadastroPessoa.getChildAt(0).findViewById(R.id.txt1);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
return root;
}
#Override
public void onResume() {
super.onResume();
//get a null pointer
viewPagerCadastroPessoa.getChildAt(0).findViewById(R.id.txt1);
}
}
Thank you all in advance
UPDATE
According with
Anton Malyshev answer
The way Viewpager creates its pages is kind of "unsynchronized", so I used the following approach to solve my problem:
On my custom adapter I definied an interface with a single method onViewPagerReady(), as my Viewpager will always have only two pages, inside instantiateItem I check when position is 1 (once position starts with 0), if position == 1 I call the method onViewPagerReady that my fragment viewpager's holder implements, so inside this method I retrieve the layout of viewpager's children
public class ViewPagerCadastroPessoaAdapter extends PagerAdapter {
public interface ViewPagerObserver{
void onViewPagerReady();
}
private Context context;
private int[] views = {R.layout.layout_cadastro_dados_pessoais,R.layout.layout_cadastro_endereco };
private ViewPagerObserver observer;
public ViewPagerCadastroPessoaAdapter(Context context, ViewPagerObserver observer) {
this.context = context;
this.observer = observer;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = LayoutInflater.from(this.context);
View layout = inflater.inflate(views[position], container, false);
container.addView(layout, position);
if(position == 1){
observer.onViewPagerReady();
}
return layout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0:
return "Dados Pessoais";
case 1:
return "Endereço";
}
return super.getPageTitle(position);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
Fragment Viewpager's Holder
#Override
public void onViewPagerReady() {
View rootCadastroDadosPessoais = viewPagerCadastroPessoa.getChildAt(0);
View rootCadastroEnderecos =viewPagerCadastroPessoa.getChildAt(1);
}
Thank you all.
Just wait until viewPagerCadastroPessoa.getChildCount() will be greater than 0. The pages in ViewPager are not created yet in onResume (they are created asynchronously).
I have a simple FragmentPagerAdapter :
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
pagerAdapterList = new ArrayList<>();
super(fm);
}
#Override
public Fragment getItem(int position) {
PagerAdapter fragment;
if (position > pagerAdapterList.size() - 1) {
fragment = new PagerAdapter(followRequests.get(position));
pagerAdapterList.add(fragment);
} else
return pagerAdapterList.get(position);
return fragment;
}
public int removeView (ViewPager pager, int position)
{
pager.setAdapter (null);
pagerAdapterList.remove (position);
data.remove (position);
pager.setAdapter (this);
return position;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
getItem(position);
return super.instantiateItem(container, position);
}
#Override
public int getCount() {
return data.size();
}
}
and PagerAdapter class:
public class PagerAdapter extends Fragment {
Data data;
private View overlayView;
public PagerAdapter() {
}
public PagerAdapter(Data data) {
this.data= data;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.notification_header_fragment_pager, container, false);
TextView id_txtView = (TextView)
overlayView = (View) rootView.findViewById(R.id.viewOverlay);
//bind some data for example id_txtView.setText(data.getText());
return rootView;
}
public void showOverlayView() {
if(overlayView != null)
overlayView.setVisibility(View.VISIBLE);
}
}
first I call
removeView(mViewPager, position);
then I get new data, and then call
adapter.notifyDataSetChanged();
All things work fine and I can bind new data in PagerAdapter and show it on View Pager.
But when I call
((PagerAdapter) adapter.getItem(mViewPager.getCurrentItem())).showOverlayView();
I get current fragment without any reference to its ui components(like overlayView) and they are null !!
what's my wrong?
Please help me, Thanks.
I want to get the hidden value which is set in Fragment Page in order to work on it. I am setting the hidden value in Fragment Page and want to get that hidden value in the activity page's Viewpager onPageSelected value in oreder to update the fragment with detail information. Is this possible?
My Fragment page
public class CountryFragment extends Fragment {
public static final String STARTUP_ID = "startupID";
private String st_id;
public CountryFragment() {
// emtpy
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String st_id = getArguments().getString(STARTUP_ID);
this.st_id = st_id;
View v = inflater.inflate(R.layout.country_fragment, container, false);
TextView hiddenstatupid= (TextView) v.findViewById(R.id.hiddenstatuptextview);
hiddenstatupid.setText(st_id);
return v;
}
}
Activity Page
public class IndividualPage extends ActionBarActivity{
ViewPager pager;
#Override
public void onCreate (Bundle savedInstanceState)
{
pager = (ViewPager)findViewById(R.id.viewpager);
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
//here is where i need to get the hidden id of startup which is in the countryfragment
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
My CountryPageAdapter
public class CountryPageAdapter extends FragmentPagerAdapter {
public CountryPageAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
mFragmentTags = new HashMap<Integer, String>();
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
}
Iy your CountryFragment part of the viewpager? If so, then you can have a local store of your fragments in your pager adapter and then get the fragment from the viewpager adapter using the position.
If your adapter extends FragmentStatePagerAdapter you have to implement the getItem() method.
You will have something like this:
#Override
public void onPageSelected(int position) {
CountryFragment frag = pager.getAdapter().getItem(position);
}
Hope this helps.
On state loss or screen rotation and when my boolean flag is true, I want to recreate my screen. However, the fragment manager still has the previous fragment attached and it gets loaded.
What can I do to get rid of that fragment?
public class ViewPagerAdapter extends FragmentPagerAdapter {
private String[] mData;
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
mData= new String[0];
}
#Override
public Fragment getItem(int position) {
Bundle bundle = new Bundle();
bundle.putInt("key", position);
Fragment fragment = new MyFragment();
fragment.setArguments(bundle);
return fragment;
}
#Override
public int getCount() {
return mData.length;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
return mData[position];
}
public void setData(String[] data) {
mData= data;
}
}
Piece of code in Activity onCreate()
mViewPagerAdapter = new ViewPagerAdapter(getFragmentManager());
mViewPagerAdapter.setData(data);
viewPager.setAdapter(mViewPagerAdapter);
You may override instantiateItem() to detect screen rotation like a Fragment can.
Code example:
#Override
public boolean isViewFromObject(View view, Object o) {
return o == view;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
View view = getActivity().getLayoutInflater().inflate(R.layout.pager_item,
container, false);
return view;
}
Note: instantiateItem() occurs upon screen rotation. Activity does not detect that. The hint is that the class does not have onCreateView() like the fragment class.
Need help to fix a code I saw in a https://stackoverflow.com/a/9275732/1938357. The solution I need is get the current view onany page in my viewpager based application
Below is the code for reference
public View getCurrentView(ViewPager pager) {
for (int i = 0; i < pager.getChildCount(); i++) {
View child = pager.getChildAt(i);
if (child.getX() <= pager.getScrollX() + pager.getWidth()
&& child.getX() + child.getWidth() >= pager.getScrollX() + pager.getWidth()) {
return child;
}
}
return getChildAt(0);
I am trying to access the current view in the view pager and used the code above. It works fine when I scroll forwards. But when I scroll backwards then after 2 backward scrolls this stops working.
Public class myActivity extends FragmentActivity
//Variable declarations
protected void onCreate(Bundle savedInstanceState) {
ViewPager mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
View CurrView;
OnPageChangeListener pageChangelistener = new OnPageChangeListener() {
#Override
public void onPageSelected(int pageSelected) {
doTextViewChnges();//access the text view and update it based on pageSelected
---THIS IS WHERE I AM STUCK IN TRYING TO GET THE TEXTVIEW IN MY CURRENT FRAGMWNT/VIEW-------
}
mPager.setOnPageChangeListener(pageChangelistener);
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(android.support.v4.app.FragmentManager fm) {
super(fm);
}
#Override
public android.support.v4.app.Fragment getItem(int position) {
return ScreenSlidePageFragment.create(position, <other parameters I want to pass>);
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
And below is my ScreenSlidePageFragment class
public class ScreenSlidePageFragment extends android.support.v4.app.Fragment {
public static final String ARG_PAGE = "pagenumber";
public static final String ARG_PAGEARRAY = "pages";
private int mPageNumber;
//Declare other variables
#SuppressLint("NewApi")
public static ScreenSlidePageFragment create(int pageNumber, String[] pages, int bookmarkPageNumber, String storyName, int linesInOnePage) {
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PAGE, pageNumber);
args.putStringArray(ARG_PAGEARRAY, pages);
fragment.setArguments(args);
return fragment;
}
#SuppressLint("NewApi")
public ScreenSlidePageFragment() {
}
#SuppressLint("NewApi")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt(ARG_PAGE);
pages = getArguments().getStringArray(ARG_PAGEARRAY);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = (ViewGroup) inflater
.inflate(R.layout.activity_story, container, false);
//load layout with all components filled up for that page
loadView(rootView);
return rootView;
}
public int getPageNumber() {
return mPageNumber;
}
It is very hard to discuss in comments with example of code.
As I see in grepcode FragmentStatePagerAdapter.setPrimaryItem(), receive object as android.support.v4.app.Fragment. But you are receive ClassCastException when trying to cast object as Fragment. Can you check your casting: do you really cast object in android.support.v4.app.Fragment not in android.app.Fragment? If all right, add your exception message to your question, please.
UPDATE:
If your ScreenSlidePagerAdapter has method getCurrentView(), then your method 'getCurrentView()' can work like this:
public View getCurrentView(ViewPager pager) {
return ((ScreenSlidePagerAdapter) pager.getAdapter()).getCurrentView();
}
And method getCurrentView() of your ScreenSlidePagerAdapter:
public View getCurrentView() {
return currView;
}
UPDATE 2:
I've made some modification with your code:
Your Activity with private Adapter:
public class myActivity extends FragmentActivity implements ISelectItemListener {
// Your variable declarations and methods
#Override
protected void onCreate(Bundle savedInstanceState) {
final ViewPager pager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
pager.setAdapter(mPagerAdapter);
pager.setOnPageChangeListener(
new OnPageChangeListener() {
#Override
public void onPageSelected(int pageSelected) {
final ScreenSlidePageFragment fragment = mPagerAdapter.getFragment(pageSelected);
if (fragment != null) {
fragment.doTextViewChnges();
}
}
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
});
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
//Your variable declarations
private final ScreenSlidePageFragment[] mFragments;
public ScreenSlidePagerAdapter(android.support.v4.app.FragmentManager fm) {
super(fm);
mFragments = new ScreenSlidePageFragment[NUM_PAGES];
}
#Override
public int getCount() {
return NUM_PAGES;
}
// It will create new ScreenSlidePageFragment by position if it's not exsist
#Override
public android.support.v4.app.Fragment getItem(int position) {
if (getFragment(position) == null) {
mFragments[position] = ScreenSlidePageFragment.create(position, <other parameters I want to pass>);
}
return getFragment(position);
}
// It will give you ScreenSlidePageFragment if it's exists, or null
public ScreenSlidePageFragment getFragment(int position) {
return mFragments[position];
}
}
}
Your Fragment:
public class ScreenSlidePageFragment extends Fragment {
//Your variables and methods
public static ScreenSlidePageFragment create(int pageNumber, String[] pages, int bookmarkPageNumber, String storyName, int linesInOnePage) {
final Bundle args = new Bundle();
args.putInt(ARG_PAGE, pageNumber);
args.putStringArray(ARG_PAGEARRAY, pages);
final ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt(ARG_PAGE);
pages = getArguments().getStringArray(ARG_PAGEARRAY);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = (ViewGroup) inflater.inflate(R.layout.activity_story, container, false);
loadView(rootView);
return rootView;
}
public int getPageNumber() {
return mPageNumber;
}
// Let's keep logic of modifications in fragment
public void doTextViewChnges() {
final View view = getView();
// do what you want with your View and his children
}
}