How to use EditText from a single fragment across tabs? - android

I'm having difficulty understanding the lifecycle of my fragment when the same fragment is created across three tabs. The main widget in each fragment is an EditText object that that is tracked in a class I created, one per tab, created when my app first executes. When the app executes the input (which is provided by buttons on the screen, not a keyboard) sets text into the second tab's EditText.
The input appears to be following getItem() inside my FragmentPagerAdapter. I understand that getItem() is called twice when my tabs are created and that this is perfectly normal behavior but what I'm not understanding is if this is a focus issue or a logic issue with my code design (I'm hoping the former). Could it have to do with instantiate() being returned by getItem()? Once instantiate() is called, does focus always follow it since I have this line of code in the onCreateView() of my fragment?
active.editLine = (EditText) v.findViewById(R.id.display);
// active is the currently active object of my app's class.
The reason I suspect instantiate() is causing my error is because when I increased the rendering of pages via setOffscreenPageLimit(2) the text was being sent to the third tab.
At this point I tried to track what was happening where and it was a nice educational exercise. What I found in my struggles was this is probably a focus issue-- however my attempts at clearing focus and setting focus did not appear to be applied, nor am I even sure if that's the underlying issue. I ended up using active.editLine.debug(0) to see if focus was being lost or gained and it wasn't. I read some of this fairly popular post and took my focus out of my XML but that didn't help either.
Just to make sure, where should I be setting my EditText objects? In OnCreateView() in my fragment? That is where the above code currently lives. I create three objects of my custom class in my activity as global variables. I have getters and setters in what I feel are appropriate areas of my code. Should the above code instead live in onCreate() of my fragment? If you think this is a focus issue, what is the accepted method of turning off focus and only using it when I need it? More importantly, when my app first runs, where do I turn on focus? OnTabSelected() is called before getItem() runs twice to render the first two tabs. So where would I gain initial focus if I'm globally turning it off? Can set focus with a type of listener?

Not exactly sure what you're going for here, but here are some suggestions:
have your global active object live in the main activity and call getActivity().someObject from your fragments
findViewById() will search down the view hierarchy until it finds the first instance of R.id.display
your fragment views won't be available until you commit them with a fragment transaction, this means that their views also won't be available until that time
you are "scoping" findViewById() to some view v by calling v.findViewById()
if you are paging through fragments, perhaps try setting your active view in the fragment's onResume() method
the fragment's onCreateView() method is only called once. This will be called between onCreate(Bundle) and onActivityCreated(Bundle).

Related

Fragment view not updating after reopening

I have a couple text views in a nested fragment that updates as you touch different objects. When I first enter the fragment it works then when I replace the fragment and reopen it. All the views are click able but my text views are not updating. If I do
((TextView)findViewById(R.id.txt_subtitle)).getText()
it returns the updated value as if it was working but the screen is not getting updated.
Its like i have an invisible copy of the text view some where
Unfortunately I can't post the source.
I'm not sure if this would work, since you haven't shown the code, but maybe you can override the onPause() method in the fragment to make the MainActivity class store the TextView information. Then, when you replace the next fragment with the fragment that's giving you the issue, you can set the TextViews to the data that was stored in the MainActivity. Does that make any sense?
EDIT: it might be the onDestroy() method if you don't invoke addToBackStack()

Situation where you would use FragmentTransaction.replace instead or show/hide or detach/reattach?

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.

How to update controls in all tabs created using FragmentPagerAdapter?

I have created a tabbed android application using android.support.v4.view.PagerAdapter.
There are about seven tabs in the application and I plan to add more. Each tab contains a lot of controls (TextView, Spinners, Toggle buttons, Check boxes etc.)
On the first tab there is a drop down to select a profile. The user can 'Load' or 'Save' a profile he wants.
A profile contains data for all the controls in the tabs and I need to update the UI controls in all the tabs.
I have all the data loaded from the profile but the UI controls are not getting updated.
There is a 'UpdateUI' function which calls 'set' functions (setText, setChecked etc. for individual controls after finding its view by ID).
I was informed that only three tabs (Previous, Current and Next) are kept in memory so I wrote the application such that the 'UpdateUI' function is called to set UI data only when user swipes to that particular tab (figuring out the active fragment).
Using DDMS logs I saw that the data loaded was proper but the 'setText' or 'setXXXXX' function does not update the fragment tab.
I have also checked several possible issues:
Possibility of 'OnTextChanged' or an 'OnListener' updating the data again.
Made sure 'UpdateUI' is called from UI thread.
Using notify data change and redrawing UI to make sure UI is updated.
I am a novice Java/Android programmer. Please point me in the right direction.
viewpager.setOffscreenPageLimit(size);
you can instantiate all your fragments by setting limit then you can update all widgets inside other fragments.
If my understanding of Android Fragment management is right, once the fragment becomes invisible for user (say some other fragment completely overlayed it or in your case you change tabs) Fragment goes through onPause and possible onStop lifecycle stages, this mean it's kind of hybernated and can't be changed before it get's visible. viewpager.setOffsetPageLimit(size) tells the fragment manager (or rather pageAdapter) how many fragments should be kept hybernated, and I doubt it playing with this will change anything, but let me know if it's, because otherwise the solution may be more complicated.
What I'd do is recreate a fragment every time user gets to see it and pass your profile data to it's constructor (or following better practice newInstance() static method), it will in fact save memory since keeping many fragments there may be overwhelming. Alternatively you can check what profile is chosen everytime fragment is calling it's onResume, and update your controls there.

Using ViewHolder-pattern in a detail Fragment

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 :)

Why is my fragment onCreate being called extensively whenever I page through my applications viewPager?

I'm not quite understanding this fragment lifecycle business.
I have a pretty standard 3 page horizontal slider view Pager layout for a "view details" section of my app. I start my app on the middle page of the three. My FragmentActivity sets the page to page 1.
mPager.setCurrentItem(1); //default to center page, current data view fragment
I've been using the FragmentStatePagerAdapter because using the FragmentPagerAdapter crashed my app at times, when coming back from a suspended state for example, and this was the quickest way to work around that for now. Seems to work but I suspect the State aspect is what might be the cause of my problem here, maybe.
So at first I thought that I would have each fragment do the work of getting data from a rest service and then showing it in a list, but I'm not so sure any more.
I tried running a unique async task to fetch data in each of the fragments onCreateView events.
Then after reading more on the fragment lifecycle I switched to onCreate, having noticed that the onCreateView was being called quite heavily, which in turn made for a greedy app that too often requested data over the wire.
Switching to onCreate hasn't changed anything. OnCreate is still geting called just as much as onCreateView for the 2 adjacent fragments.
The odd thing is that the fragment that I set to be the first one to display in Fragment Activity only gets the onCreate called the one time.
Something doesn't feel right here.
Right now I'm thinking that I should have the parent fragment activity declare and call all the async tasks to fetch the data that I need to display in the fragments.
Set the results of the async calls in an object owned by the parent fragment activity and then have the fragments use the object contained by the parent to create the list view etc.
But what if the async tasks started by the parent activity don't finish before each fragments onCreateView is called and the object contained by the parent isn't ready yet.....
signed, confused and frustrated
ViewPager is quite zealous in shutting down things it isn't currently using, and this is exactly what is happening here. The default behaviour is for ViewPager to "keep around" one page either side of the page being viewed, and destroy the rest. Hence in your 3-page view, page 3 gets destroyed when selecting page 1, then when page 2 is reselected page 3 is recreated. As you've noticed, page 2 only has onCreate(..) called once because it is always adjacent to, or is, the currently selected page.
To solve this, simply set ViewPager.setOffscreenPageLimit(2). Then the ViewPager will keep all your Fragments. Obviously this isn't a good idea for a large number of Fragments, but for your case it should be fine.
#Espiandev's solution will work for your current case, but you're right that your state is the issue. You should use the setArgument and/or onSaveInstanceState methods to save your Fragment's state (which shouldn't be too hard, since e.g., a response from the server can usually be represented as a String), and then use getArgument and/or the Bundle passed in onCreate to restore it.
Alternatively, you could have your Activity do the server fetches, and then call setArgument for each of your fragments, and check the arguments inside your Fragment to determine if your data has arrived yet (and if not, potentially display a loading state of some kind).
If you care at all about screen orientation change, this related question will also be useful to you.

Categories

Resources