ListFragment sometimes skips code in onLoadFinished() method of Loader - android

I have a ListFragment which fetches data from the net using a Loader. I use a new instance of this ListFragment in every page of my ViewPager. It works perfectly, but when I use TabLayout or moves pages quickly, the Fragment keeps loading and does not display the data in the ListView.
When I checked using log messages, I found that the ListFragment skips some lines of code in the onLoadFinished() method. It does not make the ProgressBar invisible. It does add items to Adapter, but it is not being displayed in the ListView. This problem also happens in the first page of the ViewPager.
Is there any specific rule to be followed when using ListFragments in a ViewPager?
Here is the ListFragment class. If you look at the onLoadFinished() method, you can see the lines causing problem:
public class ListViewFragment extends ListFragment
implements LoaderManager.LoaderCallbacks<List<GameNews>> {
public static ListViewFragment newInstance(String url) {
Log.d("ListViewFragment", "newInstance created");
ListViewFragment f = new ListViewFragment();
// Supply url input as an argument.
Bundle args = new Bundle();
args.putString("url", url);
f.setArguments(args);
return f;
}
List<GameNews> TotalNews;
ListView gameListView;
LinearLayout emptyView;
Button retryButton;
ListAdapter adapter ;
private View progressBar;
final private int game_loader = 0;
ArrayList<String> urls = new ArrayList<>();
String mUrlString;
int index;
//LIFE CYCLE OF FRAGMENT
//------------------------------------------------------------------
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUrlString = getArguments().getString("url");
urls.add(mUrlString);
TotalNews = new ArrayList<GameNews>();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_list_view, container, false);
ArrayList<GameNews> gameList = new ArrayList<>();
adapter = new ListAdapter(getActivity(), gameList);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
emptyView = (LinearLayout)
view.findViewById(R.id.no_internet_view);
progressBar = view.findViewById(R.id.progress_bar);
retryButton = (Button) view.findViewById(R.id.retry_button);
gameListView = getListView();
emptyView.setVisibility(View.INVISIBLE);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(adapter);
//If connected to net start the loader
if (isConnected()) {
getActivity().getSupportLoaderManager().restartLoader(game_loader,
null,
ListViewFragment.this);
}
}
//OVERRIDED METHODS OF LOADERMANAGER
//---------------------------------------------------------------------
#Override
public android.support.v4.content.Loader<List<GameNews>> onCreateLoader(int i, Bundle bundle) {
AdManager manager = new AdManager(getActivity());
return new FragmentLoader(getActivity(), urls, 1000);
}
//HERE IS THE PROBLEM PLEASE FOCUS INSIDE THIS METHOD
//-------------------------------------------------------
#Override
public void onLoadFinished(Loader<List<GameNews>> loader, List<GameNews> games) {
progressBar.setVisibility(View.INVISIBLE); //This line of code is not executed
adapter.clear();
TotalNews.addAll(games);
adapter.addAll(games);//And the listView is not populated
}
//-------------------------------------------------------
#Override
public void onLoaderReset(Loader<List<GameNews>> loader) {
adapter.clear();
}
//REUSABLE METHODS
//------------------------------------------------------------------
//Method checks if there is internet
public boolean isConnected() {
ConnectivityManager manager = (ConnectivityManager)
getActivity().getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = manager.getActiveNetworkInfo();
if (info != null && info.isConnected()) {
return true;
}
else {
return false;
}
}
}

Your Fragment class is using the Activity's LoaderManager:
getActivity().getSupportLoaderManager().restartLoader(...);
And each instance is using the same ID in its restartLoader() call:
final private int game_loader = 0;
This means that each Fragment instance was using and restarting the same Loader over and over again, leading to the weird behavior you observed.
The solution is quite simple: use Fragment's local LoaderManager, instead of the Activity's.
getLoaderManager().restartLoader(...);
With this, you don't need to worry about changing the ID in each instance, since Loaders are unique to their Fragment, and the Loader will be properly handled over the Fragment's lifetime, which would likely not have been the case when using the Activity's LoaderManager.

Related

Using ViewPager with ListFragment and CursorAdapter

I have an existing app displaying five different lists of entries from a database. It uses a left drawer to choose which list is displayed.
I would like to use ViewPager to these lists, but have a really hard time figuring out how this works. Unfortunately, I cannot find a tutorial that addresses this.
I have a MainActivity to which I connect a viewpagerAdapter
MainActivity extends AppCompatActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
// Setting the viewpager
mPager = (ViewPager) findViewById(R.id.pager_container_home);
mPagerAdapter = new EntriesListPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
...
}
private class EntriesListPagerAdapter extends FragmentPagerAdapter {
...
#Override
public Object instantiateItem(ViewGroup container, int position) {
// What to put here???
return view;
}
}
I have a ListFragment class getting its data from a ResourceCursorAdapter (called EntriesCursorAdapter). It is a long class, so I give some parts to give you an idea.
public class EntriesListFragment extends ListFragment {
...
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
...
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mEntriesCursorAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
mEntriesCursorAdapter.swapCursor(Constants.EMPTY_CURSOR);
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
setHasOptionsMenu(true);
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mUri = savedInstanceState.getParcelable(STATE_URI);
mShowFeedInfo = savedInstanceState.getBoolean(STATE_SHOW_FEED_INFO);
mListDisplayDate = savedInstanceState.getLong(STATE_LIST_DISPLAY_DATE);
mEntriesCursorAdapter = new EntriesCursorAdapter(getActivity(), mUri, Constants.EMPTY_CURSOR, mShowFeedInfo);
}
}
#Override
public View inflateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_entry_list, container, true);
if (mEntriesCursorAdapter != null) {
setListAdapter(mEntriesCursorAdapter);
}
mListView = (ListView) rootView.findViewById(android.R.id.list);
...
}
public void setData(Uri uri, boolean showFeedInfo) {
mUri = uri;
mShowFeedInfo = showFeedInfo;
mEntriesCursorAdapter = new EntriesCursorAdapter(getActivity(), mUri, Constants.EMPTY_CURSOR, mShowFeedInfo);
}
Can you help me how to create five instances of the listfragment (with cursoradapter) and connect it to the ViewPager in the MainActivity? Any hint how to proceed is welcome.
Update: I noticed that the FragmentPagerAdapter is from the support.v4 library. If I am to use this, It looks like I have to use the ListFragment from support.v4 as well, including the LoaderManager. Unfortunately, the LoaderManager is not easily upgraded to support.v4. Any thoughts are welcome.
I got it solved. I will give the main pointers.
First, read this tutorial, because the given structure works. http://www.truiton.com/2013/05/android-fragmentpageradapter-example/
Second, check that all your imports are from the support.v4 library. This means the FragmentActivity (AppCompatActivity in the case above), ListFragment, Loader, LoaderManager and CursorLoader. I spent quite some time trying to figure out what was wrong with the code, while all the time I still had the Loader and CursorLoader from the wrong library.
Third, the MainActivity is where the FragmentPagerAdapter is placed, like:
MainActivity extends AppCompatActivity {
static final int ITEMS = 5; // number of lists
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
// Setting the viewpager
mPager = (ViewPager) findViewById(R.id.pager_container_home);
mPagerAdapter = new EntriesListPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
...
}
public class EntriesListPagerAdapter extends FragmentPagerAdapter {
public EntriesListPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
public int getCount() {
return ITEMS;
}
#Override
public Fragment getItem(int position) {
return EntriesListFragment.init(position);
}
}
}
Fourth, the ListFragment has to change. It has to be able to be used several times in order to support the number of different lists in the ViewPager. I will give the importants methods/elements.
public class EntriesListFragment extends ListFragment {
// Loader to get the data from database for the list. Called from startLoaders();
private final LoaderManager.LoaderCallbacks<Cursor> mEntriesLoader = new LoaderManager.LoaderCallbacks<Cursor>() {...}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Getting the page number when created
pageViewerFragmentNum = getArguments() != null ? getArguments().getInt("val") : 1;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_entry_list, container, false); // orig was 'true'
mListView = (ListView) rootView.findViewById(android.R.id.list);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.e(TAG, "onActivityCreated");
super.onActivityCreated(savedInstanceState);
// Setting the adapter for each of the Feeds
// The used feedId is the pageViewerFragmentNum + 1
String FeedId = String.valueOf(pageViewerFragmentNum+1);
mUri = EntryColumns.ENTRIES_FOR_FEED_CONTENT_URI(FeedId);
// setting the adapter for the list
mEntriesCursorAdapter = new EntriesCursorAdapter(getActivity(), mUri,
Constants.EMPTY_CURSOR);
setListAdapter(mEntriesCursorAdapter);
startLoaders();
}
// Initialise the new listFragment for each Page. Notice the static!
public static EntriesListFragment init(int positie) {
EntriesListFragment viewPagerFragmentList = new EntriesListFragment();
// Supply val input as an argument.
Bundle args = new Bundle();
args.putInt("val", positie);
viewPagerFragmentList.setArguments(args);
return viewPagerFragmentList;
}
private void startLoaders() {
LoaderManager loaderManager = getLoaderManager();
loaderManager.restartLoader(ENTRIES_LOADER_ID, null, mEntriesLoader);
}
}
Notice that the code given above is not complete. The tutorial mentioned earlier is, but I just tried to show you the main ingredients to get you started if you get stuck on this like I did.

Android - setting data in views of a fragment based on an async server response

I have the following fragment as below, wherein the onPostNetworkRequestWithCode() method is called back by another part of the code (the App extends Application class which does network requests), to paint the views
Since the fragment creation chain (onCreate(), onCreateView() etc) are called in a different thread than the onPostNetworkRequestWithCode() which repaints the views, I am having a race condition sometimes when the onPostNetworkRequestWithCode() method does not find a view to paint. How can I ask it to wait till the view creation is done and then resume post that?
public class MeStatsTableFragment extends Fragment implements HttpResponseHandlerWithResponseCode {
private static final String LINKED_USER_ID = "linkedUserId";
private Context mContext;
public MeStatsTableFragment() {
// Required empty public constructor
}
public static MeStatsTableFragment newInstance(long linkedUserId, Context context) {
MeStatsTableFragment fragment = new MeStatsTableFragment();
Bundle args = new Bundle();
args.putLong(LINKED_USER_ID, linkedUserId);
fragment.setArguments(args);
fragment.mContext = context;
return fragment;
}
/**
* This is responsible for painting the chart after data is obtained by parent Me Fragment
*/
#Override
public void onPostNetworkRequestWithCode(HttpResponseCode responseCode) {
long linkedUserId = getArguments().getLong(LINKED_USER_ID);
MePageInfo m = App.getAppData().getMePageInfoById(linkedUserId);
View v = getView();
if (v == null) {
Log.d("XXX", "Stats fragment Got stuck at view == null in Stats Fragment");
// do nothing else, just return
} else{
v.findViewById(R.id.text_name).setText(m.name);
// do other painting similarly with the view and the variable m
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_me_stats_table, container, false);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
}
You could use greenrobot EventBus (https://github.com/greenrobot/EventBus) - and make a sticky post, in a case when there is no fragment yet.
Here is more info about sticky post and register - https://github.com/greenrobot/EventBus/blob/master/HOWTO.md#sticky-events

Listview fragment is getting recreated on pressing backbutton

I'm using loader in my ListView fragment, and it's getting recreated on pressing "back" button. Can you tell me how to handle this senario?
Here is my ListView fragment code. Here I have a boolean variable that I'm setting as true on clicking on list item. but once the back button is pressed onCreateView will get called so the backbutton will be false.
public class GTFragment extends SherlockFragment implements LoaderCallbacks<Cursor>{
ListView mTListview = null;
GoogleTasksAdapter mGTasksAdapter = null;
private SQLiteCursorLoader mTLoader=null;
private LoaderManager mTLoaderManager;
private String mSelectedListID = null;
private boolean mIsBackbuttonisPressed = false;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.task_home_activity, container, false);
if(!mIsBackbuttonisPressed)
getLoaderManager().initLoader(0, null, this);
mTListview = (ListView) view.findViewById(R.id.id_task_list_home_activity);
mGTasksAdapter = new GoogleTasksAdapter(getActivity());
mTListview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listview,
View clickedview, int position, long arg3) {
// TODO Auto-generated method stub
GoogleTaskItem item = new GoogleTaskItem();
Cursor coursor = ((GoogleTasksAdapter)listview.getAdapter()).getCursor();
if(coursor.moveToPosition(position))
{
mIsBackbuttonisPressed = true;
GoogleTaskController.get_googletask_controllerObj()
.LaunchTaskPreviewActivity();
}
}
});
mTListview.setAdapter(mGTasksAdapter);
mIsBackbuttonisPressed = false;
return view;
}
My fragment activity class code
public class TLActivity extends SherlockFragmentActivity {
LeftSliderTaskListOptions mTaskOptionsFragment = null;
GoogleTasksFragment mTFragment = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
setContentView(R.layout.layout_gt_list);
// FragmentTransaction tfragment = this.getSupportFragmentManager().beginTransaction();
mTFragment = new GTasksFragment();
t.replace(R.id.id_tfragment, mTFragment);
t.commit();
}
instead of
t.replace(R.id.id_tfragment, mTFragment);
use
t.add(R.id.id_tfragment, mTFragment);
It worked for me
I don't think that the accepted answer is right because Fragment.onSaveInstanceState will not be called until the activity hosting it needs to save its state: The docs states:
There are many situations where a fragment may be mostly torn down
(such as when placed on the back stack with no UI showing), but its
state will not be saved until its owning activity actually needs to
save its state.
In other words: if you're using a Activity with multiple fragments for each screen (which is very common), the fragment state will not be saved when you move the next screen.
You also can't use Fragment.setRetainInstance because he's meant only to fragments that aren't on the back stack.
Most of the time, you don't have to think about this but sometimes it's important. Like when you have scrolled a list and want to "remember" the scroll location.
I took a long time to realize that the fragments put on the back stack are kind of saved and you can reuse the view that you already created instead of creating one every time the fragment calls onCreateView. My setup is something like this:
public abstract class BaseFragment extends Fragment {
private boolean mSaveView = false;
private SoftReference<View> mViewReference;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mSaveView) {
if (mViewReference != null) {
final View savedView = mViewReference.get();
if (savedView != null) {
if (savedView.getParent() != null) {
((ViewGroup) savedView.getParent()).removeView(savedView);
return savedView;
}
}
}
}
final View view = inflater.inflate(getFragmentResource(), container, false);
mViewReference = new SoftReference<View>(view);
return view;
}
protected void setSaveView(boolean value) {
mSaveView = value;
}
}
public class MyFragment extends BaseFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setSaveView(true);
final View view = super.onCreateView(inflater, container, savedInstanceState);
ListView placesList = (ListView) view.findViewById(R.id.places_list);
if (placesList.getAdapter() == null) { // this check is important so you don't restart your adapter
placesList.setAdapter(createAdapter());
}
}
}
You have multiple options to rectify this issue.
Override onSaveInstanceState like this:
#Override
public void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("mIsBackbuttonisPressed", mIsBackbuttonisPressed);
}
and then in your onCreateView you can get your variable back by:
if (savedInstanceState != null)
mIsBackbuttonisPressed = savedInstanceState.getBoolean("mIsBackbuttonisPressed", false);
You can set this.setRetainInstance(true); in your onCreate method of your fragment.
If you could post your Activity code with creates your fragment I can also tell you other options. (P.S I cannot write it as a comment so posting it in the answer.)

ListFragments Wrong Data Weird Pager

I hope someone can assist please. I have a Fragment hosting multiple list fragments using support library. The list fragments are supposed to display data that i retrieve form an async task in the parent fragment. I have been trying to figure out exactly how the data is being loaded because it is not loading correctly.
Each time the list display fragment is launched it preforms an async task to get and parse Json into an ArrayList <ArrayList <HashMap <String, String> > >
Each List fragment queries the parent fragment for data at its position in the ArrayList.
eg. For the 3rd page in it should retrieve arrList[2] which contains an `ArrayList <HashMap <String, String> > to display as a list.
The pager is acting weird. Maybe i am not understanding the lifecycle of the fragments or how the pager uses them. I have 7 Fragments. If i start on frag3 the pager will show fragment 3 with no data on it. It also loads fragment 2 and 4 with no data. If i go left to frag 1 it will display fragment 1 correctly and load fragment 0. I can properly switch to frag 0 but if i switch to frag 2 it loads data from frag 0 and loads frag 0's data into all of the rest of the views. If i go back and forth enough it will replace all data in every fragment with data from frag 0. I believe that it does not load data immediately because it does not have the data when the viewpager launches. I have not made it wait for the async task yet.
I thought that each fragment gets its view redrawn each time it is taken far enough from view. So i put Update in the onCreateView() of the fragment. I feel like this is a small thing that i have just misplaced or i am overlooking it. I tried to implement FragmentStatePagerAdapter but i do not think that i did it right.
Any Help is much Appreciated And i am very open to discussion if i am just doing things horribly wrong. I usually do. It never fails. Create something to find out i need to rewrite everything.
public class ListFragmentDisplay extends SherlockFragment {
public static final String TAG = "listFragmentDisplay";
Calendar calendar = Calendar.getInstance();
private int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
// listbyday is a list of hash maps each list of hash maps represents a day
// of the week with items for that Object
private ArrayList<ArrayList<HashMap<String, String>>> listByDay = null;
private String objectName = null;
private ViewPager pager;
private FragAdapter adapter;
public ArrayList<HashMap<String, String>> getList(int day) {
return listByDay.get(day);
}
private void getObjectName() {
barName = ((MainFragActivity) getActivity()).getobjectSelected();
}
public static ListFragmentDisplay newInstance() {
return new ListFragmentDisplay();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the ListView layout file.
initArrList();
getObjectName();
fillList();
return inflater.inflate(R.layout.list_view, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
pager = (ViewPager) view.findViewById(R.id.viewPager);
adapter =new FragAdapter(getChildFragmentManager());
if (pager.getAdapter() == null)
pager.setAdapter(adapter);
reload();
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int arg0) {}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {reload();}
#Override
public void onPageSelected(int arg0) {
}
});
pager.setCurrentItem(dayOfWeek-1);
}
private void initArrList() {
if (listByDay == null) {
listByDay = new ArrayList<ArrayList<HashMap<String, String>>>();
} else {
listByDay.clear();
}
for (int i = 0; i < 7; i++) {
ArrayList<HashMap<String, String>> hm = new ArrayList<HashMap<String, String>>();
listByDay.add(hm);
}
}
synchronized private void fillList() {
LoadWebTask lWT = new LoadWebTask();
executeAsyncTask(lWT, getSherlockActivity().getApplicationContext());
}
FragmentPager
public class FragAdapter extends FragmentPagerAdapter {
private static final String[] CONTENT = new String[] { "frag0", "frag1",
"frag2", "frag3", "frag4", "frag5", "frag6" };
public FragAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int arg0) {
return MyListFragment.newInstance(arg0);
}
#Override
public int getCount() {
return CONTENT.length;
}
#Override
public CharSequence getPageTitle(int position) {
return CONTENT[position % CONTENT.length];
}
}
ListFragment
public class MyListFragment extends SherlockListFragment {
public static final String NAME_TAG = "name";
public static final String DESCRIPTION_TAG = "description";
private static int dow;
public static final String TAG = "listFragment";
// Keys used in Hashmap that will be mapped to the rows
String[] dFrom = { NAME_TAG, DESCRIPTION_TAG };
private ArrayList<HashMap<String, String>> list;
int[] dTo = { R.id.name, R.id.description };
public void upDateList() {
//**************************Not really sure if this is how things are supposed
//** to be done. For my small data- set i feel like it will work but i would
//** be interested in knowing how else this might be done.
ListFragmentDisplay lFD = (ListFragmentDisplay) this
.getParentFragment();
dList = lFD.getList(dow);
}
public static MyListFragment newInstance(int pos) {
MyListFragment frag = new MyListFragment();
dow = pos;
return (frag);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
upDateList();
View results = inflater.inflate(R.layout.list_fragment, container,
false);
SimpleAdapter adapter = new SimpleAdapter(getParentFragment()
.getActivity(), list, R.layout.listrow, dFrom, dTo);
setListAdapter(adapter);
return results;
}
}
Edit. Solved Code: In List Fragment
The Initial Question has been solved. I am only in the process of implementing the onPostExecute callback to the ListFragmentDisplay. Much Thanks to Luksprog for solving my very noobish mistake. I made dow static without knowing its affect. I think it was actually something that Eclipse offered to solve a conflict. I should have read it closer.
public class MyListFragment extends SherlockListFragment {
public static final String NAME_TAG = "name";
public static final String DESCRIPTION_TAG = "description";
public static final String TAG = "listFragment";
// Keys used in Hashmap that will be mapped to the rows
String[] dFrom = { NAME_TAG, DESCRIPTION_TAG };
private ArrayList<HashMap<String, String>> list;
int[] dTo = { R.id.name, R.id.description };
SimpleAdapter adapter = null; **NEW**
public void upDateList() {
ListFragmentDisplay lFD = (ListFragmentDisplay) this
.getParentFragment();
dList = lFD.getList(getArguments().getInt(TAG)); **NEW**
if(adapter != null) **NEW**
adapter.notifyDataSetChanged(); **NEW**
}
public static MyListFragment newInstance(int pos) {
MyListFragment frag = new MyListFragment();
Bundle args = new Bundle(); **NEW**
args.putInt(TAG, pos); **NEW**
frag.setArguments(args); **NEW**
return (frag);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
upDateList();
View results = inflater.inflate(R.layout.list_fragment, container,
false);
adapter = new SimpleAdapter(getParentFragment()
.getActivity(), list, R.layout.listrow, dFrom, dTo);
setListAdapter(adapter);
return results;
}
}
Is there any reason why you made the dow variable from MyListFragment as static? With the static keyword your fragments from the ViewPager will share their position so you'll call the lFD.getList(dow); method with the wrong position most of the cases. Make dow a private instance field: private int dow;
About the rest of the code, it looks ok, see if the change above solves the problem. To update your data in the inner fragments you could follow this scenario:
start with an empty list of data in ListFragmentDisplay and start the task
initially, your inner ListFragmnents will see that the data list is empty so you'll initialize them with an empty list(the getList(int day) method should just return an empty list if there is no data in the listByDay field)
your task now finishes. Suppose you have a callback from the onPostExecute method of the AsyncTask. In that callback which the ListFragmentDisplay will implement you'll update every Fragment from the ViewPager which is either currently visible to the user or it's in the FragmentPagerAdapter alive(so each Fragment which is not null and its getView() method doesn't return null from the ViewPager will be updated). The other Fragments will self update because the onCreateView method will need to be called for them and you have the updateList call in there.
For the point above keep in mind that calling the updateList method will not update a visible Fragment because in that method you just update the list of the Fragment you don't call notifyDataSetChanged on the adapter to let it know that the data has changed.

Fool-proof way to handle Fragment on orientation change

public class MainActivity extends Activity implements MainMenuFragment.OnMainMenuItemSelectedListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
// add menu fragment
MainMenuFragment myFragment = new MainMenuFragment();
fragmentTransaction.add(R.id.menu_fragment, myFragment);
//add content
DetailPart1 content1= new DetailPart1 ();
fragmentTransaction.add(R.id.content_fragment, content1);
fragmentTransaction.commit();
}
public void onMainMenuSelected(String tag) {
//next menu is selected replace existing fragment
}
I have a need to display two list views side by side, menu on left and its content on right side. By default, the first menu is selected and its content is displayed on right side. The Fragment that displays content is as below:
public class DetailPart1 extends Fragment {
ArrayList<HashMap<String, String>> myList = new ArrayList<HashMap<String, String>>();
ListAdapter adap;
ListView listview;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState!=null){
myList = (ArrayList)savedInstanceState.getSerializable("MYLIST_obj");
adap = new LoadImageFromArrayListAdapter(getActivity(),myList );
listview.setAdapter(adap);
}else{
//get list and load in list view
getlistTask = new GetALLListTasks().execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.skyview_fragment, container,false);
return v;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("MYLIST_obj", myList );
}
}
The onActivityCreated and onCreateView are called twice. There are many examples out there using fragments. Since I am beginner in this subject, I am unable relate the example with my problem. I need a fool proof way to handle orientation change. I have NOT declared android:configChanges in manifest file. I need the activity destroy and recreate so that I can use different layout in landscape mode.
You are creating a new fragment every time you turn the screen in your activity onCreate(); But you are also maintaining the old ones with super.onCreate(savedInstanceState);. So maybe set tag and find the fragment if it exists, or pass null bundle to super.
This took me a while to learn and it can really be a pain when you are working with stuff like viewpager.
I'd recommend you to read about fragments an extra time as this exact topic is covered.
Here is an example of how to handle fragments on a regular orientation change:
Activity:
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
TestFragment test = new TestFragment();
test.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, test, "your_fragment_tag").commit();
} else {
TestFragment test = (TestFragment) getSupportFragmentManager().findFragmentByTag("your_fragment_tag");
}
}
}
Fragment:
public class TestFragment extends Fragment {
public static final String KEY_ITEM = "unique_key";
public static final String KEY_INDEX = "index_key";
private String mTime;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, container, false);
if (savedInstanceState != null) {
// Restore last state
mTime = savedInstanceState.getString("time_key");
} else {
mTime = "" + Calendar.getInstance().getTimeInMillis();
}
TextView title = (TextView) view.findViewById(R.id.fragment_test);
title.setText(mTime);
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("time_key", mTime);
}
}
A good guideline about how to retain data between orientation changes and activity recreation can be found in android guidelines.
Summary:
make your fragment retainable:
setRetainInstance(true);
Create a new fragment only if necessary (or at least take data from it)
dataFragment = (DataFragment) fm.findFragmentByTag("data");
// create the fragment and data the first time
if (dataFragment == null) {

Categories

Resources