I'm using a TabLayout with ViewPager. I have two tabs in my tab layout which both have a different instances of the same fragment object. Both fragments have the same recycler view layout as well.
Problem: When I load the entire activity after getting the server objects back, I'm trying to populate both tab views with the new object, but I get the following error:
java.lang.IllegalStateException: FragmentManager is already executing
transactions
. So in my main activity:
public class MyMainActivity extends FragmentActivity {
private TabLayout tabLayout;
private ViewPager viewPager;
private MyAdapter adapter;
private MyFragment fragmentA;
private MyFragment fragmentB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
tabLayout = (TabLayout)findViewById(R.id.tab_layout);
viewPager = (ViewPager)findViewById(R.id.view_pager);
adapter = new MyAdapter(getSupportFragmentManager(), new
ArrayList<>(), new ArrayList<>());
// Instantiate the fragments here
adapter.addFragment(....A, "Title A");
adapter.addFragment(....B, "Title B");
.....
.....
.....
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
}
public void refreshPage() {
runOnUiThread(new Runnable() {
#Override
public void run() {
// This line crashes
adapter.notifyDataSetChanged();
}
});
}
}
I tried using getChildFragmentManager but I can't because you need to call that via a fragment object. I cannot show much code as most of it is proprietary and I had to make the code as generic as possible.
When I comment out the line that crashes, my recycler view for the first tab does not refresh unless I scroll. while the second tab does not refresh at all. Did anyone have the same issue before? How did you resolve it?
P.S I'm not using any FragmentManager or transactions so I'm not sure why it gives me that exception.
Related
I keep running into a recurring issue in many of my apps and have been using all kinds of work arounds to "solve" it, but this time I've had it and I want to figure out a real solution.
I am trying to build a tabbed layout with two tabs where each tab shows some data which should be obtained from the internet. Once the data is obtained it is cached on the device so it can be restored instantly the next time the app is opened (and will then be refreshed in the background).
To this effect I am trying to load the cached data and display it in a RecyclerView in the first tab, and I want to do this on activity create. Before I do this I obviously set up all the tab layout stuff so that the tabs should be properly loaded. The problem is that they are not, it seems the Fragments that make up the tab pages don't have their views yet, hence I cannot access the RecyclerView on them.
Here is my Activity code:
public class MainActivity extends NetworkBusActivity
{
// Views
ViewPager tabPager;
TabLayout tabLayout;
// Tab pager adapter
private ViewPagerAdapter adapter;
// Fragment one and two
private MenuFragment menuFragment;
private OrderFragment orderFragment;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create the views
tabPager = (ViewPager) findViewById(R.id.tabPager);
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
// Setup the tab layouts
this.setupTabs();
// Show cached data
this.setCachedItems();
// Start loading new data in background
this.startLoading();
}
private void setupTabs()
{
// Create Fragments
menuFragment = new MenuFragment();
orderFragment = new OrderFragment();
// Setup adapter
adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(menuFragment, getString(R.string.title_menu));
adapter.addFragment(orderFragment, getString(R.string.title_orders));
tabPager.setAdapter(adapter);
// Setup the tab layout
tabLayout.setupWithViewPager(tabPager);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener()
{
// not shown
});
}
private void setCachedItems()
{
// Show cached data
ArrayList<Item> items = Cache.menu.getItems();
menuFragment.setItems(items);
}
private void startLoading()
{
// Start loading in background (not shown)
}
}
It should be straightforward: create the views, create the fragments, and setup the tab layout, then load the cached data.
The MenuFragment extends a base class ItemListFragment which defines the setItems method:
public class MenuFragment extends ItemListFragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.tab_menu, container, false);
return view;
}
#Override
protected ItemListAdapter getAdapter(ArrayList<Item> items, boolean categorize)
{
return new MenuListAdapter(this, R.layout.row_item, items, categorize);
}
}
public abstract class ItemListFragment extends Fragment
{
private RecyclerView recycler;
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
recycler = (RecyclerView) view;
recycler.setLayoutManager(linearLayoutManager);
recycler.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
}
public void setItems(ArrayList<Item> items)
{
recycler.setAdapter(getAdapter(items, true));
}
protected abstract ItemListAdapter getAdapter(ArrayList<Item> items, boolean categorize);
}
Again straightforward: create the view in onCreateView, then obtain the RecyclerView in onViewCreated, and finally set the items with an adapter.
The problem is simple: the method setCachedItems in MainActivity is called before the onCreateView or onViewCreated methods are called in the Fragments. Hence, the RecyclerView is null and I can't set its adapter. Even though I am creating a new instance of the Fragments and adding them to a functional TabLayout before I call that method.
There seems to be some delay before the views are created, but I need to set the items already when the activity is created.
Where am I going wrong, and how do I fix it?
In onViewCreated of fragment do this
((MainActivity)getActivity).setCachedData().....
Instead of on create of activity
so this one really has me stumped. I am trying to create a simple ViewPager activity which has three swipe views containing a listview. I have a FragmentPagerAdapter, a ListFragment set to hold the listviews, and have defined the layout for the fragments. Here is my problem is that FragmentPagerAdapter takes a FragmentManager, so I try to pass it in like so
mFragmentPagerAdapter=new FragmentPagerAdapter(getSupportFragmentManager());
but the FragmentManager is always null, meaning that the rest of the work to set up the pager crashes the app
I have no idea why getSupportFragmentManager would return null.
Here is the code for my activity, or at least the relevant bits
public class JobListActivity extends FragmentActivity {
private ArrayList<Job> jobsQuote=new ArrayList<Job>();
private ArrayList<Job> jobsIn= new ArrayList<Job>();
private ArrayList<Job> jobsOut=new ArrayList<Job>();
private ListFragment quoteListFragment=new ListFragment();
private ListFragment inListFragment =new ListFragment();
private ListFragment outListFragment=new ListFragment();
JobFragmentPagerAdapter mFragmentPagerAdapter;
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_job_list);
FragmentManager fm = getSupportFragmentManager();
mFragmentPagerAdapter =
new JobFragmentPagerAdapter(fm);
mViewPager = (ViewPager) findViewById(R.id.jobs_view_pager);
mViewPager.setAdapter(mFragmentPagerAdapter);
I really appreciate any help you can provide, thanks so much for your time
Here is the tutorial I have been following from the android developer web site
http://developer.android.com/training/implementing-navigation/lateral.html
clearly I am missing something.
Figured it out. When I implement the methods from Fragment activity, I accidentily overrode getSupportFragmentManager(); to return null.
Need some help with my problem of updating pages while using viewpager. I am using a simple viewpager with FragmentStatePagerAdapter. All I want to do is get access to the current fragment/view so as I can update some textviews in my fragment/view. I searched around in the forum and came to know few things
- One of the ways to handle this is by setting tag in instantiateItem() call back of the adapter and retreive the view by findViewbyTag. I could not understand how to implement this and I am also not sure if that will work for FragmentStatePagerAdapter.
- I explored other options as suggested in various forums, but cannot make them work.
My code is very much same as in a android http://developer.android.com/training/animation/screen-slide.html. Basic components are the same (Fragment activity xml with some display components including a textview, viewpager xml with just a view pager in it, a Fragment class and the main FragmentActivity class). in my FragmentActivity class I have added a pageChangelistener to my viewpager so as I can do my textview changes during onPageSelected().
Any help is is appreciated.
Adding the code for reference.
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;
}
}
in your OnPageChangeListener:
OnPageChangeListener pageChangelistener = new OnPageChangeListener() {
#Override
public void onPageSelected(int pageSelected) {
ScreenSlidePageFragment currentFragment = (ScreenSlidePageFragment) mPagerAdapter.getItem(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-------
}
i assume you want to access your fragments from your activity and update the views.Fragments are parts of activities and their views can be accessed by them. Fragments are created dynamically in viewadapters and there is no straight forward and easy way to Tag them. You may simply access a fragment by its index number.first one is 0 second one is 1 and so on...
//access your fragment(in your case your first fragment)
//this part should be inside your doTextViewChnges()
Fragment fragment = (Fragment ) adapterViewPager
.getFragment(0);
if (fragment != null) {
//call a public method inside your fragment to update your text view
fragment .updateYourTextView();
}
Edit: inside your fragment create the following method and update your textview from there.
void updateYourTextView() {
yourTextView.setText("yourtext");
}
I've got a ViewPager with 4 tabs.
I'm using a custom adapter that extends FragmentPagerAdapter.
The problem is that when I change the tab, the first Fragment gets created again although it's not the currently visible.
Moreover, this only happens after changing the tab for the fourth time. E.g. tab1 -> tab2 -> tab3=> onStop from tab1 is called. -> tab2 => onCreateView from tab1 is called (onAttach is not called).
I want to avoid this. I need all tabs to remain always in their state even when they are not currently displayed.
How can I do this?
It's been as easy as using method setOffscreenPageLimit() from ViewPager.
More info: http://developer.android.com/reference/android/support/v4/view/ViewPager.html#setOffscreenPageLimit(int)
Viewpager has one public method named setOffScreenPageLimit which indicates the number of pages that will be retained to either side of the current page in the view hierarchy in an idle state.
Activity:
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = findViewById(R.id.container);
mViewPager.setOffscreenPageLimit(3); // change your number here
mViewPager.setAdapter(mSectionsPagerAdapter);
When you instantiate the fragment, call setRetainInstance(true). This will retain the instance state of the fragment. onCreateView will still be called and you will need to make sure that you re use the saved state and just render the views without loading any data again, e.g. by using a boolean to represent if your fragment has already been initialized.
You would do something like this:
public static class MyAdapter extends FragmentPagerAdapter {
#Override
public Fragment getItem(int position) {
MyFragment myFragment = new MyFragment();
myFragment.setRetainInstance(true);
return myFragment;
}
}
class MyFragment extends Fragment {
.
.
.
boolean initialized = false;
ArrayList<MyData> myData = null;
void onCreate(...) {
if (initialized) {
renderViews();
} else {
initialized = true;
loadData();
renderViews();
}
}
}
You don't need to implement onSaveInstanceState(). It will be handled automatically.
I've tried to use this answer to use a ViewPager with indicator inside a fragment, like so. (Just a mock up).
There's navigation spinner that changes the fragment. Each "page" would contain a ListView.
So basically I'm looking for:
SherlockFragmentActivity -> SherlockFragment -> ViewPager -> ListView
However, when I try and implement what I saw in the answer I get an error with findViewById();
I assume it's because I'm not getting the correct "context".
Here's my Fragment:
public class HomeViewFragment extends SherlockFragment {
private ViewPager mPager;
private HomePagerAdapter mAdapter;
private TitlePageIndicator mIndicator;
private Context context;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getSherlockActivity().getApplicationContext();
mAdapter = new HomePagerAdapter(context);
mPager = (ViewPager)context.findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TitlePageIndicator)context.findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
}
Step #1: Delete context = getSherlockActivity().getApplicationContext();
Step #2: Replace context.findViewById with getActivity().findViewById
Step #3: Replace all remaining occurrences of context with getActivity()
Step #4: Delete your onActivityCreated() implementation, as it is not doing anything
Step #5: Resolve that you will never again use getApplicationContext() unless you know specifically why you need it in a given circumstance