Hi I am new to android if someone is able to help me to fix this issue it will be much appreciated. Now I am trying to go back to the previous view or the root view do anyone know how to go back to the previous view from static context(I am not sure static context is the correct way to say it do feel free to correct me if I am wrong)?
I have already tried creating FragmentManager the reason I use this is because the application I am trying to create used Fragment.
FragmentManager fragmentManager =
((FragmentActivity)mContext).getSupportFragmentManager();
fragmentManager.popBackStack();
`
public class SomeView1 extends View implements View.OnTouchListener {
public SomeView1(Context c) {
super(c);
mContext = c;
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
}
public SomeView1(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
public void foodPortionCal() {
final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle("Output");
builder.setMessage("Message here");
builder.setCancelable(true);
builder.setNegativeButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
sqLiteHelper.insertDataFood(GlobalVariable.mealTypeF, String.format("%.2f", totalProteinPer) + '%', String.format("%.2f", totalGrainsPer) + '%', String.format("%.2f", totalVegetablePer) + '%', String.format("%.2f", totalFruitPer) + '%', String.format("%.2f", totalDairyPer) + '%', GlobalVariable.dataF);
Toast.makeText(mContext, "Added", Toast.LENGTH_SHORT).show();
//this is where I want to set the feature to remove the view/static context?
FragmentManager fragmentManager = ((FragmentActivity)mContext).getSupportFragmentManager();
fragmentManager.popBackStack();
dialog.dismiss();
}
});
builder.show();
}
}
//Before going the code will move to someview1 we will be in a fragment class called FoodFragment and this is how it look like
public class Food_Cal extends Fragment {
private void mealTypeSelection()
{
//this is how I call the come view activity
getActivity().setContentView(new SomeView1(view.getContext()));
}
}
// here is a link to how this app was constructed Android: Free Cropping of Image
the only biggest different and the things that is giving me issue is that my app uses fragment and the code the the reference doesn't
The result wanted to get is either it will move to next view with fragment or go all the way back to the root view with fragment. the result I am getting are either the app keep crashing and not able to go back to root view then the app crash this is the error message I am getting
2019-04-27 00:35:36.599 624-624/apd.bii.diabetesappv1 E/AndroidRuntime: FATAL EXCEPTION: main
Process: apd.bii.diabetesappv1, PID: 624
java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.support.v4.app.FragmentActivity
at apd.bii.diabetesappv1.FoodCalculation.SomeView1$1.onClick(SomeView1.java:437)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:173)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6894)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)
This is a simple method used for adding fragment into your FragmentManger so it can be visible. All you need to do is pass in the fragment you want to add on the screen.
public void addScreen(Fragment fragment) {
getSupportManager()
.beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack(null)
.commit();
screensOnTheStack++;
}
And when you want to pop that fragment out of the stack and go to your parent view is
while (screensOnTheStack > 0) {
fragments.popBackStackImmediate();
}
Or simply by defining your fragment TAG for determining the root(parent) fragment, like
private static final String BACK_STACK_ROOT_TAG = "root_fragment";
then
// Pop off everything up to and including the current tab
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack(BACK_STACK_ROOT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
// Add the new tab fragment
fragmentManager.beginTransaction()
.replace(R.id.container, TabFragment.newInstance())
.addToBackStack(BACK_STACK_ROOT_TAG)
.commit();
Now, for the part you said
The result wanted to get is either it will move to next view with fragment or go all the way back to the root view with fragment.
you can put your own condition as to when you would like to go to the fragment you want to go or either go to your parent view instead.
Related
I have a tablayout with a viewpager in my MainActivity.
My PagerAdapter looks like this:
public class MainActivityPagerAdapter extends PagerAdapter {
public MainActivityPagerAdapter(FragmentManager fm, int numOfTabs) {
super(fm, numOfTabs);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new StoresFragment();
case 1:
return new OrdersFragment();
default:
return null;
}
}
}
I am coming back from another activity like this:
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish(); //finishAffinity();
But then I get an java.lang.IllegalStateException in one of my Fragments in the viewpager of the MainActivity.
I read many related questions and tried to solve this. It is said, that this happens when one keeps references to Fragments outside of the PagerAdapter. But I am not doing this, as you can see in my code.
Does anyone know what I am doing wrong?
Edit - Stacktrace
FATAL EXCEPTION: main
Process: com.lifo.skipandgo, PID: 23665
java.lang.IllegalStateException: Fragment OrdersFragment{42c2a740} not attached to a context.
at android.support.v4.app.Fragment.requireContext(Fragment.java:614)
at android.support.v4.app.Fragment.getResources(Fragment.java:678)
at com.lifo.skipandgo.activities.fragments.OrdersFragment$1.results(OrdersFragment.java:111)
at com.lifo.skipandgo.connectors.firestore.QueryResult.receivedResult(QueryResult.java:37)
at com.lifo.skipandgo.controllers.UserController$2.onUpdate(UserController.java:88)
at com.lifo.skipandgo.connectors.firestore.QuerySubscription.onEvent(QuerySubscription.java:59)
at com.lifo.skipandgo.connectors.firestore.QuerySubscription.onEvent(QuerySubscription.java:18)
at com.google.firebase.firestore.zzg.onEvent(Unknown Source)
at com.google.firebase.firestore.g.zzh.zza(SourceFile:28)
at com.google.firebase.firestore.g.zzi.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5653)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1291)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1107)
at dalvik.system.NativeStart.main(Native Method)
Edit:
Interesting is, that the view has defenitely loaded when the error occurs. Because the error occurs about 10-15 seconds later after the fragment is shown again. I this in my orderFragment, where the error occurs:
orders = new QueryResult<UserOrder>(UserOrder.class) {
#Override
public void results(List<UserOrder> results) {
orderLoadingMessage.setBackgroundColor(getResources().getColor(R.color.green));
}
}
I do this in onCreateView and this result comes about 10-15 seconds after the view loaded.
The problem seems to be, that your fragment is listening to some events (via UserController and QueryResult) and these are fired before the fragment is attached to context.
Try to unregister the fragment when it becomes detached and to them again after attaching (LiveData can also help with this). Another way could be to receive and store the event while detached and only process it after attaching.
Use this before update your Activity UI :
if(isAdded())// This {#link androidx.fragment.app.Fragment} class method is responsible to check if the your view is attached to the Activity or not
{
// TODO Update your UI here
}
viewPager.offscreenPageLimit = (total number of fragments - 1)
viewPager.adapter = Adapter
Use this if your are using viewpager
And if you are using bottom navigation just simply check if(context != null)
But i suggest to use max 3 fragments in offscreenPageLimit
Some of your callbacks are being fired after your fragment is detached from activity. To resolve this issue you need to check whether your fragment is added before acting upon any callbacks. For example, change your orders object's initialization to this:
orders = new QueryResult<UserOrder>(UserOrder.class) {
#Override
public void results(List<UserOrder> results) {
if(isAdded()) {
orderLoadingMessage.setBackgroundColor(
getResources().getColor(R.color.green));
}
}
}
In my case this exception happened when I showed a DialogFragment and called it's methods. Because the fragment hasn't attached to a FragmentManager (this operation completes asynchronously) before calling methods, an application crashed.
val fragment = YourDialogFragment.newInstance()
fragment.show(fragmentManager, YourDialogFragment.TAG)
// This will throw an exception.
fragment.setCaptions("Yes", "No")
If you add the fragment with FragmentManager, you will get another exception: java.lang.IllegalArgumentException: No view found for id 0x1020002 (android:id/content) for fragment (or similar, if you use another id).
You can call fragment methods via post (or postDelayed), but it is a bad solution:
view?.post {
fragment.setCaptions("Yes", "No")
}
Currently I use childFragmentManager instead of fragmentManager:
val fragment = YourDialogFragment.newInstance()
fragment.show(childFragmentManager, YourDialogFragment.TAG)
fragment.setCaptions("Yes", "No")
I don't remember what I did, but now it works.
I had similar problem. I have solved it by following ferini's recommendation. I was using a live data which was firing before the context was attached.
Here is my full implementation
public class PurchaseOrderFragment extends Fragment {
FragmentPurchaseOrderBinding binding;
CurrentDenominationViewModel currentDenominationViewModel;
#Inject
ViewModelFactory viewModelFactory;
CurrentVoucherChangedObserver observer;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
currentDenominationViewModel = new ViewModelProvider(requireActivity(),viewModelFactory).get(CurrentDenominationViewModel.class);
observer = new CurrentVoucherChangedObserver();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_purchase_order, container, false);
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
currentDenominationViewModel.getCurrentVoucherStatisticsLiveData().observe(requireActivity(), observer);
}
#Override
public void onAttach(#NonNull Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
#Override
public void onDetach() {
super.onDetach();
currentDenominationViewModel.getCurrentVoucherStatisticsLiveData().removeObserver(observer);
}
final class CurrentVoucherChangedObserver implements Observer<VoucherStatistics> {
#Override
public void onChanged(VoucherStatistics x) {
String denomination = x.getDenomination()+"";
binding.tvDenomination.setText(denomination);
String stockAmount = requireContext().getResources().getString(R.string.StockAmount);
String text= "("+String.format(stockAmount,x.getQuantity()+"")+")";
binding.tvInStock.setText(text);
}
}
}
Your Solution
change your getItem() method to
switch (position) {
case 0:
return new StoresFragment();
case 1:
return new OrdersFragment();
default:
return null;
}
So, what my problem is that in one fragment(w/i a viewpager, I'll call this Fragment A) I click on this dynamically created button that adds a new fragment(I'll call this Fragment B) in a framelayout which allows me to use PayPal service. On PayPal Activity result, Fragment B communicates with the main Activity via a communicator(an interface class) to call Fragment A to change that text. But I'm getting a null pointer exeception crash.
To be specific:
what I did was that I made a global TextView variable that is initialized on click. I did this b/c I have a list of other things that are dynamically inflated and to avoid the TextView from being initialized with wrong layout I initialized it on click.
bidChange.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
eventListChangeKey = keyVal;
eventListChangeIdx = eventListIdx;
eventBiddingChangeIdx = finalI;
priceToChage = (TextView) biddersLayout.findViewById(R.id.single_list_bidder_bid_price);
Bundle bundle = new Bundle();
bundle.putInt("auctionID", auctionId);
bundle.putInt("dateID", dateId);
bundle.putInt("FromWhere", 2);
Fragment fragment = new Fragment_Home_ItemInfo_Bid();
fragment.setArguments(bundle);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.container_mainScreen, fragment, "itemInfo_bid")
.addToBackStack(null)
.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
});
In the main activity
public void changeBidderPrice(String s) {
Fragment fragment = viewPagerAdapter.getItem(1);
((Fragment_List) fragment).changePrice(s);
}
is what I do
back in Fragment A
public void changePrice(String val) {
priceToChage.setText(val);
dataMap.get(eventListChangeKey).get(eventListChangeIdx).getBidList().get(eventBiddingChangeIdx).setPrice(val);
}
I've thought this over an over but I just can't figure this out. I've searched for similar cases in StackOverflow but I wasn't able to get a help.
Would the problem be the way I initialize that TextView? or is it the way I'm calling Fragment A from the main activity?
for fragments onViewCreated() is called after onCreateView() and ensures that the fragment's root view is non-null. Any view setup should happen here. E.g., view lookups, attaching listeners.
source : codepath
for activities onCreate()
In my custom view I have a Java class running a simple game. When the game is finished I'd like to display my DialogFragment, but the getFragmentManager() method seems to be undefined.
FragmentManager manager = getFragmentManager();
Finish finish = new Finish();
finish.show(manager, "done");
I've tried getting the manager through a Fragment obj as:
Fragment fragment = new Fragment();
FragmentManager manager = fragment.getFragmentManager();
But that returns as null. I know it's a new Fragment instance, but I'm not sure what value to give it. Any help would be much appreciated.
if your view is attached to an Activity then simply you can do
((Activity)getContext()).getFragmentManager();
or
((ActivityCompat)getContext()).getSupportFragmentManager();
and to be more safe please make sure you check against of the View Context is instance of an Activity by doing such:
if(getContext() instanceof Activity)// do something
and a better solution is, i had rely on a callback between the View and the Activity.
I use that simple helper method:
fun getFragmentManager(context: Context?): FragmentManager? {
return when (context) {
is AppCompatActivity -> context.supportFragmentManager
is ContextThemeWrapper -> getFragmentManager(context.baseContext)
else -> null
}
}
For AndroidX, you can try:
ContextThemeWrapper themeWrapper = (ContextThemeWrapper) getContext();
FragmentManager fm = ((AppCompatActivity) themeWrapper.getBaseContext()).getSupportFragmentManager();
As recommoned by #k0sh, instaceof safety check is recommonded.
You can use this (in Kotlin):
if ((context as ContextThemeWrapper).baseContext is AppCompatActivity) {
//View is attached to an AppCompatActivity
} else {
//View is not attached to an AppCompatActivity
}
}
I keep having an issue with my android app where it is crashing with the following error when swiping between tabs:
09-16 16:19:27.142 4750-4750/com.khackett.runmate E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.khackett.runmate, PID: 4750
java.lang.IllegalStateException: Content view not yet created
at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
at com.khackett.runmate.ui.MyRunsFragment$1.done(MyRunsFragment.java:167)
at com.khackett.runmate.ui.MyRunsFragment$1.done(MyRunsFragment.java:135)
at com.parse.ParseTaskUtils$2$1.run(ParseTaskUtils.java:115)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
This is the MyRunsFragment:
public class MyRunsFragment extends ListFragment {
protected SwipeRefreshLayout mSwipeRefreshLayout;
// member variable to store the list of routes the user has accepted
protected List<ParseObject> mAcceptedRoutes;
private int MY_STATUS_CODE = 1111;
// Default constructor for MyRunsFragment
public MyRunsFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_my_runs, container, false);
// Set SwipeRefreshLayout component
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeRefreshLayout);
// Set the onRefreshListener
mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
mSwipeRefreshLayout.setColorSchemeResources(
R.color.swipeRefresh1,
R.color.swipeRefresh2,
R.color.swipeRefresh3,
R.color.swipeRefresh4);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Retrieve the accepted routes from the Parse backend
retrieveAcceptedRoutes();
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
// create the message object which is set to the message at the current position
ParseObject route = mAcceptedRoutes.get(position);
// String messageType = message.getString(ParseConstants.KEY_FILE_TYPE);
JSONArray parseList = route.getJSONArray(ParseConstants.KEY_LATLNG_POINTS);
JSONArray parseListBounds = route.getJSONArray(ParseConstants.KEY_LATLNG_BOUNDARY_POINTS);
String objectId = route.getObjectId();
String routeName = route.getString(ParseConstants.KEY_ROUTE_NAME);
// JSONArray ids = route.getJSONArray(ParseConstants.KEY_RECIPIENT_IDS);
// Start a map activity to display the route
Intent intent = new Intent(getActivity(), MapsActivityTrackRun.class);
intent.putExtra("parseLatLngList", parseList.toString());
intent.putExtra("parseLatLngBoundsList", parseListBounds.toString());
intent.putExtra("myRunsObjectId", objectId);
intent.putExtra("myRunsRouteName", routeName);
// Start the MapsActivityDisplayRoute activity
startActivityForResult(intent, MY_STATUS_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
private void retrieveAcceptedRoutes() {
// query the routes class/table in parse
// get messages where the logged in user ID is in the list of the recipient ID's (we only want to retrieve the messages sent to us)
// querying the message class is similar to how we have been querying users
ParseQuery<ParseObject> queryRoute = new ParseQuery<ParseObject>(ParseConstants.CLASS_ROUTES);
// use the 'where' clause to search through the messages to find where our user ID is one of the recipients
queryRoute.whereEqualTo(ParseConstants.KEY_ACCEPTED_RECIPIENT_IDS, ParseUser.getCurrentUser().getObjectId());
// order results so that most recent message are at the top of the inbox
queryRoute.addDescendingOrder(ParseConstants.KEY_CREATED_AT);
// query is ready - run it
queryRoute.findInBackground(new FindCallback<ParseObject>() {
// When the retrieval is done from the Parse query, the done() callback method is called
#Override
public void done(List<ParseObject> routes, ParseException e) {
// dismiss the progress indicator here
// getActivity().setProgressBarIndeterminateVisibility(false);
// End refreshing once routes are retrieved
// done() is called from onResume() and the OnRefreshListener
// Need to check that its called from the the OnRefreshListener before ending it
if (mSwipeRefreshLayout.isRefreshing()) {
mSwipeRefreshLayout.setRefreshing(false);
}
// the list being returned is a list of routes
if (e == null) {
// successful - routes found. They are stored as a list in messages
mAcceptedRoutes = routes;
// adapt this data for the list view, showing the senders name
// create an array of strings to store the usernames and set the size equal to that of the list returned
String[] usernames = new String[mAcceptedRoutes.size()];
// enhanced for loop to go through the list of users and create an array of usernames
int i = 0;
for (ParseObject message : mAcceptedRoutes) {
// get the specific key
usernames[i] = message.getString(ParseConstants.KEY_SENDER_NAME);
i++;
}
// Create the adapter once and update its state on each refresh
if (getListView().getAdapter() == null) {
// the above adapter code is now replaced with the following line
RouteMessageAdapter adapter = new RouteMessageAdapter(getListView().getContext(), mAcceptedRoutes);
// Force a refresh of the list once data has changed
adapter.notifyDataSetChanged();
// need to call setListAdapter for this activity. This method is specifically from the ListActivity class
setListAdapter(adapter);
} else {
// refill the adapter
// cast it to RouteMessageAdapter
((RouteMessageAdapter) getListView().getAdapter()).refill(mAcceptedRoutes);
}
}
}
});
}
protected SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
// When list is swiped down to refresh, retrieve the users runs from the Parse backend
retrieveAcceptedRoutes();
}
};
}
And the fragment_my_runs layout file:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity$PlaceholderFragment">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:clipToPadding="false"
android:paddingBottom="#dimen/inbox_vertical_margin"/>
</android.support.v4.widget.SwipeRefreshLayout>
<TextView
android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/empty_inbox_label"
android:textSize="#dimen/default_text_size"/>
</RelativeLayout>
The TabFragmentContainer
public class TabFragmentContainer extends Fragment {
// Create the FragmentPagerAdapter that will provide and manage tabs for each section.
public static MyFragmentPagerAdapter myFragmentPagerAdapter;
public static TabLayout tabLayout;
// The ViewPager is a layout widget in which each child view is a separate tab in the layout.
// It will host the section contents.
public static ViewPager viewPager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate tab_layout_fragment_container view and setup views for the TabLayout and ViewPager items.
View view = inflater.inflate(R.layout.tab_layout_fragment_container, null);
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
// Set up the ViewPager with the sections adapter.
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
// Instantiate the adapter that will return a fragment for each of the three sections of the main activity
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity(), getChildFragmentManager());
// Set up the adapter for the ViewPager
viewPager.setAdapter(myFragmentPagerAdapter);
// Runnable() method required to implement setupWithViewPager() method
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem(1, false);
// tabLayout.getTabAt(1).select();
}
});
// Return the created View
return view;
}
}
The FragmentPagerAdapter:
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
// The context to be passed in when the adapter is created.
private Context mContext;
// The number of tabs in the layout.
public static int numberOfTabs = 3;
/**
* Default constructor that accepts a FragmentManager parameter to add or remove fragments.
*
* #param context the context from the activity using the adapter.
* #param fragmentManager the FragmentManager for managing Fragments inside of the TabFragmentContainer.
*/
public MyFragmentPagerAdapter(Context context, FragmentManager fragmentManager) {
super(fragmentManager);
mContext = context;
}
/**
* Method to return the relevant fragment for the selected tab.
*/
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new MyRunsFragment();
case 1:
return new InboxRouteFragment();
case 2:
return new FriendsFragment();
}
return null;
}
/**
* Method that gets the number of tabs in the layout.
*
* #return the number of tabs in the layout.
*/
#Override
public int getCount() {
return numberOfTabs;
}
/**
* Method that returns the title of each tab in the layout.
*/
#Override
public CharSequence getPageTitle(int position) {
Locale locale = Locale.getDefault();
switch (position) {
case 0:
return mContext.getString(R.string.title_section1).toUpperCase(locale);
case 1:
return mContext.getString(R.string.title_section2).toUpperCase(locale);
case 2:
return mContext.getString(R.string.title_section3).toUpperCase(locale);
}
return null;
}
}
The tab_layout_fragment_container file that contains the ViewPager widget:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/ColorPrimaryPurple"
app:tabGravity="fill"
app:tabIndicatorColor="#color/ColorPrimaryPurple"
app:tabMode="fixed"
app:tabSelectedTextColor="#color/textColorPrimary"
app:tabTextColor="#color/pressedPurpleButton">
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
</LinearLayout>
The onCreate() method in my MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialise the DrawerLayout and NavigationView views.
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
mNavigationView = (NavigationView) findViewById(R.id.navigationDrawerMenu);
// Inflate the first fragment to be displayed when logged into the app.
mFragmentManager = getSupportFragmentManager();
mFragmentTransaction = mFragmentManager.beginTransaction();
mFragmentTransaction.replace(R.id.containerView, new TabFragmentContainer()).commit();
// Setup click events on the NavigationView items.
// When an item is selected, replace the tab fragment container with the requested fragment.
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
mDrawerLayout.closeDrawers();
if (menuItem.getItemId() == R.id.navItemHome) {
FragmentTransaction tabFragmentContainer = mFragmentManager.beginTransaction();
tabFragmentContainer.replace(R.id.containerView, new TabFragmentContainer()).commit();
}
if (menuItem.getItemId() == R.id.navItemRunHistory) {
FragmentTransaction runHistoryFragment = mFragmentManager.beginTransaction();
runHistoryFragment.replace(R.id.containerView, new RunHistoryFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemSettings) {
FragmentTransaction settingsFragment = mFragmentManager.beginTransaction();
settingsFragment.replace(R.id.containerView, new SettingsFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemHelp) {
FragmentTransaction instructionsFragment = mFragmentManager.beginTransaction();
instructionsFragment.replace(R.id.containerView, new InstructionsFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemMyProfile) {
FragmentTransaction myProfileFragment = mFragmentManager.beginTransaction();
myProfileFragment.replace(R.id.containerView, new MyProfileFragment()).commit();
}
if (menuItem.getItemId() == R.id.navItemLogOut) {
// User has selected log out option. Log user out and return to login screen.
ParseUser.logOut();
navigateToLogin();
}
return false;
}
});
// Set up the Toolbar.
setupToolbar();
}
I have followed other answers here and added the getListView() functionality to the onViewCreated() method but the problem still persists... Can anyone point out where I might be going wrong?
Based on these facts:
The exception is thrown because there is no root view yet when done() calls getListView().
done() is called when the query made by retrieveAcceptedRoutes() gets a response.
retrieveAcceptedRoutes is called in multiple places, including the OnRefreshListener mOnRefreshListener, which is registered as the refresh listener in onCreateView() before there is a root view (that is, before onCreateView() returns).
...it is possible for getListView() to be called before there is a root view.
Try moving these 3 statements from onCreateView() to onViewCreated(), so that way the refresh listener can only be called when there is a root view.
// Set SwipeRefreshLayout component
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeRefreshLayout);
// Set the onRefreshListener
mSwipeRefreshLayout.setOnRefreshListener(mOnRefreshListener);
mSwipeRefreshLayout.setColorSchemeResources(
R.color.swipeRefresh1,
R.color.swipeRefresh2,
R.color.swipeRefresh3,
R.color.swipeRefresh4);
onViewCreated is called immediately after onCreateView, but the super.onViewCreated call is missing, perhaps this is root cause of your issue.
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); // add this line back in
// Retrieve the accepted routes from the Parse backend
retrieveAcceptedRoutes();
}
I read your question again then I guess that:
Your ListFragment is destroyed while your background task keeps running. So when it's done, your callback would like to update the ListView which is no longer alive.
Actually, viewPager.setOffscreenPageLimit(3); may do the trick, but it's not a good practice. It forces your ViewPager to create and store more Fragments in memory which is not necessary. You can solve this without doing so.
What you should do: one of the following two practice should be fine, or both:
Destroy your task in your onPause or whatever lifecycle method, before your onDestroyView.
Exclude the code where you update your ListView inside your done() method. Make it a local method where you will check your ListView carefully, and there, you should ask your update process to run on UI thread to avoid any threading problem. Make sure to check if your getView() is not null (but not your getListView(), since it throws Exception if getView() returns null).
I recommend you to use both of them to make sure: your view is still useable and you don't waste your resource when running task in invisible fragment. Don't forget that by default, once your fragment is invisible, it is considered to be destroyed (not always, for example ViewPager keep reference of 2 fragments, but keep in mind that case).
remove all these imports:
import com.yourName.runmate.R;
Then resync your gradle and rebuild your project.
Also see here:
"cannot resolve symbol R" in Android Studio
edit
Your first obvious mistake in your Main is
mFragmentManager = getSupportFragmentManager();
should be:
mFragmentManager = getFragmentManager();
or change your Main activity to:
MainActivity extends FragmentActivity to make use of the support fragment manager.
You have a lot of unnecessary code in your question, majority of comments can be removed and imports for the purpose of this question.
What I have come up with is there is no activity, being used. The ListFragment needs to be attached to an Activity or you are trying to call that activity view before it is created.
java.lang.IllegalStateException: Content view not yet created
at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
If you are using Main, then you are not pulling them together well, from what I can see.
Firstly:
Take everything out of your onCreate and onCreateView (for all fragments) except the view inflater.
Place all the extra code into either onViewCreated or onActivityCreated. That way no methods can be called on a null view, as these are called after it is created.
Secondly, you need to sort out your activities and with what you're exactly trying to achieve.
You want a page viewer and a fragmentlist. The pageviewer needs to be associated with an activity, or activity fragment, not a fragment. Otherwise there is no view to attach the pageviewer pages to.
Use a FragmentActivity not a Fragment. To be the activity you run your ListFragment from.
public class TabFragmentContainer extends FragmentActivity {
MyPageAdapter pageAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tab_layout_fragment_container); // change to view layout.
// Instantiate the adapter that will return a fragment for each of the three sections of the main activity
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getFragmentManager(), getFragments());
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
// Set up the ViewPager with the sections adapter.
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
// Set up the adapter for the ViewPager
viewPager.setAdapter(myFragmentPagerAdapter);
}
}
I would suggest putting this into your ListFragment, to ensure your activity is created. You will need to move most of your code from your onCreate methods and put them in onViewCreated or onActivityCreated
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ArrayAdapter adapter = ArrayAdapter.createFromResource(getActivity(), R.layout.my_listview)layout, android.R.layout.simple_list_item_1);
setListAdapter(adapter);
getListView().setOnItemClickListener(this);
}
This code is just a guide, you'll need to tweak it.
Let me know if this helps.
These Q&As are excellent.
Content view not yet created
android Illegal state exception content view not yet create?
Fragment same principles applies to viewpager fragments ViewPager
Try to declare:
viewPager = (ViewPager) view.findViewById(R.id.viewpager);
viewPager.setAdapter(myFragmentPagerAdapter);
myFragmentPagerAdapter = new MyFragmentPagerAdapter(getActivity(), getChildFragmentManager());
before:
tabLayout = (TabLayout) view.findViewById(R.id.tabs);
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
viewPager.setCurrentItem(1, false);
// tabLayout.getTabAt(1).select();
}
});
// Return the created View
return view;
Objective: To use fragment arguments to pass along the string value from a TextView to a new fragments TextView, BUT while using a ViewPager with different layouts/fragments in the FragmentPagerAdapter.
Problem: The new fragment never receives the fragments arguments from the previous fragment.
My Set up: I have my Activity hosting the ViewPager and FragmentPagerAdapter. I have overridden FragmentPagerAdapters getItem(int position) method to create a new fragment instance depending on the current position of the ViewPager. Right now I only have two Fragments in my adapter, but will be adding more after getting over this obstacle. I am using the newInstance() technique to pass along the Fragment's arguments, but nothing is ever passed.
Pertinent Code:
My FragmentPagerAdapter code:
//this is a variable that I pass in as the newInstanct() methods parameter,
// I update this variable from my fragments constantly
public static String fragmentArgumentsValue = "";
mViewPager.setAdapter(new FragmentPagerAdapter(fm) {
#Override
public int getCount() {
return NUMBER_OF_VIEWS;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
Log.d(TAG, "---In getPosition(), position 0---");
return Frag1
.newInstance(fragmentArgumentsValue);
case 1:
Log.d(TAG, "---In getPosition(), position 1---");
return frag2
.newInstance(fragmentArgumentsValue);
default:
Log.d(TAG, "---In getPosition(), DEFAULT---");
return frag1
.newInstance(fragmentArgumentsValue);
}
}
});
One of the fragments newInstance() method:
public static Fragment newInstance(String fragmentArgumentsValue) {
Frag1 f = new Frag1();
Bundle bun = new Bundle();
bun.putString(KEY_FRAGMENT_ARGUMENTS_STRING, fragmentArgumentsValue);
f.setArguments(bun);
return f;
}
Getting the fragments arguments:
String argString = getArguments().getString(
KEY_FRAGMENT_ARGUMENTS_STRING);
if (argString.length() != 0) {
Log.d(TAG, "Trying to set the frag args to:" + argString);
mWorkingTextView.setText("" + argString);
} else {
Log.d(TAG, "Couldn't set frag args to: " + argString);
}
What I've Tried: I've tried giving the Activity that hosts the ViewPager and FragmentPagerAdapter a static variable that I constantly update in each one of my fragments, I include the static variable in the fragment.newInstance(staticVariableInActivity) method, but this doesn't seem to work.
I've also tried using the ViewPager callback viewPager.setOnPageChangeListener() I had overridden the onPageSelected(int pos) and tried to pass the fragment arguments there, nevertheless it didn't work... so please help me S.O.!!!
My thoughts: I do not have the different fragments and layouts in an ArrayList or any list for that matter, I just instantiate the Fragments via the newInstance() method depending on the positions of the FragementPagerAdapter, could this be a problem? Should I create a singleton list of the layouts/fragments? So I can change the values of the Fragments TextViews via the singleton list? (excuse me if that's a dumb or not possible thing to do).
Note: I am am saving away the TextViews values via public void onSaveInstanceState(Bundle outState) to handle screen rotations. Could this be a problem?
Alright so I this link will help in the communication between fragments: Communication with other Fragments .
You need to define a interface that the Activity will implement and the fragments will use to send data onward to the activity, and the activity will then find the fragment by tag doing something like this:
frag = (Fragment2) getSupportFragmentManager()
.findFragmentByTag(
"android:switcher:" + R.id.viewPager + ":2");
and then update the fragment by calling a public method implemented within the fragment.
The link provided will help greatly, it is from the Android Development website.
Adds the bundle inside your Adapter.
example in the constructor of the adapter :
public ViewPagerAdapter(Context context, FragmentManager fm) {
super(fm);
fragments.add(TileFragments.newInstance());
Bundle bundleFeatures = new Bundle();
bundleFeatures.putString(ContentName.FEATURES.toString(),ContentName.FEATURES.toString());
fragments.get(0).setArguments(bundleFeatures);
}