Once again, I am so annoyed by the AndroidSDK . Why does this have to be so complicated?
I have a ViewPager that I want to populate with my Fragment. I implemented this on an Activity and it works. But when its in another Fragment the whole thing crashes and the erros messages tells me nothing:
07-01 12:58:20.210: E/AndroidRuntime(10146): java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
Here is my code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_tabwrapper, container);
List<Fragment> fragments = getFragments();
pageAdapter = new MyPageAdapter(getActivity()
.getSupportFragmentManager(), fragments);
pager = (ViewPager) v.findViewById(R.id.viewpager);
pager.setAdapter(pageAdapter); // <- CRASH!
return v;
}
BTW: The whole thing is based on this tutorial: http://architects.dzone.com/articles/android-tutorial-using
This error occurs beacuse you have to remove all views before set adapter in view pager so get right way..... So check this:
MyPageAdapter pageAdapter ;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_tabwrapper, container);
List<Fragment> fragments = getFragments();
pageAdapter = new MyPageAdapter(getActivity()
.getSupportFragmentManager(), fragments);
pager = (ViewPager) v.findViewById(R.id.viewpager);
pager.removeallviews();
pager.setAdapter(pageAdapter); // <- CRASH!
return v;
}
you should not use the ViewGroup that onCreateView provides as container to put your inflated view:
Change
View v = inflater.inflate(R.layout.fragment_tabwrapper, container);
to
View v = inflater.inflate(R.layout.fragment_tabwrapper, null);
This might shed a little light on the topic: http://developer.android.com/about/versions/android-4.2.html#NestedFragments
IMHO: I don't understand why developers put up with these tools. I find them impossible to work with. No wonder there are so many low-quality apps on the Play store. I'm looking forward to the day this project is done. Won't be working with this OS anytime soon again ...
Related
I' m trying to improve my android app. I have main app menu (with possible actions) in a GridView 3x3. And I decided to add ViewPager to separate my grid. Before I've added ViewPager, I had only one Activity with GridView. In my onCreate method, I calculated the window height with DisplayMetrics to understand, what height should I use for my GridView items.
Now I'm trying to use ViewPager with Fragments. I have my Activity with ViewPager, and 2 Fragments. Each Fragment has the same layout (GridView in LinearLayout). I'm trying to pass screen height into Fragment in this way:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_grid_layout);
pageHeight = getHeight();
Log.d("HEIGHT", "Page height: "+pageHeight);
ViewPager viewPager;
viewPager = findViewById(R.id.main_menu_viewpager);
src = new ArrayList<MainPageTableFragment>();
/*Определяем количество страниц меню*/
int pagesCount = 2;
for (int i = 0; i < pagesCount; i++) {
src.add(MainPageTableFragment.newInstance(i, pageHeight));
}
FragmentPagerAdapter adapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(), src);
viewPager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabDots);
tabLayout.setupWithViewPager(viewPager, true);
}
So, each launch of onCreate method should recreate Fragments.
And in Fragment, I get my height in this way:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.activity_main_grid_fragment, container, false);
gridView = rootView.findViewById(R.id.main_menu_grid);
//PROBLEM IS HERE...
height = this.getArguments().getInt("containerHeight");
pageNum = this.getArguments().getInt("pageNumber");
populateGridItems(); //method to load items in gridview
return rootView;
}
The problem is: when I rotate my device, all methods are called, but
height = this.getArguments().getInt("containerHeight");
uses old value. For instance, in first launch (vertical screen orientation), it is 690. When I rotate my device, in onCreate I calculate new height (382), but my Fragment takes old value (690). I tried to call getArguments() in several places (onAttach, onCreate, onActivityCreated), but didn't help.
Can anybody explain me, where is the problem, and what should I do?
Thank you!
UPD: Code of my MainPageTableFragment
public static MainPageTableFragment newInstance(int pageNumber, int height) {
Bundle args = new Bundle();
args.putInt("pageNumber", pageNumber);
args.putInt("containerHeight", height);
MainPageTableFragment fragment = new MainPageTableFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.activity_main_grid_fragment, container, false);
gridView = rootView.findViewById(R.id.main_menu_grid);
height = getArguments().getInt("containerHeight");
Log.d("HEIGHT", "Fragment height in onCreateView: "+height);
pageNum = getArguments().getInt("pageNumber");
populateGridItems();
return rootView;
}
private void populateGridItems() {
/*adding items in GridView*/
gridView.setAdapter(new MenuGridAdapter(this.getContext(), items, height));
}
}
I'm not sure but i think it's because of supportFragmentManager and pagerAdapter.
First time in instantiateItem it creates a name for fragment and in destroyItem it's just detach fragment.
Then after rotating in instantiateItem it founds fragment by name and uses old instance instead of new. I think for optimization and so on.
As a solution, you can remove all fragments from supportFragmentManager or override instantiateItem/destroyItem to remove frgament as you need.
I found another way to do this. In my Activity, I've created public method to calculate height, and in onCreateView in my Fragment, I call this method. It works. But for me, it's unknown problem: why recreation of the fragment took values of arguments from the old bundle.
I am trying to do something similar as https://stackoverflow.com/a/24437224/2277631. I am even using the same layout:
I am following this approach because I am using a NavigationView (each option is a fragment) that has the first option as a fragment with Tabs (so a ViewPager with more fragments).
I found a problem when using:
viewPager.setAdapter(new AdapterView(getChildFragmentManager()));
Basically, using the ViewPager as nested fragment, it only loads 1 page (from the 3 tabs), and it only loads the other pages when I swipe to them (each tab is a fragment with its ContentLoader). I found that using setOffscreenPageLimitgets ignored in this case. Instead, if I use:
getActivity().getSupportFragmentManager() then the ViewPager works as expected (loading by default 1 page and the next and previous pages). But then, a lot of weird stuff happen (cause I am suppose to use getChildFragmentManager when using nested fragments). So... Any ideas of using ViewPager as nested fragment and make setOffscreenPageLimit to work?
Edit 1 (30 Dec 15)
Tracking the bug down, I checked that all the fragments are been created. The problem is that the three fragments have they own LoaderCallback but only the first one calls onLoadFinished. Trying to figure out why the other fragments only call onLoadFinished when navigating to it (was working without the nested approach).
I ran into the same problem. This is how I do and this is working for me, it has the same architetcure as what you expect to have.
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
view = inflater.inflate(R.layout.communitylayout, container, false);
Bundle data = getArguments();
TabLayout tabLayout = (TabLayout) view.findViewById(R.id.comtabs);
tabLayout.setTabMode(TabLayout.MODE_FIXED);
// add tabs
tabLayout.addTab(tabLayout.newTab());
tabLayout.addTab(tabLayout.newTab());
tabLayout.addTab(tabLayout.newTab());
RelativeLayout layout1 = (RelativeLayout) inflater.inflate(R.layout.communitytablayoutleft, container, false);
RelativeLayout layout2 = (RelativeLayout) inflater.inflate(R.layout.communitytablayout, container, false);
RelativeLayout layout3 = (RelativeLayout) inflater.inflate(R.layout.communitytablayoutright, container, false);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
pager = (ViewPager) view.findViewById(R.id.compager);
CommunityPagerFragment adapter = new CommunityPagerFragment(getChildFragmentManager());
pager.setAdapter(adapter);
tabLayout.setupWithViewPager(pager);
pager.setCurrentItem(0);
tabLayout.setOnTabSelectedListener(this);
// KEEP FRAGMENT STATE INSIDE OF THE TAB
pager.setOffscreenPageLimit(2);
((TextView)layout1.findViewById(R.id.tabtext)).setText(tabs[0]);
((TextView)layout2.findViewById(R.id.tabtext)).setText(tabs[1]);
((TextView)layout3.findViewById(R.id.tabtext)).setText(tabs[2]);
//tabLayout.set
tabLayout.getTabAt(0).setCustomView(layout1);
tabLayout.getTabAt(1).setCustomView(layout2);
tabLayout.getTabAt(2).setCustomView(layout3);
onTabSelected(tabLayout.getTabAt(1));
//tabLayout.set
return view;
And if you want to get the Tabs working by click in addition of the swipe, add the following:
#Override
public void onTabSelected(TabLayout.Tab tab) {
pager.setCurrentItem(tab.getPosition());
}
I want to build a ViewPager with 2 Pages. On the first page you can choose a category. When a List item is selected the screen swipes to the second page and you can choose some subcategories.
I implemented the clicklistener and so on. But I have no idea how to set the current page from within a fragment. Is it even possible? Or do you have another solution for me? Hope you understand what I want to achieve.
Ok, found a solution for my own. Thanks to Sayem for the hint.
I passed the ID of the ViewPager via bundle to my first fragment. in my fragment i could use then this simple code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
viewPagerId = getArguments().getInt("viewPagerId");
View rootView = (View) inflater.inflate(R.layout.fragment_cat, container, false);
populateListView(rootView);
viewPager = (ViewPager) getActivity().findViewById(viewPagerId);
return rootView;
}
and to switch pages then simply this:
viewPager.setCurrentItem(position);
Thanks for the help
use ((ActivityName)getActivity()).getViewPager().setCurrentItem(position);
note: getViewPager() should return the viewpager instance
try:
mPager.setCurrentItem(position);
I’m doing a navigation drawer in my android app. On my onNavigationDrawerItemSelected function i try on item select to change the current fragment into a new one. The problem is that when I change the current fragment its take the initial status of it. maybe because i'm doing this way
fragmentManager.beginTransaction()
.replace(R.id.cont, new SessionsFragement())
.commit();
I declare a new instance of my fragment and i think it’s normal, its display me the initial status of the thing. On my fragment i've try to set a textView to see if i will say it when i call the fragement from tha navigation drawer but it's not the case.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_spot, container, false);
tata = (TextView) v.findViewById(R.id.tata);
tata.setText("karim");
return inflater.inflate(R.layout.fragment_spot, container, false);
}
So please can anyone help me to fix this.
In the snippet you posted you are inflating again the view. You should return the one you modified
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_spot, container, false);
tata = (TextView) v.findViewById(R.id.tata);
tata.setText("karim");
return v;
}
I'm getting a strange problem with Fragment and ViewPager.
I have 1 ViewPager contains 3 Fragments.
When my app run for the first time. onCreateView on all my fragments get called and ViewPager works perfectly.
Code for initalizing ViewPager
private void initPager() {
mFragmentList.add(new MainSongListFragment());
mFragmentList.add(new MainAlbumArtFragment());
mFragmentList.add(new MainLyricsFragment());
if (mFragmentList == null)
mFragmentList = new ArrayList<Fragment>();
MainPagerAdapter pagerAdapter = new MainPagerAdapter(
getSupportFragmentManager(), mFragmentList);
mMainPager.setAdapter(pagerAdapter);
myListViewInsideFragment= (ListView) findViewById(R.id.my_list);
}
When I press back button and run app again. I get error at the code:
myListViewInsideFragment= (ListView) findViewById(R.id.my_list);
It returns null because the onCreateView method on Fragment does not get called so all views on this fragment is not avaiable.
Code in My Fragment:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.lo_my_list, container,
false);
mListView = (ListView) view
.findViewById(R.id.my_list);
return view;
}
My question is:
Why does not onCreateView method get called when Activity get destroyed and re-run again?.
Thank you.