Need a help with dynamic ViewPager with EditText (straggling to do a multiple tabbed editor).
Here are problems I faced:
e. g. I create 3 pages and put some text, so I have
page0 - "a"
page1 - "b"
page2 - "c"
Adding works normally here, removing page0 leads not to "a" "c" but to "a" "b" left, removing page0 leads to "a" "b" instead of "b" "c" as well, removing page2 is ok. So it seems like pager remembers text of removed page n and moves it to page n+1, text from page n+1 to n+2 and so on
same pages with the same text, but now I remove page1 and page2 (the order doesn't matter) so text "a" left. Adding two new pages will recreate "b" and "c", next pages would be blank as they should be.
if I remove just page1 and add new page, text "c" will appear.
I've been here, here and here, no help.
Tried to follow getItem(int position) calls
Also searched ViewPager and FragmentStatePagerAdapter sources, it looks like it has smth to do with instantiateItem and/or destroyItem but I have no clue what exactly.
Here is my code:
Adapter
public class PagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<Fragment> fragmentsList = new ArrayList<>();
private ArrayList<String> titlesList = new ArrayList<>();
private TabLayout layout;
public PagerAdapter(FragmentManager manager, Context context, ViewPager pager, TabLayout layout) {
super(manager);
this.layout = layout;
}
#Override
public Fragment getItem(int position) {
return fragmentsList.get(position);
}
#Override
public int getItemPosition(#NonNull Object object) {
return POSITION_NONE;
}
#Override
public int getCount() {
return fragmentsList.size();
}
#Override
public CharSequence getPageTitle(int position) {
return titlesList.get(position);
}
public void addFrag(Fragment f, String s) {
fragmentsList.add(f);
titlesList.add(s);
}
public void removeFragment(int position) {
if (layout.getChildCount() > 0)
layout.removeTabAt(position);
Fragment fragment = fragmentsList.get(position);
fragmentsList.remove(fragment);
titlesList.remove(position);
FragmentManager manager = fragment.getFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove(fragment);
trans.commit();
notifyDataSetChanged();
}
}
RootFragment
public class RootFragment extends Fragment {
private TabLayout layout;
private ViewPager pager;
private PagerAdapter adapter;
private int currentTab;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.root_fragment, container, false);
pager = view.findViewById(R.id.viewpager);
layout = view.findViewById(R.id.sliding_tabs);
adapter = new PagerAdapter(getFragmentManager(), getActivity(), pager, layout);
pager.setAdapter(adapter);
layout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
currentTab = tab.getPosition();
pager.setCurrentItem(currentTab);
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
return view;
}
public void addTab(String title) {
Bundle bundle = new Bundle();
bundle.putString("data", title);
LeafFragment leafFragment = new LeafFragment();
leafFragment.setArguments(bundle);
adapter.addFrag(leafFragment, title);
adapter.notifyDataSetChanged();
layout.setupWithViewPager(pager);
currentTab = adapter.getCount() - 1;
pager.setCurrentItem(currentTab);
}
public void deleteTab(int position) {
adapter.removeFragment(position);
}
}
Thanks and sorry for my English.
FragmentStatePagerAdapter already stores a list of Fragments, you don't need (and potentially, you should not) implement your own list while using it. Simply:
public class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return 3;
}
#Override
public Fragment getItem(int position) {
//Simply create a fragment based on position here
}
}
Second, the unfortunate truth is, removing/reordering fragments in a FragmentStatePagerAdapter appears to have still open bugs as here:
https://issuetracker.google.com/issues/36956111
My suggestion is to simply re-create the adapter if you need to delete fragments from the adapter.
Related
I have a ViewPager.When some data changed,i set the adapter for the ViewPager again by call ViewPager.setAdapter(new Adapter).What i wonder is,the Fragment is re-create (it jump on onDestroyView and then onCreateView) and my Edittext cannot set again the text.And if i remove all Fragment from FragmentTransaction and then setAdapter,the Fragment create a new one -> my Edittext set the text well.So is Fragment recreate itselft NOT from UI thread? ( Because i try the edditext.post and my edditext from first scenario work).Here is the code
Adapter:
public class PagerAdapter extends FragmentPagerAdapter {
private final ArrayList<Fragment> fmAct;
public PagerAdapter(FragmentManager supportFragmentManager, ArrayList<Fragment> fm) {
super(supportFragmentManager);
fmAct = fm;
}
#Override
public Fragment getItem(int position) {
return fmAct.get(position);
}
#Override
public int getCount() {
return fmAct.size();
}
#Override
public int getItemPosition(#NonNull Object object) {
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
return ((PrFragment) getItem(position)).getFragmentName();
}
what i do when re-set the adapter
fm.clear();
fragmentAttendanceCome = new FragmentAttendanceCome();
fragmentAttendanceCome.setFragmentName(getString(R.string.title_attendance));
fragmentAttendanceCome.setParentActivity(this);
fragmentAssessment = new FragmentAssessment();
fragmentAssessment.setFragmentName(getString(R.string.daily_evaluation));
fragmentAssessment.setParentActivity(this);
fragmentInfo = new FragmentInfo();
fragmentInfo.setParentActivity(this);
fragmentInfo.setFragmentName(getString(R.string.title_infomation));
fm.add(fragmentAttendanceCome);
fm.add(fragmentAssessment);
fm.add(fragmentInfo);
viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager(), fm));
This is my fragment :
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
textview.setText(data)
// i already Bind it but i lazy to copy it,everything work well
}
I'm using a Fragment on my Activity that will have a picture "swiper". I've created a FragmentStatePagerAdapter which I'm trying to call from this Fragment and for some reason my getItem() method is never being called.
I've searched and found that I need to use getChildFragmentManager() when instantiating my FragmentStatePagerAdapter because I'm calling it from a Fragment. I've tried this as well as getSupportFragmentManager() with no luck from either.
Any ideas?
here's my Fragment that I'm using on my MainActivity with the method that shoudl setup the FragmentStatePagerAdapter:
public class PhotoFlipper extends Fragment {
// properties and fields
// primitive types
String currentImage;
int friendShareType = 0;
ImageView share;
ViewPager pager;
PhotoFlipperItemAdapter pagerAdapter;
LatLng itemPosition;
Activity activity;
FragmentActivity fragActivity;
public ArrayList<MarkerModel> picsAndVids;
// end properties and fields
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View photoFlipperFragmentView = inflater.inflate(R.layout.photo_flipper_layout, container, false);
InitializeFragment(photoFlipperFragmentView);
return photoFlipperFragmentView;
}
private void InitializeFragment(View photoFlipperFragmentView) {
activity = getActivity();
fragActivity = (FragmentActivity)activity;
pager = (ViewPager)photoFlipperFragmentView.findViewById(R.id.photoflipper_pager);
share = (ImageView)photoFlipperFragmentView.findViewById(R.id.photoflipper_share);
share.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
IPictureFlipper iPictureFlipper = (IPictureFlipper)activity;
iPictureFlipper.ShareVideoOrImage(friendShareType, itemPosition, currentImage);
}
});
}
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) {
pager.setVisibility(View.GONE);
}
}
public void SetTappedPicture(int index) {
pagerAdapter = new PhotoFlipperItemAdapter(getChildFragmentManager(), picsAndVids, picsAndVids.size());
pager.setAdapter(pagerAdapter);
pager.setCurrentItem(index);
pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
}
});
}
}
And here's my FragmentStatePagerAdapter:
public class PhotoFlipperItemAdapter extends FragmentStatePagerAdapter {
ArrayList<MarkerModel> _markers;
int NUM_PAGES;
String TAG = "home";
public PhotoFlipperItemAdapter(FragmentManager fm, ArrayList<MarkerModel> markers, int numpages){
super(fm);
Log.d(TAG,"in adapter in constructor");
_markers = markers;
NUM_PAGES = numpages;
}
#Override
public Fragment getItem(int position) {
Log.d(TAG,"in adapter in get item");
Fragment fragment = PhotoFlipperItemFragment.create(position, _markers);
return fragment;
}
#Override
public int getCount() {
Log.d(TAG,"in adapter in get count: "+NUM_PAGES);
return NUM_PAGES;
}
}
logcat is showing me that NUM_PAGES is > 0 so that's not my issue. I see my log in the constructor and in getCount() but nothing in getItem().
TIA
I got this sorted out. I was hiding my fragment in my InitializeActivity() right after I added it to my FragmentManager so it was never fully building the Fragment with the ViewPager etc.
I'm going to have to come up with a different solution for hiding/showing the fragment but that was my problem.
My ViewPager will delete a fragment upon button click. But when I click delete, I get "canot change tag" error. Browsing through other similar questions, it was either a problem with the getItem, or getCount in the PagerAdapter, which I think my implementation is fine. Please advice.
This is the fragment to be deleted
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
displayActivity = (DisplayActivity) getActivity();
initView();
}
Button delete = (Button) getActivity().findViewById(R.id.dont_show_button_1);
delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
displayActivity.removeCurrentFragment(count-1);
}
});
My PagerAdapter is
public class PagerAdapter extends FragmentPagerAdapter {
long baseId = 0;
public static List<Fragment> fragmentArrayList = new ArrayList<Fragment>();
public PagerAdapter(FragmentManager fm){
super(fm);
fragmentArrayList.add(new FragmentOne());
fragmentArrayList.add(new FragmentTwo());
fragmentArrayList.add(new FragmentThree());
}
#Override
public Fragment getItem(int position) {
return fragmentArrayList.get(position);
}
#Override
public int getCount() {
return fragmentArrayList.size();
}
#Override
public int getItemPosition(Object object){
return PagerAdapter.POSITION_NONE;
}
#Override
public long getItemId(int position) {
// give an ID different from position when position has been changed
return baseId + position;
}
/**
* Notify that the position of a fragment has been changed.
* Create a new ID for each position to force recreation of the fragment
* #param n number of items which have been changed
*/
public void notifyChangeInPosition(int n) {
// shift the ID returned by getItemId outside the range of all previous fragments
baseId += getCount() + n;
}
}
This is the activity that hosts the ViewPager
public class DisplayActivity extends FragmentActivity {
ViewPager viewPager;
PagerAdapter pagerAdapter;
String TAG = "DisplayActivity";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.db_layout);
viewPager = (ViewPager) findViewById(R.id.pager);
pagerAdapter = new PagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(pagerAdapter);
}
public void removeCurrentFragment(int position){
pagerAdapter.fragmentArrayList.remove(position);
pagerAdapter.notifyChangeInPosition(1);
pagerAdapter.notifyDataSetChanged();
Log.e(TAG, "in removeCurrentFragment");
}
I'm getting the error:
IllegalStateException: Can't change tag of fragment FragmentTwo{170c98f1 #1 id=0x7f0c006d android:switcher:2131492973:1}: was android:switcher:2131492973:1 now android:switcher:2131492973:3
The problem was with
public class PagerAdapter extends FragmentPagerAdapter
Changing it to
public class PagerAdapter extends FragmentStatePagerAdapter
enabled correct deletion.
This can be attributed to "FragmentStatePagerAdapter disposes of views that fall outside the current and traversable views."
As suggested to this post
Remove Fragment Page from ViewPager in Android
By Looking at your code i think your count value may be wrong.
Button delete = (Button) getActivity().findViewById(R.id.dont_show_button_1);
delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// here
displayActivity.removeCurrentFragment(count-1);
//change to
displayActivity.removeCurrentFragment(adapter.getCount()-1);
}
});
Hope it helps.Please post your whole code like where you getting the value of count variable. Post whole Code if Solution Does not Solve Your Problem .
Refer here for example:- https://androidruler.wordpress.com/2016/02/22/working-with-viewpager-as-a-images-slider/
Hi I'm making an app with A fragment and few child fragment inside it using tablayout and viewpager. The problem is all my child fragment (from Tablayout) always execute (load all the code inside whenever user click the parent fragment. How to get the child fragment only load whenever the the user slide to it (for example I have 2 tab, content in tab 2 only load when user slide to tab 2)
Here is my parent fragment code
public class ManageEventFragment extends Fragment {
public ManageEventFragment() {
// Required empty public constructor
}
TabLayout tabLayout;
ViewPager viewPager;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View viewFragment = inflater.inflate(R.layout.fragment_manage_event, container, false);
viewPager = (ViewPager) viewFragment.findViewById(R.id.viewPager);
tabLayout = (TabLayout) viewFragment.findViewById(R.id.tabLayout);
viewPager.setAdapter(new CustomAdapter(getChildFragmentManager(), getContext()));
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
return viewFragment;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// tabLayout.setupWithViewPager(viewPager);
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
}
});
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
});
if (ViewCompat.isLaidOut(tabLayout)) {
tabLayout.setupWithViewPager(viewPager);
} else {
tabLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
tabLayout.setupWithViewPager(viewPager);
tabLayout.removeOnLayoutChangeListener(this);
}
});
}
super.onActivityCreated(savedInstanceState);
}
//TabLayout and ViewPager class
private class CustomAdapter extends FragmentPagerAdapter {
private String fragments[] = {"Edit Event", "Create Event"};
public CustomAdapter(FragmentManager fragmentManager, Context context) {
super(fragmentManager);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new EditEventFragment();
case 1:
return new CreateEventFragment();
default:
return null;
}
}
#Override
public int getCount() {
return fragments.length;
}
#Override
public CharSequence getPageTitle(int position) {
return fragments[position];
}
}
}
And my child fragment code. I want these code only execute whenever the user slide to the child tab. Any help is much appreciate, Thanks
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//Initializing our listEvents list
listEvents = new ArrayList();
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
adapter = new EventAdapterEdit(getContext(),listEvents);
recyclerView.setAdapter(adapter);
requestQueue = Volley.newRequestQueue(getContext());
adapter = new EventAdapterEdit(getContext(),listEvents);
recyclerView.setAdapter(adapter);
getEventDetailRespond(requestQueue);
#Override public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
}
you could use above method within your fragment that is within your ViewPager Adapter
if(isVisibleToUser){//dosomething when the fragment is visible}
else{//dosomething else.}
be aware to do not initialize views there or anything rather, init your views within onViewCreated and call the method that you wanna execute on setUserVisibleHint. another ugly way is to add a scroll listener to your ViewPager and get the current item position and trigger an action that is within the fragment. to get the fragment from the ViewPager Adapter you can do such :
MyFragment frag = (MyFragment) pager.getAdapter().instantiateItem(pager, position);
then you could call a method that is within MyFragment
The Viewpager by default loads the adjacent fragment to ensure make the app smooth, so that when the user swipe to the fragments (already loaded) it is there. To change is default behavior, use
viewpager.setOffscreenPageLimit(int limit) where limit is how many fragment next to the one you are on will be preloaded.
Hope this helps
I have a FragmentList with some items. If I click an item the appropriate Fragment will open which contains a Viewpager. No problem so far but if I go back to the List and click the item again the method getItem(int position) is not called anymore.
Here a link with a similar problem:
Display fragment viewpager within a fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.activity_fragment_runtime, container, false);
mViewPager = (ViewPager) view.findViewById(R.id.pager);
indicator = (TitlePageIndicator) view.findViewById(R.id.indicator);
MyFragmentPagerAdapter mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity().getSupportFragmentManager());
mViewPager.setAdapter(mMyFragmentPagerAdapter);
indicator.setViewPager(mViewPager);
return view;
}
class MyFragmentPagerAdapter extends FragmentStatePagerAdapter implements TitleProvider {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public String getTitle(int position) {
return muxbusTitles[position];
}
#Override
public Fragment getItem(int location) {
return muxbusFragmentList.get(location);
}
#Override
public int getCount() {
return muxbusTitles.length;
}
}
return view;
}
Edit:
If someone is interested,the solution was to extends from PagerAdapter instead of FragmentStatePagerAdpater because the Viewpager is already in a Fragment..
I did have similar problem and after scratching my head for quite some time i made an my own understanding (assumption) that ViewPager is keeping the reference of its child fragments into memory even the hosted Fragment or Activity is destroyed (didn't tested with Activity). So i wrote this method freeFragmentsFromViewPager(). You need to call it from onDestroy() of your Fragment.
/**
* This method free the child fragments from ViewPager as ViewPager keeps the reference
* of child fragments into memory and next time when you again come to visit view pager it won't show the fragment layout
* because it has the reference to the fragment whilst the fragment's view was already destroyed.
*/
private void freeFragmentsFromViewPager()
{
for(int i = 0; i < TabsFragmentPagerAdapter.fragments.length; i++)
{
Fragment fragment = TabsFragmentPagerAdapter.fragments[i];
if(fragment != null)
{
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.remove(fragment);
fragmentTransaction.commit();
TabsFragmentPagerAdapter.fragments[i] = null;
}
}
}
And this is my Adapter code
public class TabsFragmentPagerAdapter extends FragmentPagerAdapter
{
public static final int NUM_TABS = 3;
public static Fragment[] fragments = new Fragment[NUM_TABS];
public TabsFragmentPagerAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
Log.e("TabsFragmentPagerAdapter", "getItem");
Fragment fragment = TabsFragmentFactory.newInstace(position);
fragments[position] = fragment;
return fragment;
}
#Override
public int getCount()
{
return NUM_TABS;
}
#Override
public CharSequence getPageTitle(int position)
{
return TabsFragmentFactory.getName(position);
}