Im using supportApi for lower versions. I have a listFragment and an asynchAdapter which loads data in background from web. The problem is if the data is changed in Background I cant make the listFragment to refresh and show the new data. I have a customListAdapter that extend baseAdapter and I am calling setlistadapter(customListAdapter) in my ListFragment. I need some example which implements something like this not the cursorAdapter or simpleArrayAdapter.
The only problem I have is I dont know how to refresh the listFragment as onResume doesnt gets called and not sure if notifydatasetchange will work on listfragment. Some pieces of my code is as follow.
the onLoadFinish gets call only once when I initialize loader. The onresume gets called only once as well. May be my approach is wrong but that what I am not sure about.
public class MyListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<ArrayList<MyData>> {
private ArrayList<MyData> mydata;
private CustomListAdapter adapter;
#Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.adapter = new CustomListAdapter(this.getActivity(), mydata);
setListAdapter(this.adapter);
getLoaderManager().initLoader(0, null, this);
}
#Override
public void onLoadFinished(Loader<ArrayList<MyData>> Loader, ArrayList<MyData> mydata) {
this.adapter.setData(mydata);
}
}
Related
I have two views In a single activity StackView and Gridview and 2 corresponding adapters .when I clicked on StackView item it should flip the grid item in Gridview and vice versa?
// activity classs
public class SampleActivity extends Activity implements
OnGridChangeListener {
public void onCreate(bundle){
// replace this with your adapter class.
Adapter adapter = new adapter(this);
}
#override
public void OnGridChange(){
//here you go.
// write code to do what you want.
}
// interface to communicate with activity
public interface OnGridChangeListener {
public void OnGridChange()
}
// adaptor class
public class Adaptor extends "you apapter class to extend"{
OnGridChangeListener onGridChangeListener ;
public Adapter(OnGridChangeListener listener){
onGridChangeListener =listener
}
public getView(){
public void onclick(){
onGridChangeListener.OnGridChange("pass you data");
}
}
}
As per your question this is what i get -
You have a Activity with two independent views which has their own adapters. So when there is changes in one of the adapter you want it to be reflected into another adapter.
The simple solution for your query would be -
When there is a change in first adapter you reflect the change to the activity. after that call the function in the second adapter to reflect the change you want in second adapter.
For this you have to define an interface in first adapter and implement this is activity.
When the first adapter changes call the interface method and this will reflect in activity.
Then call method in second adapter to do the changes you want.
Code example -
MainActivity.Java
public class MainActivity extends AppCompatActivity implements FirstAdapter.callBackMethods {
FirstAdapter firstAdapter;
SecondAdapter secondAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firstAdapter = new FirstAdapter(MainAtivity.this.getApplicationContext());
//do all the declaration and operation of first adapter. Pass context along with your required params.
secondAdapter = new SecondAdapter();
//do all the declaration and operation of second adapter.
}
//callback method of first adapter
#override
public void callback(){
//changes have been done in FirstAdapter and this methos is fired.
//now do do the changes in SecondAdapter as per req.
if(secondAdapter != null){
secondAdapter.reflectChanges();
}
}
}
FirstAdapter.class
I am taking example of recylerview adapter.
public class FirstAdapter extends RecyclerView.Adapter<FirstAdapter.ViewHolder>{
public FirstAdapter(Context context){
this.context=context;
}
/*
all the boilerplate codes and business logic
*/
//when you want to reflect the changes
callBackMethods callBackMethods = (callBackMethods) context;
callBackMethods.callback();
//this will fireup the implementation in the MainActivity.
public interface callBackMethods{
public void callback();
}
}
SecondAdapter.class
This is where the changes will be reflected.
public class SecondAdapter extends RecyclerView.Adapter<SecondAdapter.ViewHolder>{
/*
all the boilerplate codes and business logic
*/
public void reflectChanges(){
/*
This method will be called from the MainActivity. pass whatever params you want to pass from Activity and do the changes you want to do in the SecondAdapter.
*/
}
}
I hope this solves your problem. Happy coding...
got the following example while reading android book. Can somebody please confirm to me why adapter is always created in this example? Shouldn't it be done only in the case when model == null?
If I understand correctly all data members are retained (in this example), so ListView will be retained, along with its configured ListAdapter and everything else.
public class AsyncDemoFragment extends SherlockListFragment {
private static final String[] items = { "lorem", "ipsum", "dolor" };
private ArrayList<String> model = null;
private ArrayAdapter<String> adapter = null;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
if (model == null) {
model = new ArrayList<String>();
new AddStringTask().execute();
}
adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, model);
setListAdapter(adapter);
}
class AddStringTask extends AsyncTask<Void, String, Void> {
// …
protected void onProgressUpdate(String... item) {
adapter.add(item[0]);
}
}
}
The instance of your Fragment will be retained -- however, the View created by the Fragment will still be destroyed and recreated unless specifically retained (which can very easily cause memory leaks). Basically, without setRetainInstance(), the following events (along with others) would happen on a configuration change:
// Fragment initialized
onCreate()
onCreateView()
// Configuration change
onDestroyView()
onDestroy()
onCreate()
onCreateView()
With setRetainInstance(true):
// Fragment initialized
onCreate()
onCreateView()
// Configuration change
onDestroyView()
onCreateView()
Essentially, you still need to recreate the View, but any other instance fields will not be reset.
You should still be able to handle the case where they are reset, however, as even with setRetainInstance(true) your Activity may be killed in the background due to memory pressure.
it is really strange, many people has asked this question but not even one useful answer
I have a MainActivity class
public class MainActivity extends FragmentActivity implements ActionBar.TabListener
with 3 actionbar tabs (sections 1,2,3) each section has its own fragment in the package
public class FragmentA extends Fragment
public class FragmentB extends Fragment
public class FragmentC extends Fragment
in the FragmentA I have a listView with its arraylist adapter.
in the MainActivity I fetch some data and put it in the sqlite database.
as soon as new data are added to database I want to notify the listview that the dataset is changed and populate the new data
it sounds easy but I have really got stucked in that !
I just want to know how to do this refresh, rebuild, recreate ... what ever it is from MainActivity
if any one can help ? thanks
in your activity.
this.fragment.adapter.notifyDataSetChanged();
Hehe.
Is this the poor idea?
public interface INotifyDataChange {
public void notify(Object dataChanged); // Object stand for whatever you want. :">
}
You MainActivity
MainActivity {
public static INotifyDataChange notifier;
public void inSomeMethod() {
// do input data.
if (notifier != null) {
notifier.notify(dataWillBeAttached);
}
}
}
Your Fragment:
FragmentA or B, or C implements INotifyDataChange {
onCreateView() {
MainActivity.notifier = this;
}
#Override
public void notify(Object data) {
Toast.make(..., "I've changed data " + data.toString(), ...).show();
yourListView.notifyDataSetChanged(); // or anything like this.
}
}
The simplest is to use a Local Broadcast manager. developer.android.com/reference/android/support/v4/content/… You can register your UI to receive a broadcast which you can fire when the data has been saved in the database. When the UI receives this event you can notify your adapter to refresh the list view
I have an app that downloads data which must be displayed within the fragments of a viewpager. I dont know how to call the ListFragment adaptername.notifyDataSetChanged() in the AsyncTask that does the data download.
For example say i am downloading the temperature forecast for the next seven days:
my app has the following structure:
MainActivity: Starts an AsyncTask to download the data in onCreate() and gives the user choice (button) of which day to look at. Clicking the button launches SecondActivity and passes the day index to the ViewPager (to set the current view).
SecondActivity: Contains a ViewPager that contains 7 of the same ListFragments (The list display the temperature over a period of 5 hours, so the list has 5 entries).
MyListFragment: when this loads it sets the adapter to display each temperature (If the data is downloaded) otherwise it sets the temperature to "loading..."
Now my problem is, if the user waits on the MainActivity until the data downloads they can then proceed to the ViewPager to see the ListFragment temperatures without problem. But if they try click a day and load the ViewPager before the download completes the fragments will forever just say "loading..."
I need a way that I can reload the adapter within the ListFragment from the onPostExecute() of my AsyncTask in MainActivity. To do this though i need to be able to actually access the ListFragment that the ViewPager is displaying. How do update the adapter onPostExecute()?
MainActivity:
protected void onCreate(Bundle savedInstanceState) {
...
new LoadData().execute();
}
protected class LoadData extends AsyncTask<String, Void, String> {
protected String doInBackground(String... params) {
//Download happens here
}
protected void onPostExecute(String result) {
//I need to tell the viewpager in SecondActivity to reload the ListFragment it is currently showing here
}
}
SecondActivity
private ViewPager mPager;
private PagerAdapter mPagerAdapter;
private int[] temperatureArray;
public void onCreate(Bundle savedInstanceState) {
...
Intent intent = getIntent();
int[] defaultTemps = {0, 0, 0, 0, 0};
temperatureArray = getIntArrayExtra("temps", defaultTemps);
mPager = (ViewPager) findViewById(R.id.pagerID);
mPager.setCurrentItem(intent.getIntExtra("page", 0));
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
...
public Fragment getItem(int position) {
MyListFragment lf = new MyListFragment();
lf.setTemperatures(temperatureArray);
return lf;
}
}
I need a way to be able to refresh the current displayed fragment when i finish downloading in the AsyncTask.
I can suggest doing one of two things, not sure if these are best practices.
You can Have the AsyncTask send a broadcast with a unique action when it has finished loading the information. That would of course have to be done from OnPostExecute:
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (result != null) {
//_Do whatever action you normally do, like storing result to database.
//fire up the broadcast
Intent intent = new Intent(Home.ACTION_FEED_LOADING_FINISHED);
mContext.sendBroadcast(intent);
}
}
And then intercept that via a BroadcastReceiver on your Fragment's code.
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(ACTION_FEED_LOADING_FINISHED)) {
//The AsyncTask Finished loading data
}
}
};
Once the event is received, since the BroadcastReceiver is in the Fragment, where the views are loaded, you can refresh your UI accordingly.
Or,
You can pass the View you want to refresh as a paremeter on your AsyncTask.
Imagine your AsyncTask class like this:
private static class getInternetStuffTask extends AsyncTask<String, Void, Void> {
Then you can create a custom constructor to receive the view as paremeter:
public getInternetStuffTask (final Context context, final ListView list) {
this.mContext = context;
this.mListView= list;
}
Then, during onPostExecute, when data is loaded, you can simply set the adapter to the ListView directly form the AsyncTask.
You're touching on two different problems. First, use a Service for downloading. Services are there for long running operations (like a download) that live outside an Activity's lifecycle.
Second, to communicate between Activities and Fragments you'll want to use an callback interface. The link provided is to the docs which do an excellent job of explaining and providing samples.
I have a activity that extends listactivity, extended in this class i have a class that extends baseadapter.
now in my listactivity i have this onCreate
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new BuildingAdapter(this));
final ProgressDialog loading = new ProgressDialog(this);
loading.setProgressStyle(ProgressDialog.STYLE_SPINNER);
loading.setTitle("Laddar");
loading.setMessage("Hämtar data från servern");
loading.show();
new AsyncTask<Void, Void, DataPacket.Data>()
{
#Override
protected DataPacket.Data doInBackground(Void... params){
return ServerConnection.getBuildings();
}
#Override
protected void onPostExecute(DataPacket.Data param) {
((MainenanceApplication)getApplication()).setDataStructure(param);
loading.dismiss();
//((BuildingAdapter)getListAdapter()).notifyDataSetChanged();
setListAdapter(new BuildingAdapter(BuildingSelectionActivity.this));
}
}.execute();
}
This works as it's supposed to, but my question is in onPostExecute I update the datastructure that the list adapter uses.
Why cant I just call notifyDataSetChanged ??
If I have that line the view does not update itself, but if I use the line under where I do setListAdapter, it all works.
If the adapter is already set, setting it again will not refresh the listview. Instead first check if the listview has a adapter and then call the appropriate method.
I think its not a very good idea to create a new instance of the adapter while setting the list view. Instead, create an object.
BuildingAdapter adapter = new BuildingAdapter(context);
if(getListView().getAdapter() == null){ //Adapter not set yet.
setListAdapter(adapter);
}
else{ //Already has an adapter
adapter.notifyDataSetChanged();
}
Have you tried using an AsyncTaskLoader instead of an AsyncTask for this. It's this kind of stuff that Loaders were exactly designed for. Note that even though Loaders weren't available until API-10 you can still easily access them via the android Support Pacakge from API-4 and up.
The only place you can update the UI is in onProgressUpdate(...);. From your doInBackground(...), call publishProgress(...).