I have build a slide.
I have a ViewPage Activity that call a fragment every time that i swip to left or to right.
My question is:
I have a variable int that in the initial state have the value 6 (week_of_year). When i slide to right is call a new fragment and the variable is not increment as i intend. The variable is only increment in the the second slide.
For example: 6 -> 6 -> 7 -> 8 instead of 6 -> 7 -> 8 -> And i don't have ideia why this happens.
Here is my code:
ViewPage Activity
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_slide);
/**
* Instantiate a ViewPager and a PagerAdapter
*/
date = Calendar.getInstance();
mPager = (ViewPager)findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
if(itemOld < arg0){//right
auxFront = auxFront + 1;
oldStartWeek = oldStartWeek + auxFront;
auxFront=0;
}
else if(itemOld > arg0){//left
auxBack = auxBack - 1;
oldStartWeek = oldStartWeek + auxBack;
auxBack=0;
}
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
#Override
public void onBackPressed(){
if(mPager.getCurrentItem()==0){
super.onBackPressed();
}else{
mPager.setCurrentItem(mPager.getCurrentItem()-1);
}
}
/**
* A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in sequence
*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter{
public ScreenSlidePagerAdapter(FragmentManager fm){
super(fm);
}
#Override
public ScreenSlidePageFragment getItem(int position) {
/**
* Pass values from ViewPage activity to Fragment
*/
ScreenSlidePageFragment f = new ScreenSlidePageFragment();
Bundle args = new Bundle();
if(position == 0){
oldStartWeek = date.get(Calendar.WEEK_OF_YEAR);
args.putInt("START_WEEK", oldStartWeek);
f.setArguments(args);
return f;
}
else if(position != 0){
args.putInt("START_WEEK", oldStartWeek);
f.setArguments(args);
itemOld = mPager.getCurrentItem();
return f;
}
return f;
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
Fragment that is call in each slide--------------------------------------------
public class ScreenSlidePageFragment extends Fragment implements
AdapterView.OnItemClickListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Toast.makeText(getActivity(), "week....." +getArguments().getInt("START_WEEK"), Toast.LENGTH_LONG).show(); //show the correct value
headerWeek.setText(" Week Number: "+getArguments().getInt("START_WEEK",0)); //But is not change corretly in the text view ?????
Thanks for your help and time.
To see why this happens in your ViewPager, you can Log the onCreate, onPause, onResume, etc methods for each fragment. What happens is that the first two fragments are actually created about the same time. So the second fragment is created before it actually displays in a ViewPager, so by the time the user scrolls to the second fragment, there is often old data there.
My solution to this is a little hacky. Note that instead of displayDialog() you would do whatever you needed to update the view when the user is present.
#Override
public void onResume() {
super.onResume();
fragmentActive = true;
if (userVisible()){
displayDialog();
}
else{
recursiveWait();
}
}
private void recursiveWait(){
new WaitAndDoXTask(1000, new DoXListener() {
#Override
public void doX() {
Log.d(TAG, "doX");
if (userVisible()){
displayDialog();
}
else if (fragmentActive){
recursiveWait();
}
}
}).execute();
}
private boolean userVisible(){
boolean vis = getUserVisibleHint();
Log.d(TAG, "user visible = " + vis);
return vis;
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
fragmentActive = false;
}
Here is the WaitAndDoXTask:
public class WaitAndDoXTask extends AsyncTask<Void, Void, Void>{
DoXListener mListener;
private int mTimeToWait;
/** This class will wait for the set time then do what is put into the listener.
*
*
* Run like this:
* new WaitAndDoXTask(1000, new DoXListener() {
*
* #Override
* public void doX() {
* // TODO Auto-generated method stub
*
* }
* }).execute();
**/
public WaitAndDoXTask(int timeToWait, DoXListener listener){
super();
mTimeToWait = timeToWait;
mListener = listener;
}
public interface DoXListener{
public void doX();
}
#Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(mTimeToWait);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (mListener!=null){
mListener.doX();
}
}
}
Alternatively, if you could set up your Fragment with some Adapter and get notified via onDataSetChanged that might work better.
Related
I've found several questions about this, none of which help me. Each question relates to other functions and views I don't implement in my fragments, and the issue is not that I need to swap my method getting the FragmentManager to getChildFragmentManager() anywhere in my fragments, because I don't need to get a FragmentManager there.
I'm guessing that my issue stems from the fragments and not the FragmentTabHost in the main activity, but I am not really sure. At all. All I know is that when you page between tabs, the adapter content disappears, but not the fragment itself. All views are still functional, so the functionality of each fragment remains intact.
This issue popped up only after I added a tab change listener for when to initialize the adapter for my chat fragment.
Note that the content of the tabs is fine when they are first initialized, but when you return to the tab the content in the adapters empty. This means that the tab that is not initialized yet when the FragmentTabHost is created, the hidden tabs haven't been initialized yet, so they will still work the first time you page over to them.
Through debugging, I can see that this issue occurs when the transition happens, and all adapters will remain empty for the duration of the usage session. I put this snippit of code before the initial checks in my tabHost.setOnTabChangedListener call:
//Before paging back to an initialized tab for the first time, the adapters of the initialized tab is populated.
Log.d("test", "pre");
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//At this point, the adapter is empty.
Log.d("test", "post");
}
}, 50);
The two fragments are as follows:
public class GroupTasksFragment extends Fragment {
public ArrayAdapter<String> adapter;
private Context context;
public ListView taskListView;
public GroupTasksFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_group_tasks, container, false);
taskListView = (ListView) rootView.findViewById(R.id.tasksList);
adapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, new ArrayList<String>());
taskListView.setAdapter(adapter);
return rootView;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
#Override
public void onDetach() {
super.onDetach();
}
}
public class GroupChatFragment extends Fragment{
public ArrayAdapter<String> adapter;
private Context context;
public ListView chatListView;
public GroupChatFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_group_chat, container, false);
chatListView = (ListView) rootView.findViewById(R.id.chatList);
adapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, new ArrayList<String>());
chatListView.setAdapter(adapter);
return rootView;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
#Override
public void onDetach() {
super.onDetach();
}
}
The main activity with the FragmentTabHost (I have excluded methods that just take input and send content to PubNub):
public class GroupContentActivity extends AppCompatActivity {
private GroupChatFragment chatFrag;
private GroupTasksFragment taskFrag;
private FragmentTabHost tabHost;
private PubNub connection;
private String groupName;
private String nickName;
private boolean chatFragInitialized = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group_content);
tabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
tabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
tabHost.addTab(tabHost.newTabSpec("tasks").setIndicator("Tasks"),
GroupTasksFragment.class, null);
tabHost.addTab(tabHost.newTabSpec("chat")
.setIndicator("Chat"), GroupChatFragment.class, null);
groupName = getIntent().getStringExtra("groupName");
nickName = getIntent().getStringExtra("nickName");
PNConfiguration config = new PNConfiguration();
config.setPublishKey(Constants.publishKey);
config.setSubscribeKey(Constants.subscribeKey);
connection = new PubNub(config);
tabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
#Override
public void onTabChanged(String tabId) {
if (!chatFragInitialized && tabId.equals("chat")) {
chatFragInitialized = true;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
chatFrag = (GroupChatFragment) getSupportFragmentManager().findFragmentByTag("chat");
connection.history()
.channel(groupName)
.count(50)
.async(new PNCallback<PNHistoryResult>() {
#Override
public void onResponse(PNHistoryResult result, PNStatus status) {
for (PNHistoryItemResult item : result.getMessages()) {
final String[] sForm = item.getEntry().getAsString().split(">>>>");
String m = "";
if (sForm.length > 2) {
for (int x = 1; x < sForm.length; x++) {
m += sForm[x];
}
} else {
m = sForm[1];
}
final String mCopy = m;
runOnUiThread(new Runnable() {
#Override
public void run() {
switch (sForm[0]) {
case "groupCreated":
chatFrag.adapter.clear();
break;
case "chat":
chatFrag.adapter.add(mCopy);
}
}
});
}
}
});
}
}, 50);
}
}
});
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
taskFrag = (GroupTasksFragment) getSupportFragmentManager().findFragmentByTag("tasks");
connection.history()
.channel(groupName)
.count(50)
.async(new PNCallback<PNHistoryResult>() {
#Override
public void onResponse(PNHistoryResult result, PNStatus status) {
for (PNHistoryItemResult item : result.getMessages()) {
final String[] sForm = item.getEntry().getAsString().split(">>>>");
String m = "";
if (sForm.length > 2) {
for (int x = 1; x < sForm.length; x++) {
m += sForm[x];
}
} else {
m = sForm[1];
}
final String mCopy = m;
runOnUiThread(new Runnable() {
#Override
public void run() {
switch (sForm[0]) {
case "addTask":
if (taskFrag.adapter.getPosition(mCopy) < 0) {
taskFrag.adapter.add(mCopy);
}
break;
case "deleteTask":
if (taskFrag.adapter.getPosition(mCopy) >= 0) {
taskFrag.adapter.remove(mCopy);
}
break;
case "groupCreated":
taskFrag.adapter.clear();
break;
}
}
});
}
}
});
connection.addListener(new SubscribeCallback() {
#Override
public void status(PubNub pubnub, PNStatus status) {
if (status.getCategory() == PNStatusCategory.PNUnexpectedDisconnectCategory) {
Toast.makeText(getApplicationContext(), "You were disconnected!", Toast.LENGTH_SHORT).show();
} else if (status.getCategory() == PNStatusCategory.PNConnectedCategory) {
if (status.getCategory() == PNStatusCategory.PNConnectedCategory) {
pubnub.publish().channel(groupName).message("chat>>>><ADMIN> User '" + nickName + "' Connected").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult result, PNStatus status) {
}
});
}
} else if (status.getCategory() == PNStatusCategory.PNReconnectedCategory) {
Toast.makeText(getApplicationContext(), "You were reconnected!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void message(PubNub pubnub, PNMessageResult message) {
final String[] sForm = message.getMessage().getAsString().split(">>>>");
String m = "";
if (sForm.length > 2) {
for (int x = 1; x < sForm.length; x++) {
m += sForm[x];
}
} else {
m = sForm[1];
}
final String mCopy = m;
runOnUiThread(new Runnable() {
#Override
public void run() {
switch (sForm[0]) {
case "chat":
if (chatFragInitialized) {
chatFrag.adapter.add(mCopy);
runOnUiThread(new Runnable() {
#Override
public void run() {
chatFrag.chatListView.setSelection(chatFrag.adapter.getCount() - 1);
}
});
}
break;
case "addTask":
taskFrag.adapter.add(mCopy);
connection.publish().channel(groupName).message("chat>>>><ADMIN> Task '" + mCopy + "' added.").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult pnPublishResult, PNStatus pnStatus) {
}
});
break;
case "deleteTask":
taskFrag.adapter.remove(mCopy);
connection.publish().channel(groupName).message("chat>>>><ADMIN> Task '" + mCopy + "' deleted.").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult pnPublishResult, PNStatus pnStatus) {
}
});
break;
}
}
});
}
#Override
public void presence(PubNub pubnub, PNPresenceEventResult presence) {
}
});
connection.subscribe().channels(java.util.Collections.singletonList(groupName)).execute();
}
}, 100);
}
#Override
public void onDestroy(){
super.onDestroy();
connection.publish().channel(groupName).message("chat>>>><ADMIN> User '" + nickName + "' Logged Out.").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult pnPublishResult, PNStatus pnStatus) {
}
});
connection.disconnect();
Toast.makeText(getApplicationContext(), "Logged out", Toast.LENGTH_SHORT).show();
}
//More Methods
}
Also note that the issue is not that I need to store the FragmentManager instance, as that doesn't do anything.
I found my issue. It turns out that every time a fragment is paged to in the FragmentTabHost, it's createView method is called again, and only that method, so by setting the adapter in the fragment to empty in that view, which I thought was only at the start, I reset the adapter each time.
I fixed this by keeping the adapter content as an instance variable list object that I add or remove strings to/from when I want to change the adapter. DO NOT ALSO PUT THE STRINGS IN THE ADAPTER, updating the list is enough. The list will directly add it to the adapter.
Also note that if you set the initial content outside of the fragment, it may not show when the tabs are first initialized. Just be careful of your statement ordering and when things are called. Fragment construction is funky business.
Then, I set the adapter to whatever is in the list each time the createView method is called.
I have view pager. My viewpager contains 3 fragments. In the first fragment I have an AsyncTask class. I parsed JSON with AsyncTask and I can show it in listview. (everything is ok)
I have one problem AsyncTask which I have in the first fragment does not finish when I go to the next fragments. When I am in the second fragment my AsyncTask is also running. How can I write code to cancel my AsyncTask when viewpager's page changed?
This is my source (this is the first fragment source; another fragment source is the same but the only difference is the Server Url):
public class StradaChefs1 extends Fragment {
public static CustomerStatistic stat;
private ConnectionDetector con;
private AlertDialogManager alert = new AlertDialogManager();
#SuppressLint("ClickableViewAccessibility")
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.strada_chefs_1, container,
false);
stat = new CustomerStatistic();
con = new ConnectionDetector(getActivity());
if (!con.isConnectingToInternet()) {
alert.showAlertDialog(getActivity(),
"You have not internet connection");
} else {
stat.execute("my urlllllllll"); // geo
}
return rootView;
}
public class CustomerStatistic extends AsyncTask<String, Void, String> {
ProgressDialog pDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = ProgressDialog.show(getActivity(), "Please Wait... ",
"Loading... ");
pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pDialog.setCancelable(false);
pDialog.show();
}
#Override
protected String doInBackground(String... params) {
return Utils.getJSONString(params[0]);
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
try {
JSONArray mainJson = new JSONArray(result);
String first = mainJson.getString(0);
JSONObject jobject = new JSONObject(first);
String image = jobject.getString("image");
String String_title = jobject.getString("title");
String String_name = jobject.getString("name");
String String_desc = jobject.getString("description");
String second = mainJson.getString(1);
} catch (JSONException e) {
e.printStackTrace();
}
if (pDialog != null) {
pDialog.dismiss();
pDialog = null;
}
}
}
#Override
public void onResume() {
Log.e("DEBUG", "onResume of HomeFragment");
super.onResume();
}
#Override
public void onStop() {
super.onStop();
if (stat != null && stat.equals(AsyncTask.Status.RUNNING)) {
stat.cancel(true);
Toast.makeText(getActivity(), "finished", Toast.LENGTH_SHORT)
.show();
}
}
}
This is a viewpager java code
public class TabbedActivity1 extends Fragment {
private StradaChefs1 mfragment1;
private StradaChefs2 mfragment2;
private StradaChefs3 mfragment3;
private StradaChefs4 mfragment4;
SectionsPagerAdapter mSe;
public static final String TAG = TabbedActivity1.class.getSimpleName();
ViewPager mViewPager;
private ArrayList<Fragment> fragmentList;
public static TabbedActivity1 newInstance() {
return new TabbedActivity1();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_item_one_1, container, false);
mSe=new SectionsPagerAdapter(getChildFragmentManager());
mViewPager = (ViewPager) v.findViewById(R.id.pager1);
CirclePageIndicator circle=(CirclePageIndicator)v.findViewById(R.id.circleindicator1);
mViewPager.setAdapter(mSe);
circle.setViewPager(mViewPager);
mfragment1 = new StradaChefs1();
mfragment2 = new StradaChefs2();
mfragment3 = new StradaChefs3();
mfragment4 = new StradaChefs4();
fragmentList = new ArrayList<Fragment>();
fragmentList.add(mfragment1);
fragmentList.add(mfragment2);
fragmentList.add(mfragment3);
fragmentList.add(mfragment4);
mViewPager.setPageTransformer(false, new PageTransformer() {
#Override
public void transformPage(View page, float position) {
page.setRotationY(position * -40);
}
});
return v;
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
#Override
public int getCount() {
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
return null;
}
}
}
How can I solve this problem?
The FragmentPagerAdapter keeps additional fragments, besides the one shown, in resumed state, so you can't use onPause and onResume for starting/stopping the AsyncTask. The solution is to implement a custom OnPageChangeListener and create a new method for when the fragment is shown.
1) Create LifecycleManager Interface The interface will have two methods and each ViewPager’s Fragment will implement it. These methods Are as follows:
public interface FragmentLifecycle {
public void onPauseFragment();
public void onResumeFragment();
}
2) Let each Fragment implement the interface
3) Implement interface methods in each fragment - in onPauseFragment stop the AsyncTask, in onResumeFragment start it
4) Call interface methods on ViewPager page change You can set OnPageChangeListener on ViewPager and get callback each time when ViewPager shows another page
5) Implement OnPageChangeListener to call your custom Lifecycle methods
Listener knows the new position and can call the interface method on new Fragment with the help of PagerAdapter. I can here call onResumeFragment() for new fragment and onPauseFragment() on the current one.
I need to store also the current fragment’s position (initially the current position is equal to 0), since I don’t know whether the user scrolled from left to right or from right to left. See what I mean in code:
private OnPageChangeListener pageChangeListener = new OnPageChangeListener() {
int currentPosition = 0;
#Override
public void onPageSelected(int newPosition) {
FragmentLifecycle fragmentToShow = (FragmentLifecycle)pageAdapter.getItem(newPosition);
fragmentToShow.onResumeFragment();
FragmentLifecycle fragmentToHide = (FragmentLifecycle)pageAdapter.getItem(currentPosition);
fragmentToHide.onPauseFragment();
currentPosition = newPosition;
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) { }
public void onPageScrollStateChanged(int arg0) { }
};
I didn't write the code. Full tutorial here
When you start the asynctask set the flag isRunning=true
when you are trying to jump from one fragment to other it mean
as per fragment lifecycle your are pausing and stoping your current fragment
so in onStop method of fragment you can check isRunning flag of asynctask is true if yes the
cancel the asyntask
its my logic hope it will help you to achieve your requirement
I have a fragment (YearsFragment) with a list. If I klick one item a new fragment (EventsFragment) will be executed.
The loader of the new fragment loads a new list. This works.
Now my Problem:
when I go back to the first YearsFragment and klick the same item the EventsFragment loads again. But I will get the old Loader with the old data.
EventsActivity:
public class EventsActivity extends FragmentActivity implements YearsFragment.OnYearSelectedListener, EventsFragment.OnEventSelectedListener{
private boolean screenSizeLarge;
private EventsFragment eventFrag = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.years_events);
// Check whether the activity is using the layout version with the fragment_container FrameLayout (small Display).
if (findViewById(R.id.fragment_container) != null) {
screenSizeLarge = false; //one-pane
YearsFragment firstFragment = new YearsFragment();
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, firstFragment).commit();
}else{
screenSizeLarge = true; //two-pane
}
}
#Override
public void onYearSelected(Years year) {
// The user selected the year from the YearsFragment
if (screenSizeLarge) {
// two-pane layout
// Capture the events fragment from the activity layout
eventFrag = (EventsFragment) getSupportFragmentManager().findFragmentById(R.id.event_fragment);
// Call a method in the EventFragment to update its content
eventFrag.updateEventsView(year);
} else {
//one-pane layout
if(eventFrag == null){
eventFrag = new EventsFragment();
}
Bundle args = new Bundle();
args.putInt("year", Integer.parseInt(year.getYear()));
eventFrag.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, eventFrag);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}}
YearsFragment:
public class YearsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Years>>{
YearsAdapter adapter;
// The container Activity must implement this interface so the frag can deliver messages
OnYearSelectedListener mCallback;
public interface OnYearSelectedListener {
/** Called by YearsFragment when a list item is selected */
public void onYearSelected(Years year);
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onResume() {
super.onResume();
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
adapter = new YearsAdapter(getActivity());
setListAdapter(adapter);
setEmptyText("No Data Here");
setListShown(false);
getLoaderManager().initLoader(0, null, this);
}
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
mCallback = (OnYearSelectedListener) activity;
} catch (ClassCastException e){
throw new ClassCastException(activity.toString() +
"must implement OnYearSelectedListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
mCallback.onYearSelected(adapter.getItem(position));
}
//implements Loader
#Override
public Loader<List<Years>> onCreateLoader(int arg0, Bundle arg1) {
Log.i("YearsFragment","YearsFragment.onCreateLoader");
return new YearsLoader(getActivity());
}
#Override
public void onLoadFinished(Loader<List<Years>> arg0, List<Years> data) {
Log.i("YearsFragment","YearsFragment.onLoadFinished");
adapter.setData(data);
if(isResumed()){
setListShown(true);
}else{
setListShownNoAnimation(false);
}
}
#Override
public void onLoaderReset(Loader<List<Years>> arg0) {
Log.i("YearsFragment","YearsFragment.onLoaderReset");
adapter.setData(null);
}}
EventsFragment:
public class EventsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<Events>>{
EventsAdapter adapter;
private int year;
public EventsFragment(){
year = Calendar.getInstance().get(Calendar.YEAR);
}
// The container Activity must implement this interface so the frag can deliver messages
OnEventSelectedListener mCallback;
public interface OnEventSelectedListener {
/** Called by YearsFragment when a list item is selected */
public void onEventSelected(Events event);
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Log.i("EventsFragment","EventsFragment.onActivityCreated");
adapter = new EventsAdapter(getActivity());
setListAdapter(adapter);
setEmptyText("No Data Here");
setListShown(false);
}
#Override
public void onResume() {
super.onResume();
Bundle args = this.getArguments();
if(args != null){
year = args.getInt("year");
}
updateEventsView(year);
// The activity has become visible (it is now "resumed").
}
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
mCallback = (OnEventSelectedListener) activity;
} catch (ClassCastException e){
throw new ClassCastException(activity.toString() +
"must implement OnEventsSelectedListener");
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
mCallback.onEventSelected(adapter.getItem(position));
}
public void updateEventsView(Years year) {
this.year = Integer.parseInt(year.getYear());
getLoaderManager().initLoader(this.year, null, this);
}
public void updateEventsView(int year) {
this.year = year;
getLoaderManager().initLoader(this.year, null, this);
}
//Loader
#Override
public Loader<List<Events>> onCreateLoader(int arg0, Bundle arg1) {
Log.i("EventsFragment","EventsFragment.onCreateLoader");
return new EventsLoader(getActivity(),this.year);
}
#Override
public void onLoadFinished(Loader<List<Events>> arg0, List<Events> data) {
Log.i("EventsFragment","EventsFragment.onLoadFinished");
adapter.setData(data);
if(isResumed()){
setListShown(true);
}else{
setListShownNoAnimation(false);
}
}
#Override
public void onLoaderReset(Loader<List<Events>> arg0) {
Log.i("EventsFragment","EventsFragment.onLoaderReset");
adapter.setData(null);
} }
How I should do the resume?
greets Mr.P.
I need to change a TextView's text that is placed in the layout of my FragmentActivity that has a ViewPager and a ViewPagerAdapter. Basically I have created an action bar in my application that presents the name of the current visible Fragment.
So I need this TextView to be changed on each change of the visible fragment in the ViewPager (either with the swipe motion or selected using the TabHost). From my search I found that this method should help me achieve my goal:
#Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
Log.d(TAG, "setUserVisibleHint invoked!");
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser)
{
if (odTab != null && getActivity() != null)
{
Log.d(TAG, "Currently visable tab: "+odTab.getTabTitle());
Toast.makeText(getActivity(), "Currently visable tab: "+odTab.getTabTitle(), Toast.LENGTH_LONG).show();
if (application.getCurrentDataSource() == DataSource.SSRS)
{
if (rsTab.getTabTitle().equals("") || rsTab.getTabTitle() == null)
{
tabName.setVisibility(View.GONE);
}
else
{
tabName.setText(rsTab.getTabTitle());
}
}
else if (application.getCurrentDataSource() == DataSource.SGRDL)
{
Log.d(TAG, "found Title textView from fragmentActivity");
if (odTab.getTabTitle().equals("") || odTab.getTabTitle() == null)
{
((TabsViewPagerFragmentActivity)getActivity()).findViewById(R.id.tvTabName).setVisibility(View.GONE);
}
else
{
Log.d(TAG, "set Title textView to: "+odTab.getTabTitle());
((TextView) ((TabsViewPagerFragmentActivity)getActivity()).findViewById(R.id.tvTabName)).setText(odTab.getTabTitle());
}
}
}
}
}
I tried to implement it in my fragment but for some reason this line:
((TextView) ((TabsViewPagerFragmentActivity)getActivity()).findViewById(R.id.tvTabName)).setText(odTab.getTabTitle());
doesn't do anything although this line do get presented in the log for each change of fragment in the viewPager:
Log.d(TAG, "set Title textView to: "+odTab.getTabTitle());
UPDATE:
My adapter:
public class ViewPagerAdapter extends FragmentPagerAdapter
{
private List<Fragment> fragments;
/**
* #param fm
* #param fragments
*/
public ViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentPagerAdapter#getItem(int)
*/
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
/* (non-Javadoc)
* #see android.support.v4.view.PagerAdapter#getCount()
*/
#Override
public int getCount() {
return this.fragments.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Am I doing something wrong? is there another way to do this operation?
In your activity you can do:
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int pos) {
// Change your textview
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
});
Although I can't pinpoint the problem, I would suggest to simplify your code:
avoid cast within cast. You use TabsViewPagerFragmentActivity both in if and else. assign this cast to a parameter and use this parameter. The processing overhead worth it.
instead of using findViewById, save R.id.tvTabName as a local parameter in TabsViewPagerFragmentActivity and create a method setTitle(String title) in TabsViewPagerFragmentActivity. It will also help you to debug this call at the activity itself.
I agree with #mromer - try to access the fragment from the activity. To achieve this, make your FragmentPagerAdapter inherit the following class:
public abstract class SmartFragmentPagerAdapter extends FragmentPagerAdapter {
private SparseArray<String> mFragmentTags;
private FragmentManager mFragmentManager;
public SmartFragmentPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
mFragmentManager = fragmentManager;
mFragmentTags = new SparseArray<String>();
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
// record the fragment tag here.
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
// use this method in onPageSelected
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
public FragmentManager getFragmentManager() {
return mFragmentManager;
}
}
The way I finally managed to deal with this issue is combined with a few things:
1. First I declared an interface in my fragment:
public interface OnFragmetVisibleListener{
public void fragmetVisible(boolean visible);
}
2. Next I added this function as well to the fragment to know when it's attached to the ViewPager:
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
onFragmetVisibleListener = (OnFragmetVisibleListener) activity;
isAttached = true; //flag for whether this framgnet is attache to pager
} catch (ClassCastException e){
throw new ClassCastException(activity.toString() + " must implement interface OnFragmetVisibleListener");
}
}
3. I added the following method as well to know when the fragment is visible and set it's data object in the Application class:
#Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
Log.d(TAG, "setUserVisibleHint invoked!");
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser)
{
if (odTab != null && getActivity() != null)
{
Log.d(TAG, "Currently visable tab: "+odTab.getTabTitle());
application.currentVisibleTab = odTab;
}
if (isAttached)
{
onFragmetVisibleListener.fragmetVisible(true);
}
}
}
4. In my ViewPager's activity I let it implement the defined interface in step 1:
public class TabsViewPagerFragmentActivity extends FragmentActivity implements GridFragment.OnFragmetVisibleListenerr
5. Next I added the unimplemented method:
#Override
public void fragmetVisible(boolean visible)
{
if(visible)
{
setCurrentTabTitle(application.currentVisibleTab.getTabTitle());
}
}
6. when setCurrentTabTitle is:
public void setCurrentTabTitle(String title)
{
tvTabTitle.setText(title); //my TextView that's needs to be changed.
Log.d(TAG, "set tab title from activity: "+title);
}
And that is it, this is how I changed a view of the activity depending on the visible fragment of the ViewPager.
I have a problem regarding with FragmentActivity and mutltiple Fragments inside a ViewPager.
In the FragmentActivity an object is loaded, with a AsyncTask which is used in all the other fragments. I have used the android:configChanges="orientation|keyboardHidden|keyboard" "hack" to make sure the object is only loaded once, even during a screen rotation.
However, now I would to like to display more infromation in landscape modus in one of the Fragments, so now that hack doesn't work.
I've tried implementing a AsyncLoader and the FragmentRetainInstanceSupport from the Android samples. But none of the things work:
1 - I can't get the FragmentRetainInstanceSupport get to work within the ViewPager, when I follow the sample code the onCreate() method isn't called in the worker-fragment
2 - The AsyncLoader crashes during a screen rotation...
Here is my code in which I (tried to) implement the AsyncLoader:
public class TeamActivity extends SherlockFragmentActivity implements LoaderManager.LoaderCallbacks<Response<Team>> {
ViewPager mPager;
PageIndicator mIndicator;
FragmentPagerAdapter mAdapter;
private final int MENU_FOLLOW = Menu.FIRST;
private final int MENU_UNFOLLOW = Menu.FIRST + 1;
Team team = null;
static int team_id;
public Team getTeam(){
return team;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
team_id = this.getIntent().getIntExtra("index", 0);
Log.d("Teamid",""+team_id);
getSupportLoaderManager().initLoader(0, null, this);//.forceLoad();
//getSupportLoaderManager().getLoader(0).startLoading();
//new getTeam().execute();
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if(team != null) {
team.getNaam();
SharedPreferences keyValues = this.getSharedPreferences("teams_follow", Context.MODE_PRIVATE);
MenuItem menuItem_volg = menu.findItem(MENU_FOLLOW);
MenuItem menuItem_delete = menu.findItem(MENU_UNFOLLOW);
if(keyValues.contains(String.valueOf(team.getStartnummer()))) {
menuItem_volg.setVisible(false);
menuItem_delete.setVisible(true);
} else {
menuItem_volg.setVisible(true);
menuItem_delete.setVisible(false);
}
}
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0,MENU_UNFOLLOW,Menu.NONE, R.string.ab_verwijderen)
.setIcon(R.drawable.ic_action_delete)
.setVisible(false)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menu.add(0,MENU_FOLLOW,Menu.NONE, R.string.ab_volgen)
.setIcon(R.drawable.ic_action_star)
.setVisible(false)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Utils.goHome(getApplicationContext());
break;
case MENU_FOLLOW:
Utils.addFavoTeam(getApplicationContext(), team);
invalidateOptionsMenu();
break;
case MENU_UNFOLLOW:
Utils.removeFavoteam(getApplicationContext(), team.getID());
invalidateOptionsMenu();
break;
}
return super.onOptionsItemSelected(item);
}
class TeamFragmentAdapter extends FragmentPagerAdapter implements TitleProvider {
ArrayList<Fragment> fragments = new ArrayList<Fragment>();
ArrayList<String> titels = new ArrayList<String>();
public TeamFragmentAdapter(FragmentManager fm) {
super(fm);
fragments.add(new TeamInformatieFragment());
titels.add("Informatie");
fragments.add(new TeamLooptijdenFragment());
titels.add("Routetijden");
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public String getTitle(int position) {
return titels.get(position);
}
}
private class getTeam extends AsyncTask<Void, Void, Void> {
private ProgressDialog progressDialog;
Response<Team> response;
protected void onPreExecute() {
progressDialog = ProgressDialog.show(TeamActivity.this,
"Bezig met laden", "Team wordt opgehaald...", true);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
cancel(true);
Utils.goHome(TeamActivity.this);
}
});
}
#Override
protected Void doInBackground(Void... arg0) {
if(!isCancelled())
response = api.getTeamByID(team_id);
return null;
}
#Override
protected void onPostExecute(Void result) {
if(Utils.checkResponse(TeamActivity.this, response)) {
setContentView(R.layout.simple_tabs);
team = response.getResponse();
mAdapter = new TeamFragmentAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
invalidateOptionsMenu();
progressDialog.dismiss();
}
}
}
public static class AppListLoader extends AsyncTaskLoader<Response<Team>> {
Response<Team> response;
public AppListLoader(Context context) {
super(context);
}
#Override public Response<Team> loadInBackground() {
response = api.getTeamByID(team_id);
return response;
}
#Override public void deliverResult(Response<Team> response) {
if (isReset()) {
return;
}
this.response = response;
super.deliverResult(response);
}
#Override protected void onStartLoading() {
if (response != null) {
deliverResult(response);
}
if (takeContentChanged() || response == null) {
forceLoad();
}
}
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
response = null;
}
}
private ProgressDialog progressDialog;
#Override
public Loader<Response<Team>> onCreateLoader(int arg0, Bundle arg1) {
progressDialog = ProgressDialog.show(TeamActivity.this,
"Bezig met laden", "Team wordt opgehaald...", true);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
finish();
}
});
return new AppListLoader(this);
}
#Override
public void onLoadFinished(Loader<Response<Team>> loader, Response<Team> response) {
//Log.d("Loader", "Klaar");
if(Utils.checkResponse(TeamActivity.this, response)) {
team = response.getResponse();
setContentView(R.layout.simple_tabs);
mAdapter = new TeamFragmentAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
invalidateOptionsMenu();
progressDialog.dismiss();
}
}
#Override
public void onLoaderReset(Loader<Response<Team>> arg0) {
//Utils.goHome(this);
}
}
Fragment (example):
public class TeamInformatieFragment extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Team team = ((TeamActivity)this.getActivity()).getTeam();
//ERROR ON NEXT LINE AFTER SCREEN ROTATION:
getSherlockActivity().getSupportActionBar().setTitle(team.getNaam());
View view = inflater.inflate(R.layout.team_informatie, container, false);
return view;
}
}
The method is called from the fragments (with getActivity().getTeam()) but after a screen rotation getTeam() returns null;
I think the fragments are calling getTeam() too fast, before the variable team has been initialized(?)
Can you please help me?
Thank you!
This is probably not what you want to hear, but I recommend getting rid of
android:configChanges="orientation|keyboardHidden|keyboard"
It's an ugly hack, and a lot of the newer SDK elements like Loaders will break if you don't handle configuration changes correctly. Let Android handle the config changes, and design your code around that.