I have a main activity in which i have used a view pager.So i can move between 4 tabs and the view pager handles all of that.One of my tabs scans the contacts on the phone to get their details and display it in the recylerview which is in the same tab,this task takes a long time and i am doing this in an async task.Everything is working fine but the problem is if i move to another tab while this scanning is going on the data does not get applied to the recycler view possible because that fragment is being destroyed.
Is there a workaround for this or should i just prevent the user from shifting tabs while the scanning is going on (if so some sort of code or a link to the code would be really helpfull).
I wouldn't recommend you force a user to stay on a page whilst data loads. This sounds like it would only frustrate people. To that end, I have a couple of ideas that should keep your AsyncTask running whilst your Fragment isn't visible.
First, you could call setOffScreenPageLimit(2) on your ViewPager. As you only have four Fragments, this should mean all of them are stored in memory.
viewPager.setOffScreenPageLimit(2);
Another approach is you may be able to create a UI-less Fragment whose sole function is to conduct your AsyncTask and then, once it reaches onPostExecute(), pass the Cursor result to the Fragment that requires it with either an interface or an EventBus of some sort (i.e. LocalBroadcastManager or one of the other many excellent libraries that exist, such as GreenRobot's EventBus).
Edit If you like information on how to create such a "worker" Fragment, then there is a very good and detailed post on androiddesignpatterns.
Related
I'm using the viewpagerindicator library (http://viewpagerindicator.com/) to create some sort of wizard for my android app. It works fine and does exactly what I want.
I would like to "extend" the functionality a bit by having "previous"/"next" buttons in my ActionBar - pretty much as in Android's "Done Bar" tutorial - to step through the wizard. Works like a charm, too.
HOWEVER:
I would like to display information about the "next" & "previous" fragment in the ActionBar's buttons. Information I pass to the fragments that live in the ViewPager at the time of their "creation" (actually at the time of their object instantiation - using the classical "newInstance(...)" approach to create the instance of my fragment, store the parameters in a Bundle and extract them in the fragment's "onCreate" method). The same way the template does it, when you create a new fragment for your project.
So, this information is the thing I actually want to display in my wizards button to know what fragment is next and which was last.
The type of this information is not important for my problem. It could be a String or an icon or an int or ... anything else you want.
However, wherever I've tried to access my fragments data, the fragment has not yet been fully initialized (meaning its "onCreate" method has not been called yet).
I've tried it in the host fragment's "onViewCreated" method, because I thought that's where all its subviews should be initialized already (at least their "onCreate" method should have been called, I thought), but it seems that this is handled differently for ViewPager to retain only the number of fragments in memory that was set by setOffscreenPageLimit.
So, what I'm looking for (and probably just missing) is the correct callback method here. One that is called when the ViewPager's next Fragments have been loaded and initialized. If such a callback exists, I could place my little piece of code there to update the text in my "previous"/"next" buttons within the ActionBar.
Any help, comments, ideas are highly appreciated. If needed, I can also try to attach some code sample to better explain my setup, but I think it should be easy enough to understand what my problem is.
Thanks in advance!
P.S.: I also tried to do this by using EventBus to send "onFragmentInitialized" messages from my fragments within in the ViewPager and the hosting fragment. It actually worked, but it does not seems the proper way to do this.
When a Fragment's onCreate Method is called, its already preparing to be displayed, and practically its past the point where its considered a Next or Previous fragment instead its considered current.
A fragment's onCreateViews method is called after committing a transaction in the FragmentManager. which takes less than 1 sec to bring it in front of the user (depending on the device and runtime environment)
But in your case, your data should be initalized outside the Fragment that uses it, and displayed where ever you want by passing the data itself then displaying whatever you want form it.
decouple your data from android objects (Fragment, Activity ...) and you should be able to load, maintain, access it cleanly and without worrying about their callbacks.
The Fragment's arguments can be read and loaded in its onAttach callback rather than onCreate, the Activity will then (after onAttach is complete) get a onAttachFragment callback with the Fragment as a parameter. However, I doubt onAttachFragment will be called when switching between already loaded pages in the view pager.
If not, you could have the fragment notify the activity (through an interface) that it is now active during its onActivityCreated, onViewCreated or similar method.
But it sounds more like the activity should register as a page changed listener to the ViewPager itself, and update its state depending on the page rather than which fragment is active.
As a side note, ViewPagerIndicator is quite old now (hasn't been updated in 3 years), a more modern approach is the SlidingTabs example from Google, which has been built into a library available here: https://github.com/nispok/slidingtabs
First, I'd to state that I've been searching for a solution for this problem for three days now, that may means either I'm not asking the right question or not using a good approach. If any, please guide me in the right direction.
This is the scenario: I've an Activity and a bound Service. The Service holds some data and processes it as necessary while posting a persistent (ongoing) notification with some information. The Activity has three Fragments inside a ViewPager that displays the data processed by the Service.
The Fragments are a List Fragment, that shows the active data entries available, a Details Fragment that displays the details for each data and a Parameters Fragment where the user can modify how the data is processed.
[Service] <-> ([Activity] -> [ViewPager([List], [Details], [Parameters])])
Everything works just fine. My Activity binds to the Service, the ViewPager is created after and then the Fragments fetch information trough an Interface.
Here comes the fun part... Screen Rotation!
As the Service binds asynchronously, when the user rotates the screen the Fragments no longer have the data because the Activity is bounding the service while they're already present and not recreated thanks to the ViewPager.
I've been trying to figure this out but it seems that I don't have the knowledge to solve it. I've tried making static references to the fragments, setting them up before the service is rebound but I can't get a stable solution.
I'd be using android:configChanges in my manifest but there are different layouts for each orientation.
Again, if I'm using a bad approach, please, guide me!
Difficult to suggest when I don't know your code but thinking out loud....
Can you have a "worker fragment" that is never displayed (i.e headless) and has setRetainInstance(true) set so it does not lose any state you have set.
Your worker fragment would bind to the service instead of the activity and maintain a reference to it.
If you need to communicate with your Activity, you can do this with callbacks.
Your other fragments could communicate with the worker instead of the Activity.
This process would basically make the activity little more than a shell into which the rest of your components are hosted. Rotation would lose nothing because all data is held in the retained fragment.
During the screen rotation process the activity is completely destroyed and use of android:congfigChange is discouraged. but what you can do is you can override saveInstanceState(bundle) method in which you can save the data present in your activity at the time it is destroyed by the system in response to the screen rotation. and later receive it as the system passes the bundle to the activities onCreate(bundle) method or get it from the restoreInstanceState(Bundle) method.
I need a solution for view pager implementation.
Firstly I am loading huge data from database in single page,so sometimes during swipe it slows down swipe frequency(you need to multiple time swipe on page) as in background it is doing fetching task.
I am not using async task for returning view.
Is there any way to lazy load pages just allow user to go on other page on swipe but data is lazy loaded.
My code sample is as below;
public Object instantiateItem(View container, int position) {
View v;
v = View.inflate(context,R.layout.swipearea, null);
listView = (ListView)v.findViewById(R.id.MyListView);
largeDataCall();
((ViewPager)container).addView(v);
return v;
}
I am calling this in on create method.
pager=(ViewPager) findViewById(R.id.pagerAdapter);
pager.setAdapter(new SimplePager(MyPager.this));
pager.setCurrentItem(364);
Is there any solution?
I would suggest to work with Fragments (and not directly with views).
You need an Interface on your fragments to tell them when they are shown:
public interface IShowedFragment {
public void onShowedFragment();
}
Make all your fragments implement that interface, and in that method call your loaders/asyncTasks/background tasks.
Then put an onPageChangeListener on your ViewPager, and when you detect the user changed the page, call the interface method on your fragment. You have some choices with this listener, with one of the methods you can wait for the viewPager to stop to slide to trigger your interface call.
To be able to get the right fragment to make this call, take the fragment from yourFragmentApadter.instantiateItem(ViewGroup, int) which will return the fragment for that position if it is already loaded.
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
Fragment fragment = (Fragment) mAdapter.instantiateItem(mPager, position);
if(fragment instanceof IShowedFragment){
((IShowedFragment) fragment).onShowedFragment();
}
}
(...)
Like that you can prepare your fragments with empty views and when you slide on one, you start to load the data.
I have just completed a very similar task. To get you started on finding the solution to your problem consider the following points in order;
Look at whether you need to be fetching all of that data in the first instance. Feel free to post back with some detail as to what information you are needing to be loaded and what you are doing with it (displaying it as a list on screen?)
Look at using CursorLoaders which perform heavy-lifting tasks such as database fetches asynchronously. This tutorial on the interwebs introduces the ContentProvider Android approach. Best to familiarise yourself with the official Android URI and ContentProvider documentation if those terms don't mean much.
If you are working with Fragments - Look at using the FragmentStatePagerAdapter instead of the traditional FragmentPagerAdapter. I haven't used this adapter but I have read that it only instantiates the currently visible Fragment, i.e. not those Fragments to the right or left of the currently selected tab.
Look at optimising the query you are running against the DB.
instantiateItem is called when the ViewPager is about to swap and needs a view. It doesn't have to actually create everything. I think ultimately, lazy-loading is out. The way I see it, there's two things you'll need to do here.
1:
Cache the data in the background when the user is about to reach your page. Your example claims that 364 pages (good Lord), so I'd say use a listener to handle page changes. When you're at page 363, start loading the data for 364. When you're at 364, start loading the data at 365 and keep the data at 363 in case the user wants to swap back. If the data loads relatively quickly or the user takes a long time to swap, it should be seemless assuming you're using asyncTask or thread to load the data.
2: Have a backup default view that doesn't get populated until the data is retrieved. You'll need to do this with option 1 as well in case the user loads the page before you retrieve the data. Basically, just have a view that says "loading..." or something until you have the data. Either that, or populate the data at real time as you get it. In that case the user will see it build.
Either way, I think you'll need to cache something to make the app look good.
I had a similar problem. A viewpager which was loading heavy data. If you are not going to change the views of the individual pages often, then I would suggest you keep the pages in memory. Use following code to perform this
mViewPager.setOffscreenPageLimit(#pages); to keep #pages in memory. I had 5 pages so my #pages was 4.
If you want to refresh data on the viewpager slides, use
mViewPager.getAdapter().notifyDataSetChanged(); with getItemPosition() returning POSITION_NONE.
And use FragmentStatePagerAdapter.
I do not think there's any way to lazy load data in the synchronous fashion that you describe. AsyncTask is the way to go or perhaps you could use threads directly. I believe AsyncTask was designed specifically for this kind of functionality. It's easier to use then the thread directly. If you need ideas on implementation, have a look at:
http://geekjamboree.wordpress.com/2011/11/22/asynctask-call-web-services-in-android/
Have you looked into android ignition library? according to Sample-applications there is a a component "Endless List" and a http-cache component.
I havent tried it myself and dont know if this is a solution for you-just saw the examples.....
Let's say I have a list of homogenous items which is likely to be changed in the lifetime of my Activity by user interaction or OS events. The Activity contains a FragmentPager which shows a number of ListFragments.
These fragments share the previously mentioned data but display it in different ways. E.g. they differ in sorting order or display only a subset of the data. Currently each fragment keeps a separate list containing the respective part of the data in the respective order.
When the data changes, basicly every fragment has to be updated. This means resorting or adding/removing items from some of the fragments. What is the best practice to keep the data in the different fragments consistent?
Currently I have some sort of an observer object, which is notified when something changes and subsequently notifies the connected fragments. But there are a couple of problems:
When the app just started, some of the fragments haven't been created by the FragmentPager, so notifying them is impossible.
When swiping through the fragments some of them get paused. In this state, they can't update their list. Should they disconnect from the observer in this case? This leads to:
When a change happens, while a fragment is disconnected, it basicly misses it.
And so on...
If I understood your ViewPager shows the same data (or it's portion) but in different views. So, I belive ViewPager shouldn't act in any way when data is changed, it's responsibility of Adapter.
About points below you said:
a) creating of fragments inside ViewPager can be managed by you. Just see javadoc of ViewPager::setOffscreenPageLimit(int limit) method.
b) I think you should do nothing with UI when data changed but fragment is in paused state. If you want to update do it in onResume(). Or better to set some field in DB (if you have) to "updates present" state and check it when Activity(Fragment) appears.
c) As in previous option - if fragment disconnected just ignore updates. Or if you really interested in that update use sticky BroadcastReciver (be carefull sticky BR is expensive thing)
You can keep your data in Application class, update only visible fragments when data has changed, and always ask for the new data in Fragment's onResume(), that'll do it
What I would do is have each ListFragment use a Loader to load its data. Then, instead of having the observer notify the Fragment (which might have been killed) to refresh its data, register an observer for each Loader so that it will know when the data source has changed, and will re-query when one has been detected. (If your data source is an SQLite database and you are using a ContentProvider, the CursorLoader will do all of this for you).
This is the implementation I would recommend because
Each Fragments behavior remains self-contained (i.e. each is a re-usable component that is not tied to any specific ViewPager or Activity).
It avoids the complexities of having to deal with potentially destroyed Fragments within your ViewPager.
If you need a quick fix, you could probably get away with forcing the Fragments to remain in memory using ViewPager#setOffScreenPageLimit(int limit) as Ivan suggests... however, this isn't as clean of a solution in my opinion.
I am still searching for the best solution howto use a layout with a menu and a toolbar and inflate or start activities in android. My question may sound confusing, but im trying to explain it in an example.
Lets say im programming an android app (surprise.. i really do)
My app can do following:
User can log in [3] or register [2]. If he logs in, a new activity starts and his dashboard will be shown. If he registers: an activity for the registrationprocess starts.
Registrationprocess: user puts in his desired username and password and presses a button to accept. His data will be formvalidated and if valid, a new activity starts where he can choose his settings. Backbutton works and data can be passed to the new activity. After the last registrationwindow data will be saved and dashboard started. Starting new Activities is fun!
Now THATS where it gets complicated. Dasboard has an 'actionbar'(top) and a 'toolbar' (bottom, like tabs). So everything should be viewed in the middle part of the viewport(from now called main view). No more activity switching :(, tho.
Currently each tabclick removes all views from the main view and adds its new view. Look great, can be animated and works like a charm. Except: its currently not dynamic.
So... i don't know how to solve it the best way. For example: i fetch data from a webservice, create a listview out of it and it's extending listactivity. This activity i can't start but this data need to be put into the main view. How can i do it the best way?
And is it efficient?
I'm practicing and it's actually my first small discussion i want to start. So... FIGHT! ;)
UPDATE:
I've seen an interesting way to start activities and get results.
Launching activity through intents
. Is it possible to insert new/ update views after activity started? I would then generate my results in a separate activity. Update the view. Return back to 'dashboard' and load the view that was just updated. Possible? Or inefficient? And how can i update a view out of another activity? There is so much i need to learn :/
UPDATE2:
A good example of an app that has done it: Google+
Too bad i don't have their sourcecode ;)
UPDATE3:
What is best?
load a new activity, disable animation and set selected toolbox tab +
disable backbutton functionality
startActionForResults, fetch results and update current view (still don't really know how that would be possible)
viewFlipper onflip changing+updating data in flipped view.
I still don't know any efficient solution. Or am i missing something essential? I've just finished my ListActivity to fetch data from my webservice. But it still runs in a separate activity. How can i implement it into my "main view" now? Ofcourse... i could set a list my custom adapter. But currently im updating and fetching data from the server when i create the listactivity.
Im afraid this could be the only answer i'll get: Embed external Intent in main Activity
UPDATE4: I'm trying something.
Based on nininho's answer (thank you!) im trying the following approach:
Start Dashboardactivity and create a ViewFlipper.
Each Toolbarclick represents a certain ViewFlipper page.
Each Page has a Listadapter implemented and shows different results (different webservice queries). (ListView, GridView, with profileimage, without profileimage)
On Toolbarclick start AsyncTask or Service and notify List in current Page that data has changed. (ofcourse IF data has changed). Switch to page that was clicked.
Implement updatefeature. On scroll to bottom of list = fetch more data and add it. Update other lists automatically after 5min. or update list on update-button click.
PROs so far: Backbutton standalone for whole activity. Page-flip-animation possible. Async updating of lists and still possible to switch to another list.
CONs: ... someone has any? What about efficiency of such an approach? Does the ViewFlipper carry all the information so the performance would go down or does the viewflipper recycle its Views (like ListView)?
UPDATE5:
If i have some time i will make everything here more read- and discussable. Don't be mad at me for reading my rubbish ;)
From what I understand you want your app to start, fetch some data from the internet and after show this data on the main screen.
I don't see the need of a second activity to fetch the data because from your explanation you want to use it only to fetch the data, so the best approach would be:
Create one Activity (your dashboard)
Start an AsyncTask or Service on the background to fetch the data.
When the fetch ends, notify the activity that it ended.
Change your dashboard to show the list (you can use a ViewSwitcher if you want some animation or just create a layout with the list invisible and then change to visible).
ps: you can use a ListView outside of a ListActivity, just create a ListAdapter to create the ListView items and add this as the adapter for the ListView.