java.lang.InstantiationException: can't instantiate class ... no empty constructor - android

I have found topics with similar questions like mine but cant find the answer I am looking for so far.
my application consists of a FragmentActivity which hosts a ViewPagerAdapter (child of FragmentPagerAdapter) with a fragment in each tab.
My ViewPagerAdapter is instantiated in the OnCreateView function of the parent activity
_adapter = new ViewPagerAdapter(getApplicationContext()
, getSupportFragmentManager()
, numOfTabs
, status);
The ViewPagerAdapter implements the minimum required methods getItem, getCount and getItemPosition
My getItem initializes a different Fragment for each position:
#Override
public Fragment getItem(int position)
{
Fragment f = new Fragment();
Log.d("Adbox",String.format("Inside ViewPagerAdapter.getItem(%s)",position));
switch(position)
{
case 0:
Log.d("Adbox","All offers ==");
f=FragmentAllOffers.newInstance(_context);
f.setRetainInstance(true);
break;
case 1:
Log.d("Adbox","Nearby offers ==");
f=FragmentNearbyOffers.newInstance(_context);
//f.setRetainInstance(true);
break;
case 2:
Log.d("Adbox","My coupons ==");
f=FragmentCoupons.newInstance(_context);
f.setRetainInstance(true);
break;
case 3:
Log.d("Adbox","Account ==");
f=FragmentAccount.newInstance(_context);
f.setRetainInstance(true);
//f=LayoutLocal.newInstance(_context);
break;
case 4:
Log.d("Adbox","Preferences ==");
f=FragmentPreferences.newInstance(_context);
f.setRetainInstance(true);
break;
default:
break;
}
return f;
}
The call to setRetainInstance(true) was added in my effort to resolve the problem I am facing but hasn't helped either.
Finally each of the Fragments above implement a public static newInstance() function with the application context as argument. For example the FragmentNearbyOffers contains the following:
public static android.support.v4.app.Fragment newInstance(Context ctx)
{
FragmentNearbyOffers f = new FragmentNearbyOffers();
ctx = context;
//Bundle bdl = new Bundle();
return f;
}
One more important information is that the parent activity is declared as singleInstance and I would like to keep this like this for some reasons.
Everything works fine but at some point when the activity is in the background for some time and I try to return to it either via the TaskManager or by clicking on the application icon I get the exception
android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.advisor.FragmentNearbyOffers$1: make sure class name exists, is public, and has an empty constructor that is public
The class name definitely exists, it's public and doesn't have a constructor which is like having an empty one..
I even added an empty constructor explicitly but this didn't help either, although I verified it is called.
From what I understood from various posts here is that Android when resuming the application, is placing in the FragmentPagerAdapter new instances of the fragments that are not linked to the original activity.. I verified this also because when calling the getActivity from inside the fragment I receive null..
But I don't understand why I am getting this Exception since there is an empty constructor...
I don't even know where to fix this, since the execution enters the onCreate of the activity, then immediately goes into the empty constructors of the fragments and then I get the exception.. Any other methods of the fragments i.e. onAttach, onCreate etc are not called at all..So it seems like it's actually crashing when constructing the fragments..
I am attaching the whole stacktrace I am getting just in case it helps:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.advisor/com.advisor.AdBoxWidgetConfigurationFragment}: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.advisor.FragmentNearbyOffers$1: make sure class name exists, is public, and has an empty constructor that is public
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2110)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2135)
at android.app.ActivityThread.access$700(ActivityThread.java:140)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4921)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.advisor.FragmentNearbyOffers$1: make sure class name exists, is public, and has an empty constructor that is public
at android.support.v4.app.Fragment.instantiate(Fragment.java:399)
at android.support.v4.app.FragmentState.instantiate(Fragment.java:97)
at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1760)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:200)
at com.advisor.AdBoxWidgetConfigurationFragment.onCreate(AdBoxWidgetConfigurationFragment.java:60)
at android.app.Activity.performCreate(Activity.java:5206)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1094)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2074)
... 11 more
Caused by: java.lang.InstantiationException: can't instantiate class com.advisor.FragmentNearbyOffers$1; no empty constructor
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1319)
at android.support.v4.app.Fragment.instantiate(Fragment.java:388)

Notice the $1 at the end of the error. This is a reference to an anonymous class, not the fragment named FragmentNearbyOffers:
Unable to instantiate fragment com.advisor.FragmentNearbyOffers$1
Since fragments require a default constructor, and anonymous classes can never provide one, fragments must always be a named class. The Java Language Specification, section 15.9.5.1 states:
An anonymous class cannot have an explicitly declared constructor.
This section also explains that constructors are generated automatically, according to the context in which the anonymous class is declared. All of these constructors have parameters, thus they have a different signature than the default constructor. The combined effect is that anonymous classes can never have a constructor that matches the default constructor's signature.
You can either declare the fragment class in it's own file or declare it as a static nested class:
public static class NestedFragment extends BaseFragment { ...
Both of those methods should work just fine.

I managed to resolve the problem.. It was quite a tricky one because the exception message was misleading. Inside my fragment I had another nested fragment which I created in runtime with an inner class.
mMapFragment = new SupportMapFragment()
{
#Override
public void onResume()
{
// TODO Auto-generated method stub
super.onResume();
//initMap();
}
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
map = mMapFragment.getMap();
if (map != null)
{
// map.setMapType(GoogleMap.MAP_TYPE_NONE);
map.setMyLocationEnabled(true);
CameraUpdate center = CameraUpdateFactory.newLatLng(new LatLng(lat, lng));
zoom = CameraUpdateFactory.zoomTo(18);
map.moveCamera(center);
map.animateCamera(zoom);
}
}
};
Since after the application is killed by Android, when resuming back to it, android recreates the fragments and I am guessing that it couldnt recreate the nested one because the constructor of the inner class was not visible. I moved the class to a separate file as suggested in some other posts and it worked.

Related

Dialogs from custom classes crash in Android O

I often need user interaction (dialogs) from custom classes that aren't subclasses of Activity or Fragment. Here's an example of how I'm currently doing this. I open the dialog from the fragment manager of the fragment that created the custom class, and I use a small inner class as the target so I can get the dialog result. This keeps all the related code in one place, as opposed to putting onActivityResult in the parent fragment:
public class DocumentViewer extends RelativeLayout {
public void deleteAnnotations() {
DialogFragment dialog = new DialogFragment();
Bundle args = new Bundle();
args.putString("title", this.app.getString(R.string.DELETE_ANNOTATIONS));
args.putString("message", this.app.getString(R.string.CONFIRM_DELETE_ANNOTATIONS));
args.putString("button1Text", this.app.getString(R.string.BUTTON_DELETE));
args.putString("button2Text", this.app.getString(R.string.BUTTON_CANCEL));
dialog.setArguments(args);
DocumentViewerAlertListener listener = new DocumentViewerAlertListener();
listener.canvas = this.canvas;
dialog.setTargetFragment(listener, R.id.confirmDelete);
dialog.show(this.fragment.getFragmentManager(), "confirmDelete");
}
public static class DocumentViewerAlertListener extends ALFragment {
public ALCanvas canvas;
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if ((requestCode == R.id.confirmDelete)&&(resultCode == 1)) {
this.canvas.clearItems();
}
}
}
}
Unfortunately, in Android O, the dialog.show line crashes with this stack trace:
09-20 14:06:12.852 24301-24301/com.bizname.appname E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bizname.appname, PID: 24301
java.lang.IllegalStateException: Fragment ALAlert{1ace4cd #8 confirmDelete} declared target fragment DocumentViewerAlertListener{7e21182} that does not belong to this FragmentManager!
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1209)
at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1549)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1611)
at android.app.BackStackRecord.executeOps(BackStackRecord.java:807)
at android.app.FragmentManagerImpl.executeOps(FragmentManager.java:2394)
at android.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2189)
at android.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2142)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2049)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:718)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
It makes sense that the fragment manager I'm using doesn't know about this inner class, but what's the alternative? I tried using the inner class as the fragment manager...
dialog.show(listener.getFragmentManager(), "confirmDelete");
...and there was no crash, but the dialog didn't appear, perhaps because the listener fragment is never displayed.
I'm open to either a quick fix for my current approach, or a different approach that accomplishes the same thing (but would prefer a quick fix!).
While writing out the question, I had an idea based on "the listener fragment is never displayed." I added this line before showing the dialog:
this.fragment.getFragmentManager().beginTransaction().add(listener, "confirmDelete").commit();
I assume this lets the fragment manager know about my listener fragment, so it doesn't throw the exception.
This seems like an easy and logical fix, but I'm still open to other approaches.

ViewPager + Fragment + saveInstanceState

I have a simple Activity containing a ViewPager, which displays Fragments.
My Activity should display information about a football league, and each fragment displays information like livescroes/matchdays, tables, etc.
The Intent with which I start the Activity, contains the league id.
And each Fragment needs this league id to load the correct data.
So my FragmentPagerAdapter looks like this
public class LeaguePagerAdapter extends FragmentPagerAdapter {
private String leagueId;
public LeaguePagerAdapter(FragmentManager fm, String leagueId) {
super(fm);
this.leagueId = leagueId;
}
#Override
public Fragment getItem(int pos) {
if (pos == 0){
return TableFragment.newInstance(leagueId);
} else {
return MatchdayFragment.newInstance(leagueId);
}
}
}
The TableFragment looks like this ( the matchday fragment looks similar):
public class TableFragment extends PullToRefreshListViewAdFragment {
private String leagueId;
public static TableFragment newInstance(String leagueId) {
TableFragment t = new TableFragment();
t.leagueId = leagueId;
return t;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Setup UI and load data
}
}
Sometimes the leagueId is null. I see the exceptions in the crash logs (crittercism). But Im asking my self why. It seems to me, that the problem is when the activity has been destroyed in the background and reconstructed if (for instance) the user uses the multitasking button to switch to my app.
So as far as I know, the original Intent will be stored internally by Android itself if the Activity has been destoryed. Therefore I have not implemented any onSaveInstanceState() in my activity nor in the fragment. In my activity I read the Intent Extra to retrieve the leagueId. This works fine, also on restoring the activity. I have assumed that by recreating the activity, a new LeaguePagerAdapter will be created and all fragments will also be new created.
Is that correct? Or does the "old" fragment instance will be restored and hence the leagueId is null (because the fragment has not stored the leagueId in Fragments onSaveInstanceState method?).
Is there a way to test such lifecycle things
The reason it is null is because the system restores the Fragment with the default constructor. Here's what the documents say:
Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().
edit: also, take a look at this: Fragment's onSaveInstanceState() is never called
edit: To further add on, you are creating your Fragment with your newInstance(String) method. If your Fragment is killed by Android, it uses the default constructor and so your leagueId variable won't be set. Try using setArguments/getArguments to pass the value into your Fragment instead.

Android managing fragments from activity elegantly

Description of what I'm trying to accomplish:
I have an app that uses a FragmentActivity with a LinearLayout as a container for the fragments. I click different buttons on the FragmentActivity UI and add and remove Fragments to the container in the FragmentActivity. In addition to clicking buttons on the FragmentActivity UI, each Fragment also has buttons that can be clicked which will remove the current fragment and add a different fragment in its place.
The Android way of doing things as I understand it:
I have been reading up on how to do this and as I understand it, the 'proper' way of doing things is to use the FragmentActivity as sort of a relay station and have each fragment do callbacks to the FragmentActivity to communicate events and deal with them.
Scenario:
So let's say that the FragmentActivity is displaying Fragment A and when the user clicks a button in FragmentA I want to stop showing FragmentA and start showing FragmentB. To do this I have created an interface in FragmentA called AListener. In the onAttach() method of FragmentA I use the suggested method of checking that the FragmentActivity implements AListener. When the button in FragmentA is clicked I use one of the callback methods from AListener to communicate the click event to the FragmentActivity. In the FragmentActivity I create an instance of FragmentB and add it to the container in FragmentActivity. Then if some event happens in FragmentB I use the same scheme to communicate the event to the FragmentActivity and do something interesting.
So what's the problem?
For my application I have found this scheme of having Fragments call back to the FragmentActivity and then having the FragmentActivity create a new fragment or call forward to and existing fragment very cumbersome. I have many fragments that need to be displayed by the FragmentActivity and therefore I am implementing an interface for every type of fragment that needs to be displayed (Each fragment is different so they each have their own interface). This causes clashes when I have two interfaces that have the same method signatures and I'm forced to rename one of the methods.
For instance, if I want to attach a listener to a fragment using the onAttach() method of the fragment, then my FragmentActivity must implement the interface. I have found several instances where I have callback methods that have the same name (or I'm forced to name them something similar but different because of a namespace collision). One solution to this would be to use an anonymous classes as callbacks instead of having the FragmentActivity implement the interface. This seems to work well enough, but goes against what the Android documentation says about using the onAttach() method to set the listener.
Are there any elegant ways to approach this problem? It seems to me the tradeoff is that you either force the FragmentActivity to implement an interface for each Fragment that you want to display in it and have the fun problem of watching out for method signature collisions, or you go against the Android documentation and use Anonymous classes to handle the callbacks (not sure of the implications of this).
I am fairly new to Java and feel like I could be missing a concept here that would solve my problem. Can anyone set me straight on how to solve this problem elegantly?
I completely understand your problem since i was dealing it for a long time. Here is the solution i came up right now! It may need some modification based on your need but i it works well.
first of all to to make communicating of event easier in your app use an EventBus! here is the most famous one https://goo.gl/nAEW6
event bus allows you to send event from anywhere to anywhere without need to worry about implementing interfaces, broadcast receivers, threading, etc.
Then add FragmentOrganizer to your app. It's a base class for all of your Fragment Organizers. basically you need one for each activity. Here is the code
public abstract class FragmentOrganizer {
protected FragmentManager fragmentManager;
public FragmentOrganizer(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
openFragment(getInitialFragment());
EventBus.getDefault().register(this);
}
protected abstract Fragment getInitialFragment();
protected abstract void onEvent(Object event);
public abstract boolean handleBackNavigation();
public void freeUpResources(){
EventBus.getDefault().unregister(this);
}
protected Fragment getOpenFragment(){
String tag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() -1).getName();
return fragmentManager.findFragmentByTag(tag);
}
protected boolean isFragmentOpen(Fragment fragment){
return isFragmentOpen(fragment, true);
}
protected boolean isFragmentOpen(Fragment fragment, boolean useArgs){
String fragmentTag = createFragmentTag(fragment, useArgs);
if (fragmentManager.getBackStackEntryCount() != 0) {
String name = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();
if(!useArgs)
name = name.substring(0, name.indexOf("-"));
return name.equals(fragmentTag);
}
return false;
}
private String createFragmentTag(Fragment fragment, boolean addArgs) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(fragment.getClass().getSimpleName());
if(addArgs) {
stringBuilder.append("-");
if (fragment.getArguments() != null)
stringBuilder.append(fragment.getArguments().toString());
}
return stringBuilder.toString();
}
public void openFragment(Fragment fragment) {
if(isFragmentOpen(fragment))
return;
String fragmentTag = createFragmentTag(fragment, true);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.activity_main_fragment_container, fragment, fragmentTag);
transaction.addToBackStack(fragmentTag).commit();
}
}
Now you need to create your fragment organizer that inherit from FragmentOrganizer and implements 3 required methods. here the sample
public class MainFragmentOrganizer extends FragmentOrganizer {
public MainFragmentOrganizer(FragmentManager fragmentManager) {
super(fragmentManager);
}
#Override
protected Fragment getInitialFragment() {
return HomeFragment.newInstance();
}
#Override
public void onEvent(Object event){
if(event instanceof ClickedOnPhotoEvent){
String photoCode = ((ClickedOnPhotoEvent) event).photoCode;
openFragment(PhotoFragment.newInstance(photoCode));
}
}
#Override
public boolean handleBackNavigation(){
Fragment fragment = getOpenFragment();
if (fragment instanceof HomeFragment){
return false;
} else {
fragmentManager.popBackStack();
return true;
}
}
}
And in your activity you just need to insatiate your FragmentManager and let it do the magic!
fragmentManager = getSupportFragmentManager();
fragmentOrganizer = new MainFragmentOrganizer(getSupportFragmentManager());
#Override
public void onBackPressed() {
//first let fragment organizer handle back. If it does not activity takes cares of it!
if(!fragmentOrganizer.handleBackNavigation()){
finish();
}
}
#Override
protected void onDestroy() {
fragmentOrganizer.freeUpResources();
super.onDestroy();
}
It may seem a lot of code but as you see most of the code encapsulated in FragmentOrganizer base class and it does all the general works so you just have to copy this file from one project to another.
As i said in the beginning i just came up with this solution right now, so it may not be perfect. I Plan to use this in my next project i hope you do to. And if you do i really appritiate if you share your though. have a good time
A co-worker of mine came up with what I consider an elegant solution to this problem.
Remember, what we're trying to achieve is a way for fragments to callback to the parent activity without having the activity implement the interface. Also, we need to be able to automatically set the listener again if the activity is destroyed and then recreated.
Activities have a lifecycle callback called onAttachFragment(Fragment fragment) which is called whenever a fragment is being attached to the activity. So, for instance, when a new fragment is created within the activity, this gets called. It also gets called if an activity that was previously destroyed gets recreated. What you can do is use an interface or an anonymous class to set a listener on the new fragment in onAttachFragment like this:
#Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
//Determine which fragment this is by checking its tag
if(fragment.getTag().contains(TextFrag.FRAG_TAG)){
//set a listener on this fragment using an anonymous class or interface
((TextFrag)fragment).setListener(new TextFragButtonListener() {
#Override
public void onButtonClicked() {
count++;
counterTV.setText(String.valueOf(count));
}
});
}
}
Using this technique we are able to avoid the activity having to implement an interface for the callback and thus we avoid any naming conflicts with our callback methods. Also, if the activity is destroyed, once it is recreated the listener will be automatically reset so our callbacks will still work.
There are probably many other ways to do this and I'd love to here anyone's criticisms of this technique and suggestions for any other techniques.

The life cycle of Fragment in Android

I asked a question about Fragment's initialization and post my code( Delay initialization when using Fragment in Android ). The question is solved. But my problem still exists. When I call onLoad() to get the variables, it throws NullPointerException. I initialize these variables in onCreateView(). So I guess the life cycle is end. I loged the info in A.java in these function: onDestroy, onDestroyView, onStop. the onDestroyView and onStop is called while the onDestroy is not when fragment A is changed to another one. Strange:
There is a EditText in fragment A. I input a string abc. 1、If the life cycle is end, then when I change fragment from A to another then change back to A, the input should be empty. But it's NOT. 2、if the life cycle is not end, why can't I get the variable?
So is there anything to save the abc somewhere? What's it? Where?
It seems that I can't delay the initialization in Fragment. I can't get the non-static variables.
Code:
/** super class of Fragment */
public class BaseFragment extends Fragment {
public void onLoad(Context context){
}
}
/** */
public class AFragment extends BaseFragment{
TextView name;
#Override
public View onCreateVew(...){
name = new TextView(..);
}
#Override
public void onLoad(Context context){
// here will throw NullPointerException
name.setText("=========");
}
}
public class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabChangeListener,ViewPager.OnPageChangeListener {
....
#Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.mClss.getName(), info.mArgs);
}
#Override
public void onPageSelected(int position) {
TabWidget widget = mTabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
BaseFragment f = (BaseFragment) getItem(position);
f.onLoad(mContext);
}
}
But my problem still exists. When I call onLoad() to get the
variables, it throws NullPointerException. I initialize these
variables in onCreateView().
The problem is in the way you work with those fragments. More exactly, in the onPageSelected method you call the FragmentPagerAdapter's getItem() method to find the fragment for that position(or this is what I'm thinking you're trying to do). But calling this method simply instantiate a new fragment each time you call it, it doesn't give you a reference to the ViewPager's fragment for that position. This newly instantiated fragment isn't attached to an Activity and it's onCreateView method hasn't be called so it doesn't have any views created. Attempting to use your onLoad method to access one of this views is wrong and it will throw that NullPointerException. You should try to use my code from the previous answer, as that code will try to find the fragments that the ViewPager instantiated and it's using as its pages through the FragmentManager.

Unable to instantiate Fragment

Unable to instantiate fragment make sure class name exists, is public,
and has an empty constructor that is public
Is it because my Fragment is not a static class?
Is it because my Fragment is an inner class?
If I make my Fragment a static class, all my references to findViewById fail, which means a LOT of refactoring.
How can I solve this without turning my inner Fragment into a static class?
is it because my Fragment is an inner class
If your fragment is an inner class, it must be a static inner class. Ideally, it's a standalone public Java class.
if I make my Fragment a static class, all my references to findViewById fail, which means a LOT of refactoring
You needed to do that refactoring anyway. Widgets are now owned by the fragment, not the activity. Fragments should know as little as possible about what activity contains them, so they can be shuffled between different activities as needed to support phones, tablets, TV, etc.
How can I solve this without turning my inner Fragment into a static class??
You make it a standalone public Java class.
Your Fragment shouldn't have constructors (see this documentation and its examples).
You should have a newInstance() static method defined and pass any parameters via arguments (bundle)
For example:
public static final MyFragment newInstance(int title, String message)
{
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle(2);
bundle.putInt(EXTRA_TITLE, title);
bundle.putString(EXTRA_MESSAGE, message);
fragment.setArguments(bundle);
return fragment ;
}
And read these arguments at onCreate:
#Override
public Dialog onCreate(Bundle savedInstanceState)
{
title = getArguments().getInt(EXTRA_TITLE);
message = getArguments().getString(EXTRA_MESSAGE);
//...
//etc
//...
}
This way if detached and re-attached the object state can be stored through the arguments, much like bundles attached to Intents.
As CommonsWare said make it static or standalone, additionally don't know why you need a shedload of refactoring for getting findViewById to work. Suggestions:
Using the view inflated in onCreateView,
inflatedView.findViewById(.....)
or calling it in onActivityCreated(.....)
getActivity().findViewById(......)
But even if you still need a load of refactoring then that might just be the way it is, converting an app to use fragments doesn't come for free having just finished a project doing so.
I had this problem as well - turns out it was getting confused because my custom Fragment had a constructor.
I renamed the constructor method and called the new method instead upon instantiation, and it worked!
public static class MyDialogFragment extends DialogFragment {
public MyDialogFragment(){
}
public Dialog onCreateDialog(Bundle savedInstanceState) {
LinearLayout main = new LinearLayout(getActivity());
main.setOrientation(LinearLayout.VERTICAL);
return (new AlertDialog.Builder(getActivity()).setTitle(
getText("Title")).setView(main).create());
}
}
In my case, I was missing the constructor, the post from #eoghanm above helped me
public static class MyDialogFragment extends DialogFragment {
public MyDialogFragment(){
}
...
}
Using setRetainInstance(true) worked for us. Our inner classes now look like this:
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.
Fragment fragment = new MySectionFragment();
Bundle args = new Bundle();
args.putInt(MySectionFragment.ARG_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
fragment.setRetainInstance(true);
return fragment;
}
// ...
}
public class MySectionFragment extends Fragment {
public static final String ARG_SECTION_NUMBER = "section_number";
#SuppressLint("ValidFragment")
public MySectionFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//...
}
// ...
}
PS. Here's an interesting one about setRetainInstance(boolean): Understanding Fragment's setRetainInstance(boolean)
if you don't want to make the inner class static, try to override the method onPause of the dialog fragment like this:
public void onPause()
{
super.onPause();
dismiss();
}
so the fragment should be destroyed when the app goes on pause and there is no exception. i tried it and works.
Hahah my hilarious issue was I had a call to getString() as a member level variable in my fragment which is a big no no because it's too early I guess. I wish the error was more descriptive!
Make sure the Fragment isn't abstract. Copy&paste makes this kind of things happen :(
The inner class constructor must be pass in an instance of the outer class. so it is said the compiler cannot find the constructor which has no parameter. so it should be put into static of other java file.
i have meet this problem
you need use full class name :
eg:
Fragment.instantiate(MainActivity.this, com.XX.yourFragmentName);
must full class name
It is also worth trying to check that your default Fragment constructor is public, not package-private, which Android Studio might propose. This was the cause in my case.

Categories

Resources