Android Fragment display a loading while rendering layout - android

Let's say I've got an Activity with only a FrameLayout (I've also try with the new FragmentContainerView) so I will be loading Fragments on it. Let's assume I've got two Fragments: fragA and fragB, I will first load fragA and with a button, present in that fragment, replace it with fragB.
I've define a function on my Activity so I can load a new Fragment on the container:
public void loadFragment(Fragment fragment) {
FragmentTransaction fragTrans = getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment)
.addToBackStack(null);
fragTrans.commit();
}
And I will call this from fragA to go to fragB. This far so good, everything works fine. Actually the problem I'm facing is not about functioning, is that I don't like the loading time of fragB, it takes about 2sec, and I'd like to display a loading to the user, but I'm not able to achieve it.
The first thing I've try is adding a gone ProgressBar to the activity layout, where the container is. Then my loadFragment function will first set this progressBar visible and then perform the fragment transaction and each fragment will hide (gone) this View before exiting the onCreateView method. But the problem is that the user never sees this ProgressBar, it's like the main UI is freezed while rendering the layout of the fragB and therefore does not update the UI making this progressBar usesless. I even see some frame skipped in the LogCat about 50 frames.
Both fragments have no logic just the onCreateView implemented where the layout is inflated. I can notice that if I change the layout of fragB to a simpler one it loads instantly, so I'm guessing the issue is related to the time it takes to render the layout. This heavy layout is a really big form with lots of TextInputLayout with TextInputEditText and MaterialSpinner (from GitHub).
I know I maybe can simplify the layout, but I'd like to know how can I display a loading while rendering. For example, I've seen some apps load some kind of dummy-view while loading and then replace it with real data.
One thing I've try is to load a dummy layout with a ProgressBar in the middle and in the onCreateView method post a Handler to inflate a real layout on the same container in the background, like this:
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_dummy, container, false);
Handler mHandler = new Handler();
mHandler.post(new Runnable() {
#Override
public void run() {
container.removeAllViews();
View realView = inflater.inflate(R.layout.fragment_real, container);
}
});
}
This kinda work, as the viewing experience is nice, but when navigating back, the layout of fragB remains visible as background of the other fragments. I have not test it but maybe I can call container.removeAllViews(); before exiting the fragment and it will work, but still seems like a workaround rather than a solution, to me.
And other thing I've not try because maybe is an over-kill is to have an intermediate or loading fragment and load it always before the real fragment, and I will pass an Intent Extra to it so I can tell what's the real fragment I want to display. This intermediate fragment will not be added to the backstack.
How do you solve this kind of problems?

Well it seems that AsyncLayoutInflater is the way to go!
Here's how I use it:
private ViewGroup fragmentContainer;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View dummyView = inflater.inflate(R.layout.fragment_dummy_loading, container, false);
fragmentContainer = container;
AsyncLayoutInflater asyncLayoutInflater = new AsyncLayoutInflater(getContext());
asyncLayoutInflater.inflate(R.layout.fragment_real_layout, container, new AsyncLayoutInflater.OnInflateFinishedListener() {
#Override
public void onInflateFinished(#NonNull View view, int resid, #Nullable ViewGroup parent) {
fragmentContainer.removeAllViews();
fragmentContainer.addView(view);
}
});
return dummyView;
}
#Override
public void onStop() {
super.onStop();
fragmentContainer.removeAllViews();
}
Up there, fragment_dummy_loading is a layout with a ProgressBar and fragment_real_layout is the real heavy layout.
There's just one thing I don't get... and that's how can I bind Objects to the XML widgets (without using Android Data Binding, if possible) when the AsyncLayoutInflater fallsback to infalte in the UI thread.
According to the docs:
If the layout that is trying to be inflated cannot be constructed asynchronously for whatever reason, AsyncLayoutInflater will automatically fall back to inflating on the UI thread.
And I check the source of AsyncLayoutInflater and I can tell that the method onInflateFinished is called no matter if the inflation was in the background or in the main thread

I think you have to use AsycTask when your new fragment loading
1- Load the new fragment
2 -Start Async task
3 - Initialize Progressdialog in constructor of AsyncTask where you want to load the data
4 - Show Progressdialog in onPreExecute() in your AsyncTask with dialog.show()
5 - Dismiss the Progressdialog in onPostExecute() in your AsyncTask with dialog.dismiss()
6- Update the UI / set the new data to your adapter etc
Check here
Show a loading spinner while a fragment is loading

Related

Changing DialogFragment Layouts while dialog is open

I have an Android Activity, from which I want to show a Dialog. It would probably be a custom DialogFragment. Now when the user clicks on specific buttons I want the layout's inside the dialog to change with the data from the previous DialogFragment and so that it would have an ability to also go back to previous Layout.
I dont think there is an easy way to change views inside of the same DialogFragment so what would be the best way to do this?
I have tried doing it in method onViewCreated and when a button is clicked, but nothing happens.
In my activity I call the fragment like this at the moment:
FragmentManager fm = getSupportFragmentManager();
NewDialog newDialog = NewDialog.newInstace(userId, loc, currentId);
newDialog.setNewClickListener(new NewDialog.OnNewClickListener() {
#Override
public void onCancelClicked() {
finishAdd();
}
#Override
public void onAcceptClicked() {
...
}
});
newDialog.show(fm, "new_frag");
And the fragment:
public class NewDeliveryPointDialog extends DialogFragment {
private LayoutInflater inflater;
private ViewGroup container;
public NewDialog(){
}
public static NewDialog newInstace(){
...
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
this.inflater = inflater;
this.container = container;
return inflater.inflate(R.layout.dialog_layout_1, container);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
saveButton.setOnClickListener(v -> {
View.inflate(getContext(), R.layout.dialog_layout_2, container);
view.invalidate();
view.refreshDrawableState();
}
});
}
}
A DialogFragment is not made to have navigation to other fragments within the same dialog.
You basically have these options:
On your button click you close the Dialog and open another Dialog. But this seems odd. If there is so much happening, probably dialogs are not the best shot.
Instead of DialogFragments have another fragment container overlaying the original one (basically what a Dialog fragment does for you). Within the second container you can easily navigate to other fragments and set it to gone when the user finished interaction.
If there are just a few Views in the Dialog, you could consider setting the old ones to gone and the new ones to visible
I think your code didn't work, because container is null. Method onCreateView gives you #Nullable ViewGroup container, which is null for DialogFragment (but non null for Fragment). So when you call View.inflate(getContext(), R.layout.dialog_layout_2, container), it just creates a view in memory and doesn't attach it to container, cause it is null. See LayoutInflater.inflate, cause View.inflate is just a convenience wrapper for this function.
I dont think there is an easy way to change views inside of the same DialogFragment so what would be the best way to do this?
Instead of changing dialog root you can just manipulate child views inside dialog root layout (add, remove them, or change visibility).
Also my advice is to use recommended way to create dialog with custom layout (onCreateDialog + setView), but if you don't want to do that, you can refer view you've created in onCreateView as dialog root.
You can try creating a dialog fragment with an empty shell layout in which you would replace your two different fragments with ChildFragmentManager and regular fragment transactions
passing data between them can be done using the activity's view model since they both live in the same activity.
So add the ShellDialogFragment using the activity's FragmentManager and in the shell fragment class change between NewDialog & NewDeliveryPointDialog on your button click listener with ChildFragmentManager

android Fragments onFocusChanges

I have some code which I run in normal activities in method onFocusChanges, it is important to run it just there as the code requires the activity to be loaded first as it get images width and heights from the view:
in MainActivity
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
SizeModifier sizeModifier = SizeModifier.getInstance(this);
sizeModifier.adjust
}
so all this is working just fine, the problem is this main view contains a fragment changes according to button press like that
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.main_view_fragment,fragment).commit();
the fragment of course has activity and xml, I want now when the fragment open to run the previous code when focus changes but the problem is fragments does not have onFocusChange, I tried all onStart onResume onCreateActivity
but non seems to work as I want, of course the method is called but all images widths are returned 0,
so is there away to be sure that images will return correct width or some alternative to onFocusChanges
Ok, finally I found what could do the purpose,
simply for fragments we add this simple code to the onCreateView after inflating the view like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater .inflate(R.layout.activity_courses_fragment, container, false);
// >>>> this is the thread which will run when every thing is ready
rootView.post(new Runnable() {
#Override
public void run() {
// >>> this method now can get width with no problems
SizeModifier.getInstance(getContext()).adjust(rootView);
}
});
return rootView;
}
special thanks to #Staffan for this idea as solution for another question

Avoid execution of AsyncTask when ViewPager is slided fast

I'm using a ViewPager to show content fetched from a website with jsoup.
In the onCreateView of each page I call an AsyncTask that fetches the data and updates the View for each page.
The problem is that when the user slides the pages faster than usual the AsyncTask is called several times and, consequently, several useless requisitions are made with jsoup, since the only useful is the last.
I tried using setUserVisibleHint on the Fragment class and adding setOnPageChangeListener in the Activity class but these methods make me lose the ViewPager behaviour of preloading the next page and I don't want that.
Is there a way to know when the user stopped sliding and only call the AsynTask at that moment?
public class ScreenSlidePageFragment extends Fragment {
public static final String PAGE_NUMBER = "page";
private int mProblemNumber;
public static ScreenSlidePageFragment create(int pageNumber) {
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
Bundle args = new Bundle();
args.putInt(PAGE_NUMBER, pageNumber);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt(PAGE_NUMBER);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.fragment_screen_slide_page, container, false);
new GetPageTask(url).execute();
return rootView;
}
}
I think the best way would be just to check whether the AsyncTask is running or not. Store a reference to your AsyncTask and then if user scrolls back to this page you can check its status using AsyncTask.Status (http://developer.android.com/reference/android/os/AsyncTask.Status.html).
Also, if you want to avoid starting new tasks when user scrolls too fast, you can use handler.postDelayed(yourRunnable, longMs). Each time user selects a page you can do something like this:
handler.removeCallbacks(yourRunnable);
handler.postDelayed(yourRunnable, longMs);
This way you will remove previous pending task and schedule a new one in longMs time. E.g. if you put 1000 ms then your tasks will start only in a second after user selected a page.
You might want to delay the request to fetch the content. For instance, if you are swiping quickly, waiting like half a second to load the content (instead of right away) would give the system a chance to breathe and check if the page is still visible.
Something like this:
handler.postDelayed(new Runnable(){
if (isVisible){
new GetPageTask(url).execute();
}
}, 500);

How to prevent the reload of a webView in a fragment when another fragment in the top was closed

I'm making an app with webviews.
I just have a single activity containing
a FrameLayout to dynamically add fragments.
Every fragment contains a webView.
Everything works fine, but when I remove a fragment
from the stack, the webview of the fragment in the
top of the stack is reloaded so the content inside the
script of the html is called again.
The restoreState() method is not working.
This is the onCreateView of the fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.wv_container, container, false);
webView = (WebView)view.findViewById(R.id.webviewcontainer);
if(webViewBundle == null)
webView.loadUrl("file:///android_asset/www/main.html");
else
webView.restoreState(webViewBundle);
return view;
}
This is the onPause() method:
#Override
public void onPause() {
super.onPause();
webViewBundle = new Bundle();
webViewContent.saveState(webViewBundle);
}
How should I prevent the page reloading for my case?
I don't want to hack the html with flags or something like that.
Thanks in advance!
The saveState method in your onPause returns a WebBackForwardList, which contains a list of WebHistory items. These are basically the titles, urls and favicons from recently visited pages. It does not cache the contents of the downloaded url.
Try looking at the method saveWebArchive instead.

Android Fragment no view found for ID?

I have a fragment I am trying to add into a view.
FragmentManager fragMgr=getSupportFragmentManager();
feed_parser_activity content = (feed_parser_activity)fragMgr
.findFragmentById(R.id.feedContentContainer);
FragmentTransaction xaction=fragMgr.beginTransaction();
if (content == null || content.isRemoving()) {
content=new feed_parser_activity(item.getLink().toString());
xaction
.add(R.id.feedContentContainer, content)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null)
.commit();
Log.e("Abstract", "DONE");
}
When this code is executed I get the following error in debug..
java.lang.IllegalArgumentException: No view found for id 0x7f080011
for fragment feed_parser_activity{41882f50 #2 id=0x7f080011}
feed_parser_activity is a Fragment that is set to Fragment layout in xml.
I am using a FragmentActivity to host the Fragment Layout holding the feed_parser_layout.
Am I coding this correctly above?
I was having this problem too, until I realized that I had specified the wrong layout in setContentView() of the onCreate() method of the FragmentActivity.
The id passed into FragmentTransaction.add(), in your case R.id.feedContentContainer, must be a child of the layout specified in setContentView().
You didn't show us your onCreate() method, so perhaps this is the same problem.
This error also occurs when having nested Fragments and adding them with getSupportFragmentManager() instead of getChildFragmentManager().
The solution was to use getChildFragmentManager()
instead of getFragmentManager()
when calling from a fragment. If you are calling the method from an activity, then use getFragmentManager().
That will solve the problem.
Another scenario I have met.
If you use nested fragments, say a ViewPager in a Fragment with it's pages also Fragments.
When you do Fragment transaction in the inner fragment(page of ViewPager), you will need
FragmentManager fragmentManager = getActivity().getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
getActivity() is the key here.
...
I had this problem (when building my UI in code) and it was caused by my ViewPager (that showed Fragments) not having an ID set, so I simply used pager.setID(id) and then it worked.
This page helped me figure that out.
In my case I was trying to show a DialogFragment containing a pager and this exception was thrown when the FragmentPagerAdapter attempted to add the Fragments to the pager. Based on howettl answer I guess that it was due to the Pager parent was not the view set in setContentView() in my FragmentActivity.
The only change I did to solve the problem was to create the FragmentPagerAdapter passing in a FragmentMager obtained by calling getChildFragmentManager(), not the one obtained by calling getFragmentManager() as I normally do.
public class PagerDialog extends DialogFragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.pager_dialog, container, false);
MyPagerAdapter pagerAdapter = new MyPagerAdapter(getChildFragmentManager());
ViewPager pager = (ViewPager) rootView.findViewById(R.id.pager);
pager.setAdapter(pagerAdapter);
return rootView;
}
}
This exception can also happen if the layout ID which you are passing to FragmentTransaction.replace(int ID, fragment) exists in other layouts that are being inflated. Make sure the layout ID is unique and it should work.
With Nested fragments
For me by using getChildFragmentManager() instead of getActivity().getSupportFragmentManager() resolved crash
java.lang.IllegalArgumentException: No view found for id
An answer I read on another thread similar to this one that worked for me when I had this problem involved the layout xml.
Your logcat says "No view found for id 0x7f080011".
Open up the gen->package->R.java->id and then look for id 0x7f080011.
When I had this problem, this id belonged to a FrameLayout in my activity_main.xml file.
The FrameLayout did not have an ID (there was no statement android:id = "blablabla").
Make sure that all of your components in all of your layouts have IDs, particularly the component cited in the logcat.
I got this error when I upgraded from com.android.support:support-v4:21.0.0 to com.android.support:support-v4:22.1.1.
I had to change my layout from this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
To this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/container_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</FrameLayout>
So the layout MUST have a child view. I'm assuming they enforced this in the new library.
I know this has already been answered for one scenario, but my problem was slightly different and I thought I'd share in case anybody else is in my shoes.
I was making a transaction within onCreate(), but at this point the view tree has not been inflated so you get this same error. Putting the transaction code in onResume() made everything run fine.
So just make sure your transaction code runs after the view tree has been inflated!
I was facing a Nasty error when using Viewpager within Recycler View.
Below error I faced in a special situation.
I started a fragment which had a RecyclerView with Viewpager (using FragmentStatePagerAdapter). It worked well until I switched to different fragment on click of a Cell in RecyclerView, and then navigated back using Phone's hardware Back button and App crashed.
And what's funny about this was that I had two Viewpagers in same RecyclerView and both were about 5 cells away(other wasn't visible on screen, it was down). So initially I just applied the Solution to the first Viewpager and left other one as it is (Viewpager using Fragments).
Navigating back worked fine, when first view pager was viewable . Now when i scrolled down to the second one and then changed fragment and came back , it crashed (Same thing happened with the first one). So I had to change both the Viewpagers.
Anyway, read below to find working solution.
Crash Error below:
java.lang.IllegalArgumentException: No view found for id 0x7f0c0098 (com.kk:id/pagerDetailAndTips) for fragment ProductDetailsAndTipsFragment{189bcbce #0 id=0x7f0c0098}
Spent hours debugging it. Read this complete Thread post till the bottom applying all the solutions including making sure that I am passing childFragmentManager.
Nothing worked.
Finally instead of using FragmentStatePagerAdapter , I extended PagerAdapter and used it in Viewpager without Using fragments. I believe some where there is a BUG with nested fragments. Anyway, we have options. Read ...
Below link was very helpful :
Viewpager Without Fragments
Link may die so I am posting my implemented Solution here below:
public class ScreenSlidePagerAdapter extends PagerAdapter {
private static final String TAG = "ScreenSlidePager";
ProductDetails productDetails;
ImageView imgProductImage;
ArrayList<Imagelist> imagelists;
Context mContext;
// Constructor
public ScreenSlidePagerAdapter(Context mContext,ProductDetails productDetails) {
//super(fm);
this.mContext = mContext;
this.productDetails = productDetails;
}
// Here is where you inflate your View and instantiate each View and set their values
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.product_image_slide_cell,container,false);
imgProductImage = (ImageView) layout.findViewById(R.id.imgSlidingProductImage);
String url = null;
if (imagelists != null) {
url = imagelists.get(position).getImage();
}
// This is UniversalImageLoader Image downloader method to download and set Image onto Imageview
ImageLoader.getInstance().displayImage(url, imgProductImage, Kk.options);
// Finally add view to Viewgroup. Same as where we return our fragment in FragmentStatePagerAdapter
container.addView(layout);
return layout;
}
// Write as it is. I don't know much about it
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
/*super.destroyItem(container, position, object);*/
}
// Get the count
#Override
public int getCount() {
int size = 0;
if (productDetails != null) {
imagelists = productDetails.getImagelist();
if (imagelists != null) {
size = imagelists.size();
}
}
Log.d(TAG,"Adapter Size = "+size);
return size;
}
// Write as it is. I don't know much about it
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
Hope this was helpful !!
Just in case someone's made the same stupid mistake I did; check that you're not overwriting the activity content somewhere (i.e. look for additional calls to setContentView)
In my case, due to careless copy and pasting, I used DataBindingUtil.setContentView in my fragment, instead of DataBindingUtil.inflate, which messed up the state of the activity.
I had the same issue but my issue was happenning on orientation change. None of the other solutions worked. So it turns out that I forgot to remove setRetainInstance(true); from my fragments, when doing a two or one pane layout based on screen size.
My mistake was on the FragamentTransaction.
I was doing this t.replace(R.layout.mylayout); instead of t.replace(R.id.mylayout);
The difference is that one is the layout and the other is a reference to the layout(id)
This happens when you are calling from a fragment inside another one.
use :
getActivity().getSupportFragmentManager().beginTransaction();
I had this same issue, let me post my code so that you can all see it, and not do the same thing that I did.
#Override
protected void onResume()
{
super.onResume();
fragManager = getSupportFragmentManager();
Fragment answerPad=getDefaultAnswerPad();
setAnswerPad(answerPad);
setContentView(R.layout.abstract_test_view);
}
protected void setAnswerPad(AbstractAnswerFragment pad)
{
fragManager.beginTransaction()
.add(R.id.AnswerArea, pad, "AnswerArea")
.commit();
fragManager.executePendingTransactions();
}
Note that I was setting up fragments before I setContentView. Ooops.
This page seems to be a good central location for posting suggestions about the Fragment IllegalArgumentException. Here is one more thing you can try. This is what finally worked for me:
I had forgotten that I had a separate layout file for landscape orientation. After I added my FrameLayout container there, too, the fragment worked.
On a separate note, if you have already tried everything else suggested on this page (and the entire Internet, too) and have been pulling out your hair for hours, consider just dumping these annoying fragments and going back to a good old standard layout. (That's actually what I was in the process of doing when I finally discovered my problem.) You can still use the container concept. However, instead of filling it with a fragment, you can use the xml include tag to fill it with the same layout that you would have used in your fragment. You could do something like this in your main layout:
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include layout="#layout/former_fragment_layout" />
</FrameLayout>
where former_fragment_layout is the name of the xml layout file that you were trying to use in your fragment. See Re-using Layouts with include for more info.
I fixed this bug, I use the commitNow() replace commit().
mFragment.getChildFragmentManager()
.beginTransaction()
.replace(R.id.main_fragment_container,fragment)
.commitNowAllowingStateLoss();
The commitNow is a sync method, the commit() method is an async method.
I use View Binding in my project and was inattentive to add setContentView() after inflating ActivityHelloWorldBinding class:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHelloWorldBinding.inflate(layoutInflater)
// Add this line.
setContentView(binding.root)
}
In my case I had a SupportMapFragment in a recycler view item (I was using the lower overhead "liteMode" which makes the map appear as non-interactive, almost like a static image). I was using the correct FragmentManager, and everything appeared to work fine... with a small list. Once the list of items exceeded the screen height by a bit then I started getting this issue when scrolling.
Turned out, it was because I was injecting a dynamic SupportMapFragment inside a view, which was inside another fragment, to get around some issues I was having when trying to declare it statically in my XML. Because of this, the fragment placeholder layout could only be replaced with the actual fragment once the view was attached to the window, i.e. visible on screen. So I had put my code for initialising the SupportMapFragment, doing the Fragment replace, and calling getMapAsync() in the onAttachedToWindow event.
What I forgot to do was ensure that my code didn't run twice. I.e. in onAttachedToWindow event, check if my dynamic SupportMapFragment was still null before trying to create a new instance of it and do a Fragment replace. When the item goes off the top of the RecyclerView, it is detached from the window, then reattached when you scroll back to it, so this event is fired multiple times.
Once I added the null check, it happened only once per RecyclerView item and issue went away! TL;DR!
This issue also happens when you don't put <include layout="#layout/your_fragment_layout"/> in your app_bar_main.xml
use childFragmentManager instead of activity!!.supportFragmentManager
I encountered this problem when I tried to replace view with my fragment in onCreateView(). Like this:
public class MyProjectListFrag extends Fragment {
private MyProjectListFragment myProjectListFragment;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
FragmentManager mFragmentManager = getFragmentManager();
myProjectListFragment = new MyProjectListFragment();
mFragmentManager
.beginTransaction()
.replace(R.id.container_for_my_pro_list,
myProjectListFragment, "myProjectListFragment")
.commit();
}
It told me
11-25 14:06:04.848: E/AndroidRuntime(26040): java.lang.IllegalArgumentException: No view found for id 0x7f05003f (com.example.myays:id/container_for_my_pro_list) for fragment MyProjectListFragment{41692f40 #2 id=0x7f05003f myProjectListFragment}
Then I fixed this issue with putting replace into onActivityCreated(). Like this:
public class MyProjectListFrag extends Fragment {
private final static String TAG = "lch";
private MyProjectListFragment myProjectListFragment;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater
.inflate(R.layout.frag_my_project_list, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
FragmentManager mFragmentManager = getFragmentManager();
myProjectListFragment = new MyProjectListFragment();
mFragmentManager
.beginTransaction()
.replace(R.id.container_for_my_pro_list,
myProjectListFragment, "myProjectListFragment")
.commit();
}
You have to return a view in onCreateView() so that you can replace it later
You can put any operation towards this view in the following function in fragment liftcycle, like onActivityCreated()
Hope this helps!
In my case this exception was thrown when I used different ids for the same layout element (fragment placeholder) while having several of them for different Build Variants. For some reason it works perfectly well when you are replacing fragment for the first time, but if you try to do it again you get this exception.
So be sure you are using the same id if you have multiple layouts for different Build Variants.
I was having this problem. In my case I have forgotten to add FrameLayout in my Xml File, after adding frame layout, my problem has been solved.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/wraper"
android:layout_above="#id/wraper"/>
If you are trying to replace a fragment within a fragment with the fragmentManager but you are not inflating the parent fragment that can cause an issue.
In BaseFragment.java OnCreateView:
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.replace(R.id.container, new DifferentFragment())
.commit();
}
return super.onCreateView(inflater, container, savedInstanceState);
Replace super.onCreateView(inflater, container, savedInstanceState);
with inflating the correct layout for the fragment:
return inflater.inflate(R.layout.base_fragment, container, false);
I've had the same problem when was doing fragment transaction while activity creation.
The core problem is what Nick has already pointed out - view tree has not been inflated yet. But his solution didn't work - the same exception in onResume, onPostCreate etc.
The solution is to add callback to container fragment to signal when it's ready:
public class MyContainerFragment extends Fragment {
public static interface Callbacks {
void onMyContainerAttached();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d(TAG, "--- onAttach");
((Callbacks) activity).onMyContainerAttached();
}
//... rest of code
}
And then in activity:
public class MainActivity extends Activity
implements MyContainerFragment.Callbacks
{
#Override
public void onMyContainerAttached() {
getFragmentManager()
.beginTransaction()
.replace(R.id.containerFrame, new MyFragment())
.commit();
}
//...
}
In my case, i was using a fragment class file to declare a listview adapter class.
I just used a different file for the public adapter class and the error was gone.
It happens also when you have two views in two fragments with the same ids

Categories

Resources