In my Android project I have an Activity that uses a Master-Detail view, created with two fragments.
My detailfragment is giving me some "problems" though.
It consists of 50+ controls (TextViews, EditTexts, CheckBoxes, Spinner). Of this 50+ controls I programmatically get a reference to 32 of these controls in my detail-fragment and load their data from my SQLite database.
When I run this and initialize my controls by using
(SomeControl).findViewById(R.id.mycontrol);
LogCat keeps warning me about that I might be doing too much on the main thread.
I know that findViewById and inflating views is an expensive operation, so I had an idea!
I was wondering if there was some way to use the viewholder pattern or view-recycling on my detail-fragment like I'm doing on my ListFragment. that way I could avoid reinitializing my detailview each time I select another item in my ListView. And avoid calling .findViewById as much as I do. Does anyone have an idea on how to implement something like this. Would it make any difference if I did initialization of the controls in the onCreate-method of my detailsfragment? I was also thinking about making my detailsfragment a "singleton" and then just use getLoaderManager().restartLoader when the selection of my listfragment changes. Any thoughts on all of this would be very much appreciated.
Well unless you're using the exact same layout for each control then I'm not sure if there's a way to do that.
But there may be a way to solve your problem: using an AsyncTask.
As long as those controls aren't necessary for your program to crunch data (and from your explanation I don't think the UI elements would be triggered until the user has interacted with it) you should be fine and the main thread will be free to do whatever it needs. The only downside I see with this method would be that some UI elements might appear maybe a half a second late (not so bad if you think about it).
Found a solution. Had to change my implementation entirely though.
Now my implementation is using loaders on both ListFragment and DetailFragment.
Here's a list of changes I've made:
Created a interface for my ListFragment with one method (onSomethingClicked(SomeObject obj)) and made ListFragment observable through this interface.
Implemented the interface in my DetailFragment and registered a listener on the ListFragment.
Implemented the method onSomethingClicked() in DetailFragment. When this is triggered i pass data from ListFragment to DetailFragment and restart my DetailFragment-loader and load data into my already initialized controls in OnLoadFinished.
No need to inflate the view every time the selection in the list changes and no need to .findViewById's AND MOST IMPORTANTLY no more Choreographer warnings :)
Related
I have a little app wich contain MainActivity and two subclasses of View - FirstScreenView and SecondScreenView.
Now MainActivity manages both subclasses and output them successive.
I want to show both classes in one time in a one screen (in this layout - firstscreen.xml that is used instead of main.xml now. main.xml does not exist).
I know that this challenge can be overcome by applying the fragment. But my classes extends View, not Fragment.
May two View classes work as two Fragment in one screen?
Please, tell me in what way and how should I solve this problem.
Thank you (and thanks Google Translate).
Talking from a UI perspective, the app will look the same. Talking from a functional perspective, the app could do the same. The only difference is that if you use fragments, you will have to manage both lifecycles.
I rather preffer what you have done, working with these two views. Remember that if you work with custom views, it is a best practice to delegate its logic to the activity, something you will relay to the fragmeents in the case you use them.
After spending a fair bit of time figuring out that the reason my fragments chosen from a drawer layout weren`t displaying sometimes due to the choreographer skipping frames (I was using transaction.replace rather than show/hide) it made me wonder -- what are the situations where one would want to use replace rather than show/hide or detach/reattach? My problem went away when I switched to using show/hide btw.
Taken from this thread I got this on what happens when you call FragmentTransaction.replace():
Android will effectively perform a sequence of
FragmentTransaction.remove(...) (for all Fragments currently added to
that container) and FragmentTransaction.add(...) (for your supplied
Fragment). Removing a Fragment from the FragmentManager will cause the
Fragment to be destroyed and its state will no longer be managed. Most
noticeably, when you re-add the Fragment all of the views will have
been reset. Note: since you are reusing the same Fragment instance,
the Fragment will still keep the value any instance variables.
and from this thread I got that it is probably better to show/hide rather than replace if you plan on using that fragment again. My question is, in which situations do you use FragmentTransaction.Replace()? The only place I could see it really being useful is for something you know you won`t need again, kind of like a dialog picker with options but I use dialog fragments for those situations.
Does anyone use FragmentTransaction.replace regularly, and if so, why did you choose that over another method? Cheers
It maybe useful, for example, when implementing a deep fragments hierarchy in Multi-pane pattern (when click on item in the right fragment moves it to the position of the left).
Also, since hiding a Fragment keeps it in FragmentManager, it maybe expensive if you have a heavy content in it or hide multiple instances. Calling remove() or replace() and properly saving fragment's state is more Android-way, I think.
I have an activity with a ViewPager with a variable number of fragments (tabs).
Upon start the activity checks if the associated (complex) data has been loaded. If it hasn't it shows a progress bar view and starts an AsyncTask which fetches the data. Depending on the data the activity creates a number of fragments (tabs) and gives each fragment a sub set of the data.
I currently hold references to the fragments (I know that it is discouraged) and I run into all sorts of problems when the fragments gets reused - I'm giving the data to the wrong instance of the fragment.
So, is there an "android way" of solving this problem?
I run into all sorts of problems when the fragments gets reused
Fragments usually don't get reused in a ViewPager. This is not like an AdapterView where rows get recycled. Using FragmentPagerAdapter or FragmentStatePagerAdapter, a fragment represents one and only one page.
I re-instantiate the ViewPager each time (but the fragments get reused?)
Ah. That's a slightly different problem.
The stock implementations of FragmentPagerAdapter and FragmentStatePagerAdapter make a couple of assumptions:
They are in complete control over the fragments, particularly in terms of running the transactions to add and remove them from the UI
That those fragments will only ever be used by one "logical" ViewPager (IOW, recreating that ViewPager for a configuration change is fine, but that' pretty much it)
Complicating matters is that these adapters store the fragments under certain tags, and therefore if those fragments still exist in those tags, those existing ones will get used, instead of new ones being created.
So, is there an "android way" of solving this problem?
It's unclear from your question why you even need to "re-instantiate the ViewPager". I'm assuming that this is tied to some sort of refresh operation, or something else that is forcing you to go through the process described in your second paragraph.
You could give my ArrayPagerAdpater a try, as it is friendlier about external agents mucking about with the fragments. Since you control the fragments' tags, you can always be certain that you are working with the right fragment -- rather than caching them yourself, just retrieve the right one and manipulate it.
I need to know when fragments(and which fragments) are added/removed/replaced, especially when popped from backstack. I'd like to be notified both before and after the transactions occur, as I sometimes want to delay a transaction for changing an underlaying layout. So in general, I'd like to run some code before and after the transaction plus eventually delay the transaction. I've already thought about using custom animations for that purpose but transactions only accept ids, no objects/classes. And there might be a better solution I currently can't think of. Any ideas?
I've been using the fragments for quite a while and if you want something ready out-of-the-box you're out of luck.
The only listener available is the addOnBackStackChangedListener and that's it.
But remember that every call that creates and commits a fragment transaction is made by you, either directly via code or indirectly via fragments instantiated on a XML layout or passed to you via the Actionbar tab.
So based on that you should be able to organize your code in some way to always call through a wrapper, but it will be a big work for sure.
I am trying to update my listfragment when a button is pressed. the button puts a name into a database but how do I update the list so that name appears? the listfragment uses a cursorLoader to load from the database. I even tried using a content observer on the database and calling getLoaderManager().restartLoader() but that didnt work either.
So what can I do to update the list when I click a button in an activity? I have found very little information on this please help
I can't be sure, but I assume there is something weird going on with the interaction between your Fragment and your Activity, which you are apparently displaying simultaneously somehow. Is there any reason why you are listening for the Button in the Activity? Are you dealing with a multi-pane screen or something? It seems like it'd be a lot simpler to just have the Fragment deal with this itself (since it is the one implementing the LoaderCallbacks, I assume).
Also, be sure that you are implementing your Fragment lifecycle correctly... i.e. you call initLoader() and set the list's adapter in onActivityCreated() as opposed to onCreate().
Hope that helps!