I have a main activity and three fragments under it that can be switched by swiping left and right. Nothing special. The 1st and 3rd(last) fragment both use Asynctask to grap some info from online. What I'm trying to achieve here is three fragments that load up info from online and later on, I want to add in the pull down to refresh, but that shouldn't be a problem.
Here's what happens.
When you open the app, its set to open the 2nd(middle) fragment.
Upon swiping to the 3rd fragment, you'll see that everything loads up fine.
Upon returning to the 2nd fragment and going back to the 3rd, it completely disappears and returns as a blank page.
Now if I scroll twice back to the 1st fragment, and complete the network operation, it works fine.
Try to do it again, the button doesn't reply.
I believe it has something to do with the Asynctask. If there's a better way to setup this whole thing I have here, then please feel free to do so.
Main
public class Main extends FragmentActivity {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {#link android.support.v4.app.FragmentPagerAdapter} derivative, which
* will keep every loaded fragment in memory. If this becomes too memory
* intensive, it may be best to switch to a
* {#link android.support.v4.app.FragmentStatePagerAdapter}.
*/
SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.a_main);
// Create the adapter that will return a fragment for each of the three
// primary sections of the app.
mSectionsPagerAdapter = new SectionsPagerAdapter(
getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.setCurrentItem(1, false);
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a DummySectionFragment (defined as a static inner class
// below) with the page number as its lone argument.
// Fragment fragment = new DummySectionFragment();
// Bundle args = new Bundle();
// args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
// fragment.setArguments(args);
// return fragment;
switch (position) {
case 0:
// Top Rated fragment activity
return new Post();
case 1:
// Games fragment activity
return new Feed();
case 2:
// Movies fragment activity
return new Explore();
}
return null;
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return "Post";
case 1:
return "Feed";
case 2:
return "Explore";
}
return null;
}
}
}
1st Fragment
public class Post extends Fragment {
private MyAsyncTask mAuthTask = null;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.f_post, container, false);
Button addGoalButton = (Button) rootView.findViewById(R.id.submit);
addGoalButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
// Pass the fragmentView through to the handler
// so that findViewById can be used to get a handle on
// the fragments own views.
attemptLogin();
}
});
return rootView;
}
public void attemptLogin() {
if (mAuthTask != null) {
return;
}
mAuthTask = new MyAsyncTask();
mAuthTask.execute((Void) null);
}
private class MyAsyncTask extends AsyncTask<Void, Void, Void>
{
#Override
protected void onPostExecute(Void result) {
//mProgressDialog.dismiss();
}
#Override
protected void onPreExecute() {
Toast.makeText(getActivity(),
"dont worry, be happy!", Toast.LENGTH_LONG).show();
}
#Override
protected Void doInBackground(Void... params) {
// my network operation
return null;
}
}
}
3rd Fragment
public class Explore extends Fragment {
private MyAsyncTask mAuthTask = null;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.f_explore, container, false);
attemptLogin();
return rootView;
}
public void attemptLogin() {
if (mAuthTask != null) {
return;
}
mAuthTask = new MyAsyncTask();
mAuthTask.execute((Void) null);
}
private class MyAsyncTask extends AsyncTask<Void, Void, Void>
{
#Override
protected void onPostExecute(Void result) {
//mProgressDialog.dismiss();
}
#Override
protected void onPreExecute() {
Toast.makeText(getActivity(),
"Loading Explore", Toast.LENGTH_LONG).show();
}
#Override
protected Void doInBackground(Void... params) {
// my network operation
return null;
}
}
}
I think it a may be a couple issues.
Multiple Asynctasks cannot be run at the same time and with Viewpagers it loads 3 views by default. I believe you need to call: (whereever you don't want a code to be run for all the views which need to be loaded)
if(getUserVisibleHint()) {
//logic in here
attemptLogin();
}
I believe this will stop your onCreateView() method being called 3 times. So pretty much because a view pager has this method: mPager.setOffscreenPageLimit(1); which does this:
Set the number of pages that should be retained to either side of the current page
in the view hierarchy in an idle state.
By default it is at 1, which means if it will load the fragment to the left and right to make scrolling smooth. (You can't change this to 0). And so the getUserVisibleHint will check if the current view is the one the suer can see and call whatever stuff you put inside it, only when its visible. (because by default it would call your asynctask 3 times, and they cannot be run at the same time.
I may be wrong though.
Edit #1:
First fragment fixed by setting asynctask back to null in onPostExecute.
Related
I have a navigation drawer activity, with one fragment having a view pager and tabs. All 4 fragments are fetching data from a server. My problem is that the view pager is loading the first 2 fragments therefore my first fragment doesn't show a content at first because of the delay of the rest api. So the second fragment is being created and shown before the data in the first fragment is parsed and shown. How can I solve this?
This is my fragment container
public class FragmentMoviesContainer extends KFragment {
private MainActivity activity;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_movies_container, container, false);
activity = (MainActivity) getActivity();
assert activity != null;
activity.setVisibleFragment(this);
SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager());
// Set up the ViewPager with the sections adapter.
ViewPager mViewPager = rootView.findViewById(R.id.container);
TabLayout tabLayout = rootView.findViewById(R.id.tabs);
mViewPager.setAdapter(mSectionsPagerAdapter);
tabLayout.setupWithViewPager(mViewPager);
return rootView;
}
#Override
public void onResume() {
super.onResume();
ActionBar actionBar = activity.getSupportActionBar();
if (actionBar != null)
actionBar.setTitle(R.string.movies);
activity.getNavigationView().setCheckedItem(R.id.nav_movies);
activity.setElevation(true);
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return replaceFragmentMovies(Constants.STRINGS.UPCOMING);
case 1:
return replaceFragmentMovies(Constants.STRINGS.NOW_PLAYING);
case 2:
return replaceFragmentMovies(Constants.STRINGS.POPULAR);
case 3:
return replaceFragmentMovies(Constants.STRINGS.TOP_RATED);
default:
return null;
}
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.coming_soon);
case 1:
return getString(R.string.now_playing);
case 2:
return getString(R.string.popular);
case 3:
return getString(R.string.top_rated);
default:
return "";
}
}
#Override
public int getCount() {
return 4;
}
private FragmentMovies replaceFragmentMovies(String type) {
FragmentMovies fragmentMovies = new FragmentMovies();
fragmentMovies.setType(type);
return fragmentMovies;
}
}
#Override
public void serviceResponse(int responseID, List<KObject> objects) {
}
#Override
public void update(ModelService service, boolean reload) {
}
}
Here's my fragment showed in the tabs
public class FragmentMovies extends KFragment implements MoviesAdapter.OnLoadMoreListener {
private MainActivity activity;
private ModelService service;
private RecyclerView moviesRv;
private String type;
public void setType(String type) {
this.type = type;
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_movies, container, false);
activity = (MainActivity) getActivity();
if (activity != null) {
service = activity.getService();
activity.setVisibleFragment(this);
}
moviesRv = rootView.findViewById(R.id.movies_list);
moviesRv.setLayoutManager(new LinearLayoutManager(getContext()));
this.update(service, false);
return rootView;
}
#Override
public void serviceResponse(int responseID, List<KObject> objects) {
if ((objects != null && !objects.isEmpty()) && (responseID == Constants.UPCOMING || responseID == Constants.NOW_PLAYING || responseID == Constants.POPULAR
|| responseID == Constants.TOP_RATED)) {
Section section = (Section) objects.get(0);
MovieListAdapter adapter = new MovieListAdapter(getContext(), section.getMovieList());
moviesRv.setAdapter(adapter);
}
}
#Override
public void update(final ModelService service, final boolean reload) {
boolean hasConnection = Connection.isNetworkAvailable(getContext());
if (hasConnection && service != null) {
final int responseId = getResponseID();
service.getMovies(type, "", false, responseId, reload);
} else {
// progressBar.setVisibility(View.GONE);
DialogHelper.noConnectionDialog(getContext());
}
}
private int getResponseID() {
switch (type) {
case Constants.STRINGS.UPCOMING:
return Constants.UPCOMING;
case Constants.STRINGS.NOW_PLAYING:
return Constants.NOW_PLAYING;
case Constants.STRINGS.POPULAR:
return Constants.POPULAR;
case Constants.STRINGS.TOP_RATED:
return Constants.TOP_RATED;
default:
return 0;
}
}
#Override
public void onLoadMore(MoviesAdapter adapter) {
}
#Override
public void onResume() {
super.onResume();
if (activity.getSupportActionBar() != null)
activity.getSupportActionBar().setTitle("Movies");
activity.getNavigationView().setCheckedItem(R.id.nav_movies);
activity.setElevation(true);
activity.getAddFab().hide();
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
The method update calls the rest api url and fetches the data. This is a framework I created based on AsyncTask. The list of objects then is returned to the fragment parsed in the method onServiceResponse where I create the adapter and show the data. The problem is that the second fragment is being created before the method onServiceResponse of the first fragment.
You should make api call from the first fragment and after getting the result you should make the rest of the calls. Let me know if you need any help with the code. I think this should be straight forward.
After Looking your code, there are Two things to inflate Fragments on to tabs.
Use single Fragment for all tabs.
Use individual fragment for every tab.
in the First case, if you are calling APIs form fragment that kind of problem occurs(As yours).
in the Second case APIs, the call will be in individual fragment and there will not be such kind of problem.
So the first solution to your problem is to use individual fragment for every tab.
And if really want to use single fragment for every tab then maintain the sequence of API calling for every instance of the fragment for every tab.
As you are doing in fragment like:
if (activity != null) {
service = activity.getService();
activity.setVisibleFragment(this);
}
moviesRv = rootView.findViewById(R.id.movies_list);
moviesRv.setLayoutManager(new LinearLayoutManager(getContext()));
this.update(service, false);
in this case you are calling service and and then you are reading setting your view.
The scenario is that here API call will be in the background but the code below API call will execute. Due to that if the response of API any fragment comes then that fragment view will be populated. So Solution of that scenario is that put your API call method in fragment and then call APIs and maintain calls.
if any help just comments. thanks.
I think the accepted answer is not very explanatory, so for anyone coming across this in future, this is what I did. I am calling my REST API from the on create method of the activity hosting the fragments and viewpager and using a single fragment class for 6 tabs by creating 6 instances of the fragment class. But the catch here is, dont set up the viewpager in onCreate, rather set it after the API call receives a successful response, after the data has been saved inside some object. So now the data is ready to be displayed within the fragment when it is first presented.
You should add this code on your one of your fragment.
Handler().postDelayed({
//api call
}, 3000)
So that two fragment can not do api call at same time when you use viewpager.
For the 2 last days I spent all my time to try to find a solution to a strange problem in a viewpager with fragment. All fragment contains a listview (which has his own adapter).
Here is the situation :
I have an activity which instantiate a viewpager and an adapter for it and contains the object adapter class.
public class SellerActivity extends AppCompatActivity {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {#link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {#link android.support.v4.app.FragmentStatePagerAdapter}.
*/
private SectionsPagerAdapter mSectionsPagerAdapter;
private FirebaseAuth mAuth = FirebaseAuth.getInstance();
/**
* The {#link ViewPager} that will host the section contents.
*/
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_seller);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.vpContainer2);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs2);
tabLayout.setupWithViewPager(mViewPager);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
// This method will be invoked when a new page becomes selected.
#Override
public void onPageSelected(int position) {
mViewPager.setCurrentItem(position);
mSectionsPagerAdapter.getItem(position);
Toast.makeText(SellerActivity.this,
"Selected page position: " + position, Toast.LENGTH_SHORT).show();
}
// This method will be invoked when the current page is scrolled
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Code goes here
}
// Called when the scroll state changes:
// SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING
#Override
public void onPageScrollStateChanged(int state) {
// Code goes here
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_shop, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
Intent i = new Intent(SellerActivity.this, MainActivity.class);
if (id == R.id.action_home) {
startActivity(i);
finish();
return true;
}
if (id == R.id.action_logout) {
mAuth.signOut();
startActivity(i);
finish();
return false;
}
return super.onOptionsItemSelected(item);
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return SellerFragment.newInstance(position + 1, "1");
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "En Attente";
case 1:
return "En cours";
case 2:
return "Terminées";
}
return null;
}
}
I have then a fragment which looks like this :
public class SellerFragment extends Fragment {
private String title;
private int page;
private AllOrders allOrders;
final List<CommandItemForSeller> orders = new ArrayList<>();
final LinkedHashMap<String, Order> saveOrders = new LinkedHashMap<>();
public SellerFragment(){
}
// newInstance constructor for creating fragment with arguments
public static SellerFragment newInstance(int page, String title) {
SellerFragment fragmentFirst = new SellerFragment();
Bundle args = new Bundle();
args.putInt("someInt", page);
args.putString("someTitle", title);
fragmentFirst.setArguments(args);
return fragmentFirst;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
page = getArguments().getInt("someInt", 0);
title = getArguments().getString("someTitle");
allOrders = new AllOrders();
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_seller, container, false);
TextView tvLabel = (TextView) view.findViewById(R.id.tvLabel);
tvLabel.setText(page + " -- " + title);
DatabaseReference myRef = FirebaseDatabase.getInstance().getReference().child("orders");
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot orders : dataSnapshot.getChildren()) {
String orderString = orders.getValue(String.class);
//allOrders.addOrder(new Order(orderString, orders.getKey().substring(orders.getKey().length() - 4, orders.getKey().length())));
saveOrders.put(orders.getKey(), new Order(orderString, orders.getKey().substring(orders.getKey().length() - 4, orders.getKey().length())));
}
}
#Override
public void onChildChanged (DataSnapshot dataSnapshot, String s){
}
#Override
public void onChildRemoved (DataSnapshot dataSnapshot){
}
#Override
public void onChildMoved (DataSnapshot dataSnapshot, String s){
}
#Override
public void onCancelled (DatabaseError databaseError){
}
};
myRef.addChildEventListener(childEventListener);
orders.clear();
allOrders.clear();
allOrders.addAllOrders(saveOrders.values());
List<Order> selectedOrders = allOrders.getItemsOrderedByStatus(page);
for (int i = 0; i < selectedOrders.size(); i++) {
orders.add(new CommandItemForSeller(selectedOrders.get(i)));
}
CommandItemForSellerAdapter aa = new CommandItemForSellerAdapter(getActivity(), R.layout.pending_listview_item, orders);
ListView lv = (ListView) view.findViewById(R.id.commandListView);
lv.setAdapter(aa);
return view;
}
As you can see each fragment calls content from Firebase Database so it remains asynchronous.
My problem is :
When I try to load "synchronous" data (that I previsouly write in my code), list content is set by onCreate method at the first creation so each fragment for each tab get some content. However, the second tab never ever refresh for new data.
So in a case of asynchronous, onCreate method doesn't return any data (due to the load time) and new datas should be added by the onCreateView (like a refresh) but for the second tab it never happens !
After many hours of debugging, I've just found out the following :
When I switch from tab 1 to tab 2, fragment is rendered for tab 3. When I switch from tab 3 to tab 2, fragment is rendered for tab 1. Even if the getItem(position) returns the right position for the tab, tab 2 now never calls onCreateView method and remains empty.
Please.. tell me why this projet behaves like that, I don't want to give up but I really don't understand what is the issue here.
If you need more specific information, tell me I can give you anything you want.
When using FragmentPagerAdapter, ViewPager keeps adjacent pages "alive" (one to the left and one to the right) - meaning that when you have three fragments only, the second one will be never destroyed. You have to find a different way if you want to refresh data when fragments come into view - one idea is to add listener for page changes, using ViewPager.addOnPageChangeListener method - https://developer.android.com/reference/android/support/v4/view/ViewPager.html#addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener)
Then, you can trigger refresh from within onPageSelected callback.
In your case, I think the simplest solution is to make SectionsPagerAdapter implement ViewPager.OnPageChangeListener, store reference to each created Fragment (preferably WeakReference), and then call some refresh method on an appropriate SellerFragment instance.
It will be like 3 hours I try to update data from a activity to a fragment. I check all pages but it didn't work...
I have to made a BookManager. I have create some class to do it.
I have a main activity with two fragment, a first empty for the moment and a second with the summary of my list of book (most price of book, average...). When I want to add a book, I use a new activity and take data back after add the book (its work).
and I want to refresh the fragment after add the book.
Code of my main activity :
public class MainActivity extends AppCompatActivity {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {#link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {#link android.support.v4.app.FragmentStatePagerAdapter}.
*/
private SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link ViewPager} that will host the section contents.
*/
private ViewPager mViewPager;
private SimpleBookManager bookManager;
public final static int ADD_BOOK_REQUEST = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bookManager = new SimpleBookManager();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.button_add_menu) {
Intent intent = new Intent(MainActivity.this, AddBookActivity.class);
startActivityForResult(intent, ADD_BOOK_REQUEST);
return true;
}
return super.onOptionsItemSelected(item);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ADD_BOOK_REQUEST) {
// On vérifie aussi que l'opération s'est bien déroulée
if (resultCode == RESULT_OK) {
// On affiche le bouton qui a été choisi
String[] valueBook = data.getStringArrayExtra("VALUE");
Book book = this.bookManager.createBook();
book.setTitle(valueBook[0]);
book.setAuthor(valueBook[1]);
book.setCourse(valueBook[2]);
book.setIsbm(valueBook[3]);
book.setPrice((int) Integer.parseInt(valueBook[4]));
}
}
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
switch (position) {
case 0:
return PlaceholderFragment.newInstance(position + 1);
case 1:
Fragment frag = SummaryFragment.newInstance(bookManager);
return frag;
}
return PlaceholderFragment.newInstance(position + 1);
}
#Override
public int getCount() {
// Show 2 total pages.
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "COLLECTION";
case 1:
return "SUMMARY";
}
return null;
}
}
In the method onActivityResult, I add the book and I want here to refresh or eventuelly when I'm back to the summary fragment. I tried with a FragmentTransaction, but the commit didn't work, because I don't have a tag to get my fragment (I don't know how it works to get one)... And the we don't have instance of the fragment because we use a static method to create it.
Code of my fragment :
public class SummaryFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String BOOKMANAGER = "book";
// TODO: Rename and change types of parameters
private String[] info;
private OnFragmentInteractionListener mListener;
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #return A new instance of fragment SummaryFragment.
*/
// TODO: Rename and change types and number of parameters
public static SummaryFragment newInstance(SimpleBookManager bookManager) {
String[] info = new String[5];
info[0] = String.valueOf(bookManager.count());
info[1] = String.valueOf(bookManager.getTotalCost());
info[2] = String.valueOf(bookManager.getMaxPrice());
info[3] = String.valueOf(bookManager.getMinPrice());
info[4] = String.valueOf(bookManager.getMeanPrice());
SummaryFragment fragment = new SummaryFragment();
Bundle args = new Bundle();
args.putStringArray(BOOKMANAGER, info);
fragment.setArguments(args);
return fragment;
}
public SummaryFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
this.info = getArguments().getStringArray(BOOKMANAGER);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView;
TextView textView;
rootView = inflater.inflate(R.layout.fragment_summary, container, false);
textView = (TextView) rootView.findViewById(R.id.nbbook_value);
textView.setText(this.info[0]);
textView = (TextView) rootView.findViewById(R.id.totalcost_value);
textView.setText(this.info[1] + " SEK");
textView = (TextView) rootView.findViewById(R.id.mostprice_value);
textView.setText(this.info[2] + " SEK");
textView = (TextView) rootView.findViewById(R.id.leastprice_value);
textView.setText(this.info[3] + " SEK");
textView = (TextView) rootView.findViewById(R.id.averageprice_value);
textView.setText(this.info[4] + " SEK");
return rootView;
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity a;
if (context instanceof Activity){
a=(Activity) context;
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p/>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(Uri uri);
}
}
You can get the Fragment instance by calling FragmentPagerAdapter's getItem method.
SummaryFragment frag = (SummaryFragment) mSectionsPagerAdapter.getItem(1);
And then call a method in the Fragment instance to refresh display, make sure to check null before calling method on the Fragment.
if (frag != null) {
frag.refreshBook(book);
}
I am working on Swipe Views with Tabs. The code provided in the "EffectiveNavigation" project at the Creating Swipe Views with Tabs page provides a solid starting ground. Experimenting further I added an OnClickListener to the given TextView and added a setCurrentItem to the onClick method. This behaves as expected and the ViewPager jumps to the requested page.
/**
* A dummy fragment representing a section of the app, but that simply displays dummy text.
*/
public static class DemoObjectFragment extends Fragment {
public static final String ARG_OBJECT = "object";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false);
Bundle args = getArguments();
((TextView) rootView.findViewById(android.R.id.text1)).setText(
Integer.toString(args.getInt(ARG_OBJECT)));
((TextView) rootView.findViewById(android.R.id.text1)).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
/*
*setCurrentPagerItem(5); -> omitted here to reduce complexity
*/
mViewPager.setCurrentItem(5);
}
});
return rootView;
}
}
As the project I'm working on requires the loading of static webpages instead of text. I replaced the TextView with a WebView to load a different webpage at every swipe. This works perfectly well. Click events from the HTML side are handled by a JavascriptInterface I have implemented.
It is here that I'm facing a problem. The setCurrentPagerItem method works perfectly well when called outside of the JavascriptInterface. When called from within the JavascriptInterface the WebView shows a blank screen and stays so until a swipe to the right or left is made. A swipe to the right displays the next page to the one requested and a swipe to the left displays the requested page. LogCat shows no errors and this behaviour is consistent across a 4.3 based emulator and a Nexus 7 running 4.4.4. I shall provide the entire code below.
public class CollectionDemoActivity extends FragmentActivity {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide fragments representing
* each object in a collection. We use a {#link android.support.v4.app.FragmentStatePagerAdapter}
* derivative, which will destroy and re-create fragments as needed, saving and restoring their
* state in the process. This is important to conserve memory and is a best practice when
* allowing navigation between objects in a potentially large collection.
*/
DemoCollectionPagerAdapter mDemoCollectionPagerAdapter;
/**
* The {#link android.support.v4.view.ViewPager} that will display the object collection.
*/
ViewPager mViewPager;
private static Context context;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collection_demo);
context = this;
// Create an adapter that when requested, will return a fragment representing an object in
// the collection.
//
// ViewPager and its adapters use support library fragments, so we must use
// getSupportFragmentManager.
mDemoCollectionPagerAdapter = new DemoCollectionPagerAdapter(getSupportFragmentManager());
// Set up action bar.
final ActionBar actionBar = getActionBar();
// Specify that the Home button should show an "Up" caret, indicating that touching the
// button will take the user one step up in the application's hierarchy.
actionBar.setDisplayHomeAsUpEnabled(true);
final OnPageChangeListener mPageChangeListener = new OnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
#Override
public void onPageSelected(int pos) {
final Toast pageNo;
pageNo = Toast.makeText(context,"PAGE "+(Integer.toString(pos+1))+"/100",Toast.LENGTH_SHORT);
pageNo.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
pageNo.cancel();
}
}, 100);
}
};
// Set up the ViewPager, attaching the adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mDemoCollectionPagerAdapter);
mViewPager.setOnPageChangeListener(mPageChangeListener);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// This is called when the Home (Up) button is pressed in the action bar.
// Create a simple intent that starts the hierarchical parent activity and
// use NavUtils in the Support Package to ensure proper handling of Up.
Intent upIntent = new Intent(this, MainActivity.class);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is not part of the application's task, so create a new task
// with a synthesized back stack.
TaskStackBuilder.from(this)
// If there are ancestor activities, they should be added here.
.addNextIntent(upIntent)
.startActivities();
finish();
} else {
// This activity is part of the application's task, so simply
// navigate up to the hierarchical parent activity.
NavUtils.navigateUpTo(this, upIntent);
}
return true;
}
return super.onOptionsItemSelected(item);
}
private void setCurrentPagerItem(int item) {
mViewPager.setCurrentItem(item);
}
/**
* A {#link android.support.v4.app.FragmentStatePagerAdapter} that returns a fragment
* representing an object in the collection.
*/
public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
public DemoCollectionPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new DemoObjectFragment();
Bundle args = new Bundle();
args.putInt(DemoObjectFragment.ARG_OBJECT, i + 1); // Our object is just an integer :-P
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
// For this contrived example, we have a 100-object collection.
return 100;
}
#Override
public CharSequence getPageTitle(int position) {
return "OBJECT " + (position + 1);
}
}
/**
* A dummy fragment representing a section of the app, but that simply displays dummy text.
*/
public static class DemoObjectFragment extends Fragment {
public static final String ARG_OBJECT = "object";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false);
Bundle args = getArguments();
final WebView webView = (WebView) rootView.findViewById(R.id.webView);
switch(args.getInt(ARG_OBJECT)) {
case 1 :
webView.loadUrl("file:///android_asset/html/index.html");
break;
default :
webView.loadUrl("file:///android_asset/html/page_"+(Integer.toString(args.getInt(ARG_OBJECT)-1))+".html");
break;
}
WebSettings ws = webView.getSettings();
ws.setJavaScriptEnabled(true);
webView.addJavascriptInterface(new Object()
{
#JavascriptInterface
public void toPage(String pageNo) {
((CollectionDemoActivity) getActivity()).setCurrentPagerItem(4);
}
}, "external");
return rootView;
}
}
}
I could be wrong but it sounds like you are not updating on the UIThread.
You could try something like this.
getActivity().runOnUiThread(new Runnable(){
#Override
public void run() {
((CollectionDemoActivity) getActivity()).setCurrentPagerItem(4);
}
});
I've read some question about this kind of problem but i can't solve it.
I've had this:
public class Classwork extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_homework);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a DummySectionFragment (defined as a static inner class
// below) with the page number as its lone argument.
Fragment fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
args.putInt("posizione", position);
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
//code for number of pages
}
#Override
public CharSequence getPageTitle(int position) {
//code for return the title
}
}
public class DummySectionFragment extends Fragment {
public static final String ARG_SECTION_NUMBER = "section_number";
public DummySectionFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//query to populate ListView
//ListView with CustomAdapter
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> myAdapter, View myView, int myItemInt, long mylng) {
//Start new activity DialogDeleteAddGradeCopy
}
});
myDbHelper.close();
return rootView;
}
}
}
I've created it with the Eclipse wizard.
A DummyFragment contains a ListView, that is populated (correctly) with a SQLite query.
With a click on an item of the ListView a "dialog" (actually is an activity with a dialog style) is displayed.
Dialog:
public class DialogDeleteAddGradeCopy extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//retrive data form Intent for the update in the db
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
//update the db
finish();
}
}
});
}
This dialog allows the user to update (with db query) one of the values of the clicked item.
All is working fine, except the update of the Fragment. The update query is ok (db is up-to-date when the dialog is closed), but to update the Fragment i need to swipe right or left two times and turn back (in this way the fragment is re-created so all works fine).
How can i force updating/re-creating the Fragment?
Thanks in advance.
Sorry for my bad English...
Normally when using multiple instances of Fragment (which I'm guessing you are based on the swipe left/swipe right comment) you don't want to update the content using an Intent as you would with an activity but using an Interface.
If the data is coming from another Fragment hosted by the same activity, say in a ViewPager or tab set up, you would set up an interface between the information sending fragment and the host activity and then call a public method to update the reciving fragment from within the host activity. The basics of using an interface to update fragments can be found here. I don't know the specifics of your whole project but I'd look into this.
As for what's posted:
You're code is not behaving properly because getItem is not proper in this context. When you are updating information by passing an intent and need to retrieve a new Bundle of information you should use
#Override
protected void onNewIntent(Intent intent) {
// TODO Auto-generated method stub
super.onNewIntent(intent);
}
You may have to set an activity flag to get this to work (for example FLAG_ACTIVITY_SINGLE_TOP) but basicly the idea is onNewIntent will be updated when a new intent is passed to your activity. Using getItem as you have it now will only update when the fragment is initially created and the args are set.