Blank fragment after activity recreated - android

I have an Activity with a ViewPager and TabLayout. I enabled "destroy activities" from Settings in my phone to simulate low memory.
I use getSupportFragmentManager because it's an Activity.
The first time I open the activity, I see my fragments. When I minimize my app and reopen it, I see the tabs but not the content. Inside fragments I call setRetainInstance(true).
Of course if I call super.onCreate(null); instead of super.onCreate(savedInstanceState); it's working fine because the activity is being recreated at all.
If I use FragmentStatePagerAdapter the content is ok but the menu displays duplicated items.
So please, don't suggest FragmentStatePagerAdapter (want all fragments visible), getChildFragmentManager (ViewPager is in an Activity), setRetainInstance (already using it).
public class SystemActivity extends LoadingActivity {
#BindView(R.id.pager)
ViewPager viewPager;
#BindView(R.id.tab_layout)
TabLayout tabLayout;
#BindView(R.id.toolbar)
Toolbar toolbar;
int[] ICONS = {
R.drawable.ic_tab_general,
R.drawable.ic_tab_details
};
int[] TEXTS = {
R.string.general,
R.string.details,
};
private CustomPagerAdapter customPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fetch();
}
protected void fetch() {
super.fetch();
final Long systemId = getIntent().getLongExtra(Constants.Intent.SYSTEM_ID, 0);
this.realm = Realm.getDefaultInstance();
this.call = ApiClientHelper.getInstance(this).getSystem(systemId);
call.enqueue(new ApiUICallback<System>(this) {
#Override
public void onError(int code, String message) {
System system = realm.where(System.class).equalTo("id", systemId).findFirst();
if (system == null) {
fail();
} else {
init(system);
}
}
#Override
public void onSuccess(System system) {
realm.beginTransaction();
realm.copyToRealmOrUpdate(system);
realm.commitTransaction();
init(system);
}
});
}
private void init(System system) {
setContentView(R.layout.activity_system);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
customPagerAdapter = new CustomPagerAdapter(getSupportFragmentManager(), system);
viewPager.setOffscreenPageLimit(TEXTS.length);
viewPager.setAdapter(customPagerAdapter);
tabLayout.setupWithViewPager(viewPager);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
CharSequence title = customPagerAdapter.getPageTitle(0);
getSupportActionBar().setTitle(title);
for (int i = 0; i < ICONS.length; i++) {
TabLayout.Tab tab = tabLayout.getTabAt(i);
tab.setCustomView(R.layout.tab_view);
((ImageView) tab.getCustomView().findViewById(R.id.icon)).setImageResource(ICONS[i]);
}
tabLayout.setOnTabSelectedListener(
new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
CharSequence title = customPagerAdapter.getPageTitle(tab.getPosition());
getSupportActionBar().setTitle(title);
hiddenKeyboard();
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
hiddenKeyboard();
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
hiddenKeyboard();
}
}
);
}
private void hiddenKeyboard() {
View focus = getCurrentFocus();
if (focus != null) {
focus.clearFocus();
InputMethodManager keyboard = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
keyboard.hideSoftInputFromWindow(focus.getWindowToken(), 0);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
class CustomPagerAdapter extends FragmentPagerAdapter {
private System system;
public CustomPagerAdapter(FragmentManager fm, System system) {
super(fm);
this.system = system;
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch (position) {
case 0:
fragment = new SystemGeneralFragment();
break;
case 1:
fragment = new SystemDetailsFragment();
break;
default:
return null;
}
Bundle bundle = new Bundle();
bundle.putLong(Constants.Intent.SYSTEM_ID, this.system.getId());
fragment.setArguments(bundle);
return fragment;
}
#Override
public int getCount() {
return TEXTS.length;
}
#Override
public CharSequence getPageTitle(int position) {
return getResources().getString(TEXTS[position]);
}
}
}
And one of my fragments:
public class SystemGeneralFragment extends MyFragment {
#BindView(R.id.progress)
View progress;
#BindView(R.id.progress_background)
View progressBackground;
#BindView(R.id.progress_percentage)
TextView percentage;
protected Call call;
protected Realm realm;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
realm = Realm.getDefaultInstance();
}
#Override
public void onDestroyView() {
if (this.call != null) {
this.call.cancel();
}
super.onDestroyView();
}
#Override
public void onDestroy() {
realm.close();
super.onDestroy();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_system_general, container, false);
ButterKnife.bind(this, view);
System system = realm.where(System.class).equalTo("id", getArguments().getLong(Constants.Intent.SYSTEM_ID)).findFirst();
System.Status status = system.getStatus();
percentage.setText(String.format("%d%%", status.getProgress()));
setProgress(progress, status.getColor(getContext()));
setProgress(progressBackground, ContextCompat.getColor(getContext(), R.color.light_gray));
return view;
}
private void setProgress(View view, int color) {
GradientDrawable shape = new GradientDrawable();
shape.setCornerRadius(16);
shape.setColor(color);
if (Build.VERSION.SDK_INT >= 16) {
view.setBackground(shape);
} else {
view.setBackgroundDrawable(shape);
}
}
}

Related

Getting a null pointer exception when trying to pass a boolean via an interface

I am using an interface, as is standard (best?) practice to communicate between a series of fragments. The business logic requires the app to collect some information in fragment n+1 and if the "next" button is tapped then the user goes to fragment n+2. If the "back" button is tapped then the user goes to fragment n. I am also using a nice sliding animation to display the transition from one fragment to the other depending on the direction. I cannot figure out why this is not working and I am getting the null pointer error on this line:
createPlanListener.onCreatePlan(bundle);
Here is the initial fragment Mealplan.class where I trigger the transition. I have left all of the boiler plate code generated by Android Studio as is:
public class MealplanFragment extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
private FloatingActionButton createMealplan;
// bunch of variables
private Bundle bundle;
private OnCreatePlanListener createPlanListener;
public MealplanFragment() {
// Required empty public constructor
}
public static MealplanFragment newInstance(String param1, String param2) {
MealplanFragment fragment = new MealplanFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Other code that has nothing to do with the bundle or the listener
// Floating action bar
createMealplan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
bundle.putBoolean("direction", true);
createPlanListener.onCreatePlan(bundle);
}
});
return mealplanView;
}
public void onButtonPressed(Bundle bundle) {
if (createPlanListener != null) {
createPlanListener.onCreatePlan(bundle);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mealplanContext = context;
if (context instanceof OnCreatePlanListener) {
createPlanListener = (OnCreatePlanListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
createPlanListener = null;
}
public interface OnCreatePlanListener {
void onCreatePlan(Bundle bundle);
}
#Override
public void onResume() {
super.onResume();
}
And here is MainActivity.class
public class MainActivity extends AppCompatActivity implements
MealplanFragment.OnCreatePlanListener {
// Non related variables
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// MealplanFragment is the default fragment at onCreate
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().replace(R.id.frame_container, new MealplanFragment(), null).commit();
}
}
#Override
public void onCreatePlan(Bundle bundle) {
if (bundle != null) {
Boolean direction = bundle.getBoolean("direction");
ReceptionFragment fragment = new ReceptionFragment();
openFragment(bundle, fragment, direction);
}
}
private void openFragment(Bundle bundle, Fragment fragment, Boolean direction) {
fragment.setArguments(bundle);
//Starting fragment with animation
if (direction) {
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_right).replace(R.id.frame_container, fragment, null);
fragmentTransaction.commit();
} else {
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_left).replace(R.id.frame_container, fragment, null);
fragmentTransaction.commit();
}
}
}
createMealplan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
bundle.putBoolean("direction", true);
createPlanListener.onCreatePlan(bundle);
}
});
This is your click listener. bundle is defined as class variable but never initialized and hence the null pointer exception. I would suggest that you use a local variable -> create a new instance of bundle, add data and then invoke callback. Also, createPlanListener is nullable, so you should add a check for that as well.

How to make 500 Questions Quiz in android with single activity?

I am creating an android app, where I'll be asking for multiple types of questions using RadioButtons. I don't want to make multiple Activities for these questions. Can anyone please tell me how to do that with a short example, of at least two questions?
You can use multiples fragments... or call the activity itself multiple times...
I did an app like yours and i choose the first method!
This is some fragment of a project that i wrote, and the activity that manipulate it, you will have to change it according to your needs.
Activity
public class CollectActivity extends FragmentActivity {
MyPageAdapter pageAdapter;
NonSwipeableViewPager pager;
SpringIndicator springIndicator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collect);
List<Fragment> fragments = getFragments();
pager = (NonSwipeableViewPager) findViewById(R.id.view_pager);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
PagerModelManager manager = new PagerModelManager();
manager.addCommonFragment(fragments, getTitles());
ModelPagerAdapter adapter = new ModelPagerAdapter(getSupportFragmentManager(), manager);
pager.setAdapter(adapter);
springIndicator = (SpringIndicator) findViewById(R.id.indicator);
springIndicator.setViewPager(pager);
springIndicator.setOnTabClickListener(new TabClickListener() {
#Override
public boolean onTabClick(int position) {
return false;
}
});
}
private List<Fragment> getFragments() {
List<Fragment> fList = new ArrayList<Fragment>();
fList.add(CollectFragment.newInstance("Fragment 1"));
fList.add(CollectFragment.newInstance("Fragment 2"));
fList.add(CollectFragment.newInstance("Fragment 3"));
//add your fragments with a loop
return fList;
}
private List<String> getTitles() {
return Lists.newArrayList("1", "2", "3");
}
public void swipeFragment() {
pager.setCurrentItem(pager.getCurrentItem() + 1);
}
public int getFragment() {
return pager.getCurrentItem();
}
}
Fragment
public class CollectFragment extends Fragment {
private Button openButton;
private Button confirmationCloseButton;
private Button yesRenew;
private Button noRenew;
private BroadcastReceiver udpMessages;
public static final String EXTRA_MESSAGE = "EXTRA_MESSAGE";
public static final CollectFragment newInstance(String message) {
CollectFragment f = new CollectFragment();
Bundle bdl = new Bundle(1);
bdl.putString(EXTRA_MESSAGE, message);
f.setArguments(bdl);
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String message = getArguments().getString(EXTRA_MESSAGE);
View v = null;
if (message.compareTo("Fragment 1") == 0) {
v = inflater.inflate(R.layout.fragment_collect_open, container, false);
openButton = (Button) v.findViewById(R.id.open_button);
openButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i2 = new Intent();
i2.setComponent(new ComponentName("qira.com.locker", "qira.com.locker.Service.MessageService"));
i2.putExtra("Message", "CONFIRM_LOCKER_1_CLOSED");
getContext().startService(i2);
}
});
}
if (message.compareTo("Fragment 2") == 0) {
v = inflater.inflate(R.layout.fragment_collect_close, container, false);
confirmationCloseButton = (Button) v.findViewById(R.id.confirmation_close_button);
confirmationCloseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i2 = new Intent();
i2.setComponent(new ComponentName("qira.com.locker", "qira.com.locker.Service.MessageService"));
i2.putExtra("Message", "OPEN_LOCKER_1");
getContext().startService(i2);
}
});
}
if (message.compareTo("Fragment 3") == 0) {
v = inflater.inflate(R.layout.fragment_collect_renew, container, false);
yesRenew = (Button) v.findViewById(R.id.yes_button);
noRenew = (Button) v.findViewById(R.id.no_button);
yesRenew.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
((CollectActivity) getActivity()).swipeFragment();
}
});
noRenew.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(getContext(), ReserveActivity.class);
startActivity(i);
}
});
}
return v;
}
#Override
public void onResume() {
super.onResume();
udpMessages = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null && intent.getAction().equals("UDP.MESSAGES.COLLECT")) {
if (intent.getExtras().getString("Type").compareTo("OPEN_LOCKER_1-LOCKER_OPENED") == 0) {
if (((CollectActivity) getActivity()).getFragment() != 0) { // onCreateView called twice, dont know why... workaround to solve this problem
((CollectActivity) getActivity()).swipeFragment();
}
}
if (intent.getExtras().getString("Type").compareTo("CONFIRM_LOCKER_1_CLOSED-TRUE") == 0) {
if (((CollectActivity) getActivity()).getFragment() != 1) { // onCreateView called twice, dont know why... workaround to solve this problem
((CollectActivity) getActivity()).swipeFragment();
}
}
}
}
};
getContext().registerReceiver(udpMessages, new IntentFilter("UDP.MESSAGES.COLLECT"));
}
#Override
public void onPause() {
super.onPause();
getContext().unregisterReceiver(udpMessages);
}
#Override
public void onDestroyView() {
super.onDestroyView();
}
}

Main activity variable becoming null

In my main activity I have:
private DataManager mDataManager;
In the same activity I manage this variable following the lifecycle. These are the related methods:
public class MainActivity extends AppCompatActivity {
#Override
public void onStart() {
super.onStart();
mDataManager = new DataManager(this);
}
#Override
public void onResume() {
super.onResume();
mDataManager.connect(getIpAddress());
}
#Override
public void onPause() {
super.onPause();
mDataManager.disconnect();
}
#Override
public void onStop() {
super.onStop();
mDataManager = null;
}
#Override
public DataManager getDataManager() {
return mDataManager;
}
#Override
public void updateFragments() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (mLoading.isShowing())
mLoading.dismiss();
List<Fragment> allFragments = getSupportFragmentManager().getFragments();
if (allFragments == null || allFragments.isEmpty())
return;
for (Fragment fragment : allFragments)
if (fragment.isVisible())
((UpdatableFragment) fragment).updateView();
}
});
}
I have some fragments that use getDataManager() to update themselves with data, but sometimes, often when I do not use the application for a while I get a nullpointerexception because getDataManager() returned null. According to the activity lifecycle, the object should never be null or am I missing something?
EDIT: Initialization of the ViewPager
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(myToolbar);
MainAdapter adapter = new MainAdapter(getSupportFragmentManager());
ViewPager viewPager = (ViewPager) findViewById(R.id.my_pager);
viewPager.setAdapter(adapter);
}
public class MainAdapter extends FragmentPagerAdapter {
MainAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return FragmentX.newInstance();
case 1:
return FragmentY.newInstance();
case 2:
return FragmentZ.newInstance();
case 3:
return FragmentU.newInstance();
}
return null;
}
#Override
public int getCount() {
return 4;
}
}

FragmentTabHost Fragment Adapters Are Empty Upon Returning To Tab, Fragment/Views Remain

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.

Android AsyncLoader: Share objects between fragmentactivity/fragment

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.

Categories

Resources