One Fragment inside ViewPager doesn't show up - android

I have a Tabbed Activity hosting a viewpager (auto-generated with eclipse wizard). I slightly modified just the auto generated FragmentPagerAdapter in order to instantiate a specific fragment on each tab. Initially i had 4 tab, 3 fragments for the first tabs and still the auto generated placeholder in the last one, and they worked. When i replaced the last placeholder with an instance of one of my Fragment i faced the following problem: the fragment in one of the last two tabs stays blank, as follow:
_____1st TAB___|____2nd TAB_|___3rd TAB_____|____4th TAB____|
[MasterFragment][RequestMap][MasterFragment][MasterFragment]
This stays blank_____________________|
Moreover, if i make a "random" sequence of actions (change tabs, click on the buttons to perform some actions,...) the fragment that doesn't show up swaps, and becomes the last one as follow:
_____1st TAB___|____2nd TAB_|___3rd TAB_____|____4th TAB____|
[MasterFragment][RequestMap][MasterFragment][MasterFragment]
This stays blank__________________________________|
Here's the code for my FragmentPagerAdapter:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case REQUEST_TAB:
masterFragment = new MasterFragment(MasterFragment.ALL_REQUEST);
return masterFragment;
case MAP_TAB:
requestMap = new RequestMap();
return requestMap;
case OWNER_TAB:
masterFragmentOwner = new MasterFragment(MasterFragment.OWNER_REQUEST);
System.out.println("I should have created the Owner MasterFragment");
return masterFragmentOwner;
case JOINED_TAB:
masterFragmentJoined = new MasterFragment(MasterFragment.JOINED_REQUEST);
return masterFragmentJoined;
}
return null;
}
#Override
public int getCount() {
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case REQUEST_TAB:
return getString(R.string.request_tab_title).toUpperCase(l);
case MAP_TAB:
return getString(R.string.map_tab_title).toUpperCase(l);
case OWNER_TAB:
return getString(R.string.owner_tab_title).toUpperCase(l);
case JOINED_TAB:
return getString(R.string.joined_tab_title).toUpperCase(l);
default:
return "";
}
}
}
I omit the code regarding the setup of the viewPager because is the same auto generated from the eclipse wizard. While here's the code for the MasterFragment class:
package it.polimi.frontend.fragment;
import it.polimi.appengine.entity.manager.model.Request;
import it.polimi.appengine.entity.manager.model.User;
import it.polimi.frontend.activity.R;
import it.polimi.frontend.util.QueryManager;
import it.polimi.frontend.util.QueryManager.OnRequestLoadedListener;
import java.util.ArrayList;
import java.util.List;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MasterFragment extends Fragment implements OnRequestLoadedListener, RequestList.OnRequestSelectedListener, RequestDetail.OnUserClickedListener{
private boolean twoPane;
private static View view;
public final static int ALL_REQUEST=0;
public final static int OWNER_REQUEST=1;
public final static int JOINED_REQUEST=2;
private int mode;
public MasterFragment(){
this.mode=0;
}
public MasterFragment(int mode){
this.mode=mode;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
QueryManager.getInstance().addListener(this);
if (view != null) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null)
parent.removeView(view);
}
try {
view = inflater.inflate(R.layout.fragment_master,container, false);
List<Request> requests = new ArrayList<Request>();
RequestList requestListFragment = null;
switch (mode) {
case OWNER_REQUEST:
requests = QueryManager.getInstance().getCurrentUser().getRequests();
System.out.println("Dovrei aver recuperato le richieste dell'owner");
break;
case JOINED_REQUEST:
String mail=null;
if (QueryManager.getInstance().getCurrentUser()!=null){
User current =QueryManager.getInstance().getCurrentUser();
mail = current.getPwAccount() != null ?
current.getPwAccount()
: current.getGmailAccount() != null ?
current.getGmailAccount()
: current.getFbAccount() != null ?
current.getFbAccount()
: null;
requests = QueryManager.getInstance().getUserPartecipation(mail);
}
else
requests = new ArrayList<Request>();
break;
default: //ALL_REQUEST case and all the other possibilities
requests = QueryManager.getInstance().getRequests();
break;
}
requestListFragment = new RequestList(requests, mode);
if (view.findViewById(R.id.detail_container) != null) {//TABLET CASE
twoPane = true;
getChildFragmentManager().beginTransaction()
.replace(R.id.request_list_container,requestListFragment,RequestList.ID)
.commit();
} else { //PHONE CASE:
getChildFragmentManager().beginTransaction()
.replace(R.id.container,requestListFragment,RequestList.ID)
.commit();
}
} catch (InflateException e) {
// is already there, just return view as it is
e.printStackTrace();
}
return view;
}
#Override
public void onRequestSelected(int position, Request request) {
if (twoPane) {
DetailContainerFragment detailContFrag = new DetailContainerFragment(request,mode);
getChildFragmentManager().beginTransaction()
.replace(R.id.detail_container, detailContFrag, DetailContainerFragment.ID).commit();
} else {
switch (mode) {//This empty switch if for future changes
case OWNER_REQUEST:
break;
case JOINED_REQUEST:
break;
default://ALL_REQUEST
break;
}
RequestDetail fragment = new RequestDetail(request,mode);
Fragment reqList=getChildFragmentManager().findFragmentByTag(RequestList.ID);
getChildFragmentManager().beginTransaction()
.hide(reqList)
.addToBackStack(RequestDetail.ID)
.add(R.id.container,fragment,RequestDetail.ID)
.commit();
}
}
#Override
public void onUserClicked(User user,String requestId) {
if (!twoPane) {
FeedbackDetail fragment = new FeedbackDetail(user,this.mode,requestId);
Fragment reqDetail=getChildFragmentManager().findFragmentByTag(RequestDetail.ID);
getChildFragmentManager().beginTransaction()
.hide(reqDetail)
.addToBackStack(FeedbackDetail.ID)
.add(R.id.container,fragment,FeedbackDetail.ID)
.commit();
} else {
/*DetailContainerFragment should take care of it*/
}
}
#Override
public void onRequestLoaded(List<Request> requests) {
System.out.println("Ho caricato: "+requests.size());
RequestList requestListFragment = (RequestList)getChildFragmentManager().findFragmentByTag(RequestList.ID);
switch (mode) {//Also this switch is for future changes, but the requests list is anyway fetched and assigned to RequestList above
case OWNER_REQUEST:
//TODO
break;
case JOINED_REQUEST:
//TODO
break;
default: //ALL_REQUEST
if (requestListFragment!=null)
requestListFragment.setRequestAdapter(requests);
break;
}
}
}
If i'm missing something important please, let me know.
Thank you all in advance
EDIT:
I forgot to say that the System.out that i placed show up in the console, so the "blank" fragment should have been created, and it should have passed through his onCreateView().

I found the bug. The problem was
private static View view;
because of the static properties. Removing "static" it worked again. Actually i can't explain even now how it could have worked for a while on the first and last tabs and not in the third, because the static attributes should be shared among all instances of the class. Because of the internals of the ViewPager the problem seemed to show up only on adjacent identical MasterFragment, if they where interleaved with any other fragment it worked.

Related

First fragment in view pager not showing because of rest api delay

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.

Fragment's reference to fragmentPagerAdapter is null

So I've got 5 (6?) fragments in a ViewPager, they all interact with each other using callbacks through the Activity to get information from one another. I was getting some problems because they needed to be started in a particular order, and some seemed to be starting before others were ready regardless of what I did, so I implemented a callback for all the fragments to initialize themselves only once all of them were ready:
#Override
public void notifyReady() {
boolean settingsReady = false;
boolean fileReady = false;
boolean recordTableReady = false;
boolean chartReady = false;
boolean pieChartReady = false;
if (settingsFragment != null)
settingsReady = settingsFragment.isReady();
if (fileFragment != null)
fileReady = fileFragment.isReady();
if (recordTableFragment != null)
recordTableReady = recordTableFragment.isReady();
if (chartFragment != null)
chartReady = chartFragment.isReady();
if (pieChartFragment != null)
pieChartReady = pieChartFragment.isReady();
boolean allReady = settingsReady & fileReady & recordTableReady & chartReady & pieChartReady;
if (allReady) {
settingsFragment.initialize();
fileFragment.initialize();
recordTableFragment.initialize();
chartFragment.initialize();
pieChartFragment.initialize();
}
I put the interface implementation in my FragmentPagerAdapter, since that's where I read I should be initializing fragments anyway:
#Override
public Fragment getItem(int pos) {
switch(pos) {
case 0:
return new SettingsFragment();
case 1:
return new FileFragment();
case 2:
return new OracleFragment(); // No implementation yet
case 3:
return new RecordTableFragment();
case 4:
return new ChartFragment();
case 5:
return new PieChartFragment();
default:
return null; // BAD
}
}
Alongside the initialization of the callback interface to the FragmentPagerAdapter:
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
switch (position) {
case 0:
settingsFragment = (SettingsFragment) createdFragment;
settingsFragment.setNotificationListener(this);
break;
case 1:
fileFragment = (FileFragment) createdFragment;
fileFragment.setNotificationListener(this);
break;
case 2:
oracleFragment = (OracleFragment) createdFragment;
//oracleFragment.setNotificationListener(this); (This fragment has no implementation yet)
break;
case 3:
recordTableFragment = (RecordTableFragment) createdFragment;
recordTableFragment.setNotificationListener(this);
break;
case 4:
chartFragment = (ChartFragment) createdFragment;
chartFragment.setNotificationListener(this);
break;
case 5:
pieChartFragment = (PieChartFragment) createdFragment;
pieChartFragment.setNotificationListener(this);
break;
}
return createdFragment;
}
And I had my fragments call:
public void setNotificationListener(FragmentNotifier fragmentNotifier) {
this.fragmentNotifier = fragmentNotifier;
}
for the FragmentPagerAdapter to set itself as the interface object, and
#Override
public void onStart() {
super.onStart();
ready = true;
fragmentNotifier.notifyReady();
}
For initialization. This works incredibly well at first, but fragmentNotifier becomes null after coming back to the activity after a while, and the fragments' onStart() is called when the activity starts up again, producing a NullPointerException. Why is it becoming null? Other fields like the Fragment's reference to the Activity and the interface provided by it aren't null at this point in the execution, I've checked with a debugger, but fragmentNotifier IS! Why?!
I'm not going to debug your code and find out where is it going wrong, but I'll give you a better solution.
I've been through this in the past and I've realized that the best way to find and store callbacks defined in your Activity is inside your Fragments. So instead of using setters, use this in your Fragments:
#Override
public void onAttach(Activity activity){
if (activity instanceof FragmentNotifier)
mFragmentNotifier = (FragmentNotifier) activity;
else throw new IllegalAccessException("Activity must implement the FragmentNotifier interface");
}
This will ensure that you'll have a reference to your Activity. And add this to avoid leaking the reference:
#Override
public void onDetach(){
mFragmentNotifier = null;
}

Android Getting Fragment ID for use in Switch statement

So, I am writing a navigation part of my app and have dynamically created fragments inside an activity.
I want, when a user hits either the next or previous arrow, for the app to find out what fragment is in the activity and replace it with either the next or previous fragment. In order to do that, I created a switch statement to check on what fragment is being displayed.
I thought that I would be able to do this by using
getSupportFragmentManager().findFragmentById(R.id.activity_public_internet)
but that doesn't seem to work in the switch statement.
So I then tried
getFragmentManager().findFragmentById(R.id.public_internet_intro_fragment).getId()
and that doesn't work either.
Here is the full code. Any help will be fully appreciated. And feel free to tell me if there is a better way to do it.
public void goPrev(View view) {
switch(getSupportFragmentManager().findFragmentById(R.id.activity_public_internet).getId()) {
case R.id.public_internet_intro_fragment:
Intent intent = new Intent(this, LearnActivity.class);
startActivity(intent);
break;
case R.id.public_internet_topic_fragment:
Fragment introFragment = new PublicInternetIntroFragment();
FragmentTransaction exampleTransaction = getFragmentManager().beginTransaction();
exampleTransaction.replace(R.id.activity_public_internet, introFragment);
exampleTransaction.addToBackStack(null);
exampleTransaction.commit();
break;
case R.id.public_internet_example_fragment:
Fragment topicFragment = new PublicInternetTopicFragment();
FragmentTransaction topicTransaction = getFragmentManager().beginTransaction();
topicTransaction.replace(R.id.activity_public_internet, topicFragment);
topicTransaction.addToBackStack(null);
topicTransaction.commit();
break;
}
}
public void goNext(View view) {
switch(getFragmentManager().findFragmentById(R.id.public_internet_intro_fragment).getId()) {
case R.id.public_internet_intro_fragment:
Fragment topicFragment = new PublicInternetTopicFragment();
FragmentTransaction topicTransaction = getFragmentManager().beginTransaction();
topicTransaction.replace(R.id.activity_public_internet, topicFragment);
topicTransaction.addToBackStack(null);
topicTransaction.commit();
break;
case R.id.public_internet_topic_fragment:
Fragment exampleFragment = new PublicInternetExampleFragment();
FragmentTransaction exampleTransaction = getFragmentManager().beginTransaction();
exampleTransaction.replace(R.id.activity_public_internet, exampleFragment);
exampleTransaction.addToBackStack(null);
exampleTransaction.commit();
break;
case R.id.public_internet_example_fragment:
Intent intent = new Intent(this, LearnActivity.class);
startActivity(intent);
break;
}
}
When checking the error log, a Null Pointer Exception is raised after attempting to invoke int android.app.Fragment.getId()
EDIT: Added PublicInternetActivity.java
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
public class PublicInternetActivity extends ActionBarActivity{
private static final String tag_public_internet_intro_fragment = "public_internet_intro_fragment";
private static final String tag_public_internet_topic_fragment = "public_internet_topic_fragment";
private static final String tag_public_internet_example_fragment = "public_internet_example_fragment";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_public_internet);
introFrag();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu items for use in the action bar
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_learn, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_home) {
goHome();
return true;
}
else {
return super.onOptionsItemSelected(item);
}
}
public void introFrag() {
Fragment introFragment = new PublicInternetIntroFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.activity_public_internet, introFragment);
transaction.addToBackStack(null);
transaction.commit();
}
public void goHome() {
Intent homeIntent = NavUtils.getParentActivityIntent(this);
NavUtils.navigateUpTo(this, homeIntent);
}
public void pushNewFragment( Fragment newFrag, String tag) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.activity_public_internet, newFrag, tag);
transaction.addToBackStack(tag);
transaction.commit();
}
public String getActiveFragmentTag() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
return tag;
}
public void goPrev(View view) {
switch(getActiveFragmentTag()) {
case tag_public_internet_intro_fragment:
Intent intent = new Intent(this, LearnActivity.class);
startActivity(intent);
break;
case tag_public_internet_topic_fragment:
Fragment introFragment = new PublicInternetIntroFragment();
pushNewFragment(introFragment, tag_public_internet_intro_fragment);
break;
case tag_public_internet_example_fragment:
Fragment topicFragment = new PublicInternetTopicFragment();
pushNewFragment(topicFragment, tag_public_internet_topic_fragment);
break;
}
}
public void goNext(View view) {
switch(getActiveFragmentTag()) {
case tag_public_internet_intro_fragment:
Fragment topicFragment = new PublicInternetTopicFragment();
pushNewFragment(topicFragment, tag_public_internet_topic_fragment);
break;
case tag_public_internet_topic_fragment:
Fragment exampleFragment = new PublicInternetExampleFragment();
pushNewFragment(exampleFragment, tag_public_internet_example_fragment);
break;
case tag_public_internet_example_fragment:
Intent intent = new Intent(this, LearnActivity.class);
startActivity(intent);
break;
}
}
}
Try to do it this way:
public void pushNewFragment( Fragment newFrag, String tag) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.activity_public_internet, newFrag, tag);
transaction.addToBackStack(tag);
transaction.commit();
}
public String getActiveFragmentTag() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
return tag;
}
public void goPrev(View view) {
switch(getActiveFragmentTag()) {
case tag_public_internet_intro_fragment:
Intent intent = new Intent(this, LearnActivity.class);
startActivity(intent);
break;
case tag_public_internet_topic_fragment:
Fragment introFragment = new PublicInternetIntroFragment();
pushNewFragment(introFragment, tag_public_internet_intro_fragment);
break;
case tag_public_internet_example_fragment:
Fragment topicFragment = new PublicInternetTopicFragment();
pushNewFragment(introFragment, tag_public_internet_topic_fragment);
break;
}
}
EDIT
from the documentation:
Get fragments that exist in the activity, with findFragmentById() (for
fragments that provide a UI in the activity layout) or
findFragmentByTag() (for fragments that do or don't provide a UI).
Normally we use ids for static fragments (embedded in the activity layout and that you don't need to change in runtime). For dynamic Fragments which is your case it's better to use tags.
Best of luck
This looks very complicated, I would suggest something more simple.
Use Fragment.instantiate to create a Fragment and replace the current one. If the order of your Fragments is fixed, keep an Array with the name of the class of each Fragment in it (in the good order).
Then, keep a pointer to know where you are in your Array, and each time you go to the next/previous Fragment, increment your pointer accordingly.
This way,you don't have to implement case by case handling, which seems to be very error-prone. You should consider the cases where
pointer == 0 and pointer == length-1.
This way, you can have any number of Fragments the one after the other. Note that however, it only works for a determined order. If you want something more complicated, you should extend the Fragment class and add logic into your subclass.

Why my async task is too slow when it uses in pager adapter

I am using the following code
ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
SectionsPagerAdapter mSectionsPagerAdapter= new SectionsPagerAdapter(
getSupportFragmentManager());
mViewPager.setAdapter(mSectionsPagerAdapter);
My SectionsPagerAdapter class is
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(android.support.v4.app.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;
}
#Override
public int getCount() {
int numpages = 0;
numpages=5;
return numpages;
}
#Override
public CharSequence getPageTitle(int position) {
// Locale l = Locale.getDefault();
String chnl_name = "";
switch (language) {
case 1:
chnl_name = Constants.telugu_channels[position];
break;
case 2:
chnl_name = Constants.tamil_channels[position];
break;
case 3:
chnl_name = Constants.english_channels[position];
break;
case 4:
chnl_name = Constants.hindi_channels[position];
break;
}
return chnl_name;
}
}
and fragment class is
// enter code here
public static class DummySectionFragment extends Fragment {
GridView gridplaylst;
ProgressBar progress;
Play_list playlist;
public static final String ARG_SECTION_NUMBER = "section_number";
public DummySectionFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_grid, container,
false);
// dummyTextView.setText(Integer.toString(getArguments().getInt(
// ARG_SECTION_NUMBER)));
gridplaylst = (GridView) rootView.findViewById(R.id.gridplaylst);
progress = (ProgressBar) rootView.findViewById(R.id.progress);
if (playlist != null)
playlist.cancel(true);
if (language != 0) {
int a = getArguments().getInt(ARG_SECTION_NUMBER);
switch (language) {
case 1:
playlist = new Play_list(
Constants.telugu_playlists[getArguments().getInt(
ARG_SECTION_NUMBER) - 1]);
playlist.execute();
break;
case 2:
new Play_list(Constants.tamil_playlists[getArguments()
.getInt(ARG_SECTION_NUMBER) - 1]).execute();
break;
case 3:
new Play_list(Constants.english_playlists[getArguments()
.getInt(ARG_SECTION_NUMBER) - 1]).execute();
break;
case 4:
new Play_list(Constants.hindi_playlists[getArguments()
.getInt(ARG_SECTION_NUMBER) - 1]).execute();
break;
}
}
}
and playlist is a async task like
public class Play_list extends AsyncTask<Object, Object, Object> {
protected Object doInBackground(Object... arg0) {
}
}
Here all the functionality is working fine, but my problem is the data downloading by the async tasks is too slow.
I think the problem is creating a new task every time.
Please suggest me how can i overcome this problem.
#Prakash : There is a catch while you use ViewPager. ViewPager will start preparing view for next 2 fragments and as when your activity with ViewPager is called by System , ViewPager will creating next 2 views that you have defined in FragmentPagerAdapter.
You can use setOffscreenPageLimit(pagestoCache) to minimize this functionality.
Prakash , now you have to call asyn task only when fragment is visible to user and cancel if user moves out of current fragment. Why i am asking because if you are not cancelling the Aync task there will be many thread will be running in application space. You can also chaeck if you can use ThreadPoolExecutor in your code.
Are you aware of the fact, that Android does not run AsyncTasks in parallel? If you start many AsyncTasks in a row, they are queued and executed one after the other?
You can overcome this, by starting them with executeOnExecutor(...) instead of regular execute().

Fragments ViewPages and Animations

I am working on a ViewPager whose elements can be flipped, as in the CardFlipActivity of android animations demo.
I am having a problem with the beginTransaction() method.
The only quick fix options I get ask me to change the type of frag from android.support.v4.app.Fragment to android.app.Fragment
When I do so, another error comes up and I have to change the type back to support.v4.app.Fragment.
How do I proceed ?
I am using all support.v4 library imports in my code for fragments and viewPagers.
Code:
package com.example.flag_o;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class QuizGame extends FragmentActivity
implements FragmentManager.OnBackStackChangedListener {
static DataClass dc;
static int numQues;
static int quizLev;
static int FLIP_FOR_MAP = 1;
static int FLIP_FOR_INFO = 2;
static int frag_status=0;
static int questions[] = null;
MenuItem map;
MenuItem ques;
private Handler mHandler = new Handler();
private boolean mShowingBack = false;
QuizPagerAdapter quizAdapter;
ViewPager quizPager;
static String RETRIEVE_POS = "position";
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.quiz_pager);
dc = new DataClass();
numQues = getIntent().getExtras().getInt("numQ");
quizLev = getIntent().getExtras().getInt("level");
java.util.List<Integer> randNums = new java.util.ArrayList<Integer>(dc.flags.length);
for (int i = 0; i <= dc.flags.length; i++)
{
randNums.add(new Integer(i));
}
java.util.Collections.shuffle(randNums);
for(int i=0;i<numQues;i++)
questions[i] = randNums.get(i);
quizAdapter = new QuizPagerAdapter(getSupportFragmentManager(),questions);
quizPager = (ViewPager) findViewById(R.id.quizPager);
quizPager.setAdapter(quizAdapter);
if (savedInstanceState == null) {
// If there is no saved instance state, add a fragment representing the
// front of the card to this activity. If there is saved instance state,
// this fragment will have already been added to the activity.
getFragmentManager()
.beginTransaction()
.add(R.id.container, new FlagFragment())
.commit();
} else {
mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
// Add either a "photo" or "finish" button to the action bar, depending on which page
// is currently selected.
map = menu.add(Menu.NONE, R.id.action_map, Menu.NONE,
mShowingBack
? R.string.action_photo
: R.string.action_map);
map.setIcon(mShowingBack
? R.drawable.ic_action_photo
: android.R.drawable.ic_menu_mapmode);
map.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
ques = menu.add(Menu.NONE, R.id.action_ques, Menu.NONE,
mShowingBack
? R.string.action_photo
: R.string.action_ques);
ques.setIcon(mShowingBack
? R.drawable.ic_action_photo
: android.R.drawable.ic_menu_help);
ques.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// Navigate "up" the demo structure to the launchpad activity.
// See http://developer.android.com/design/patterns/navigation.html for more.
//NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class));
frag_status=0;
return true;
case R.id.action_map:
if(mShowingBack)
frag_status=0;
else
frag_status=1;
flipCard(FLIP_FOR_MAP);
return true;
case R.id.action_info:
if(mShowingBack)
frag_status=0;
else
frag_status=2;
flipCard(FLIP_FOR_INFO);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
switch(frag_status)
{
case 1:
map.setVisible(true);
ques.setVisible(false);
break;
case 2:
map.setVisible(false);
ques.setVisible(true);
break;
}
return super.onPrepareOptionsMenu(menu);
}
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
map.setVisible(true);
ques.setVisible(true);
}
private void flipCard(int flipType) {
if (mShowingBack) {
getFragmentManager().popBackStack();
return;
}
mShowingBack = true;
Fragment mapFrag;
InfoFragment infoFrag;
Bundle args = new Bundle();
args.putInt(RETRIEVE_POS, questions[quizPager.getCurrentItem()]);
switch(flipType) {
case 1 :
mapFrag = new Fragment();
mapFrag.setArguments(args);
getFragmentManager()
.beginTransaction()
.setCustomAnimations(
R.animator.card_flip_right_in, R.animator.card_flip_right_out,
R.animator.card_flip_left_in, R.animator.card_flip_left_out)
.replace(R.id.container, mapFrag)
.addToBackStack(null)
.commit();
break;
case 2 :
frag = new InfoFragment();
frag.setArguments(args);
getFragmentManager()
.beginTransaction()
.setCustomAnimations(
R.animator.card_flip_right_in, R.animator.card_flip_right_out,
R.animator.card_flip_left_in, R.animator.card_flip_left_out)
.replace(R.id.container, frag)
.addToBackStack(null)
.commit();
break;
}
mHandler.post(new Runnable() {
#Override
public void run() {
invalidateOptionsMenu();
}
});
}
#Override
public void onBackStackChanged() {
mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
// When the back stack changes, invalidate the options menu (action bar).
invalidateOptionsMenu();
}
public static class FlagFragment extends Fragment {
int position;
Bundle args;
public FlagFragment() {
position = args.getInt(RETRIEVE_POS);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.pic_fragment, container, false);
ImageView flipImg = (ImageView)rootView.findViewById(R.id.flipFlagImage);
try {
flipImg.setImageResource(dc.flags[position]);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return rootView;
}
}
public static class MapFragment extends Fragment {
int position;
Bundle args;
public MapFragment() {
position = args.getInt(RETRIEVE_POS);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.map_fragment, container, false);
ImageView flipImg = (ImageView)rootView.findViewById(R.id.flipMapImage);
try {
flipImg.setImageResource(dc.ortho[position]);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return rootView;
}
}
public static class InfoFragment extends Fragment {
public InfoFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.info_fragment, container, false);
}
}
public class QuizPagerAdapter extends FragmentStatePagerAdapter {
int fragDet[];
Fragment frag;
Bundle args;
public QuizPagerAdapter(FragmentManager fragmentManager, int [] q) {
super(fragmentManager);
fragDet = q;
}
#Override
public Fragment getItem(int i) {
frag = new FlagFragment();
args.putInt(RETRIEVE_POS, fragDet[i]);
frag.setArguments(args);
return frag;
}
#Override
public int getCount() {
return numQues;
}
#Override
public CharSequence getPageTitle(int position) {
String title = null;
switch(position)
{
case 0 : title = "Paired Devices";
break;
case 1 : title = "Chats";
break;
case 2 : title = "New Devices";
break;
}
return (CharSequence)title;
}
}
}
The support library's Fragment class doesn't support animations in transactions.
Its a trade off between animating or building the app for devices with API higher than 11.
http://developer.android.com/reference/android/app/FragmentTransaction.html
Another way about this is to use the NineOldAndroids library from Jake Wharton and code the transaction yourself.

Categories

Resources