In my custom view I have a Java class running a simple game. When the game is finished I'd like to display my DialogFragment, but the getFragmentManager() method seems to be undefined.
FragmentManager manager = getFragmentManager();
Finish finish = new Finish();
finish.show(manager, "done");
I've tried getting the manager through a Fragment obj as:
Fragment fragment = new Fragment();
FragmentManager manager = fragment.getFragmentManager();
But that returns as null. I know it's a new Fragment instance, but I'm not sure what value to give it. Any help would be much appreciated.
if your view is attached to an Activity then simply you can do
((Activity)getContext()).getFragmentManager();
or
((ActivityCompat)getContext()).getSupportFragmentManager();
and to be more safe please make sure you check against of the View Context is instance of an Activity by doing such:
if(getContext() instanceof Activity)// do something
and a better solution is, i had rely on a callback between the View and the Activity.
I use that simple helper method:
fun getFragmentManager(context: Context?): FragmentManager? {
return when (context) {
is AppCompatActivity -> context.supportFragmentManager
is ContextThemeWrapper -> getFragmentManager(context.baseContext)
else -> null
}
}
For AndroidX, you can try:
ContextThemeWrapper themeWrapper = (ContextThemeWrapper) getContext();
FragmentManager fm = ((AppCompatActivity) themeWrapper.getBaseContext()).getSupportFragmentManager();
As recommoned by #k0sh, instaceof safety check is recommonded.
You can use this (in Kotlin):
if ((context as ContextThemeWrapper).baseContext is AppCompatActivity) {
//View is attached to an AppCompatActivity
} else {
//View is not attached to an AppCompatActivity
}
}
Related
So, what my problem is that in one fragment(w/i a viewpager, I'll call this Fragment A) I click on this dynamically created button that adds a new fragment(I'll call this Fragment B) in a framelayout which allows me to use PayPal service. On PayPal Activity result, Fragment B communicates with the main Activity via a communicator(an interface class) to call Fragment A to change that text. But I'm getting a null pointer exeception crash.
To be specific:
what I did was that I made a global TextView variable that is initialized on click. I did this b/c I have a list of other things that are dynamically inflated and to avoid the TextView from being initialized with wrong layout I initialized it on click.
bidChange.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
eventListChangeKey = keyVal;
eventListChangeIdx = eventListIdx;
eventBiddingChangeIdx = finalI;
priceToChage = (TextView) biddersLayout.findViewById(R.id.single_list_bidder_bid_price);
Bundle bundle = new Bundle();
bundle.putInt("auctionID", auctionId);
bundle.putInt("dateID", dateId);
bundle.putInt("FromWhere", 2);
Fragment fragment = new Fragment_Home_ItemInfo_Bid();
fragment.setArguments(bundle);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.container_mainScreen, fragment, "itemInfo_bid")
.addToBackStack(null)
.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
});
In the main activity
public void changeBidderPrice(String s) {
Fragment fragment = viewPagerAdapter.getItem(1);
((Fragment_List) fragment).changePrice(s);
}
is what I do
back in Fragment A
public void changePrice(String val) {
priceToChage.setText(val);
dataMap.get(eventListChangeKey).get(eventListChangeIdx).getBidList().get(eventBiddingChangeIdx).setPrice(val);
}
I've thought this over an over but I just can't figure this out. I've searched for similar cases in StackOverflow but I wasn't able to get a help.
Would the problem be the way I initialize that TextView? or is it the way I'm calling Fragment A from the main activity?
for fragments onViewCreated() is called after onCreateView() and ensures that the fragment's root view is non-null. Any view setup should happen here. E.g., view lookups, attaching listeners.
source : codepath
for activities onCreate()
I read the many posts that have already dealt with similar issues but haven't been able to find something that answers my question completely.
I have an Android app that uses nested fragments (from the v4 support library). I have a main FragmentActivity that contains a fragment, and that fragment contains a ViewPager which enables swiping between 3 internal fragments.
I'd like to be able to save the state of each of the 3 internal nested fragments, and for that I overrode the onSaveInstanceState() method for each of the 3 internal fragments and attempted to restore the state in onActivityCreated(), like so:
InternalFragment1.java:
public class InternalFragment1 extends Fragment {
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
// Commands to attach to main UI components...
if(savedInstanceState != null) {
// Commands to restore the saved state...
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
// Commands to save the state into outState...
super.onSaveInstanceState(outState);
}
}
However when onActivityCreated() is called, savedInstanceState is always null, regardless of whether a saved state exists or not.
I should also point out that calling this.setRetainInstance() throws an exception stating: "Can't retain fragments that are nested in other fragments".
How can I properly save and restore the nested fragments' state?
I had a similar issue and was looking for hints on solving it. Eventually, I realized that my parent fragment's onCreateView included:
mChildFragment = ChildFragment.newInstance(mId);
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, mChildFragment).commit();
This, of course, creates a new instance of the child fragment, which has a null bundle for the savedInstanceState. Surrounding the above block with a conditional:
if(savedInstanceState == null) {
mChildFragment = ChildFragment.newInstance(mId);
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, mChildFragment).commit();
}
seems to make it work, in that now the onCreate in the child fragment sees the non-null savedInstanceState I created for it in onSaveInstanceState, and is restored to how I want it.
if you use setRetainInstance(true) then of course the bundle is null.
The fragment is not destroyed but only detached from the current activity and attached to the new activity. Only when the fragment is destroyed, you get a bundle with the values you saved in onSaveInstanceState.
Just try to remove setRetainInstance(true).
That's a problem you may encounter when the parent fragment is retaining.
You can try this:
http://ideaventure.blogspot.lu/2014/10/nested-retained-fragment-lost-state.html
But I would better recommend removing the setRetaining() on the parentFragment.
There doesn't seem to be a simple way for a nested fragment to retain information. My solution was to have the parent fragment hold onto a map of Bundles and the nested fragments get their own during onCreate. The biggest issue with this is you can't have more than one instance of each nested fragment.
Ex (sorry this is in Kotlin, but it's the same thing in Java)
class ParentFragment : Fragment(), ParentFragmentListener {
val bundles = SparseArray<Bundle>()
fun getChildBundle(fragmentId : Int) : Bundle {
if (bundles.get(fragmentId) == null) {
val bundle = Bundle()
bundles.put(fragmentId,bundle)
return bundle
}
return bundles.get(fragmentId)
}
}
interface ParentFragmentListener {
fun getChildBundle(fragmentId : Int) : Bundle
}
class ChildFragment : Fragment() {
lateinit var childBundle : Bundle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val listener = parentFragment as? ParentFragmentListener
val childBundle = listener?.getFragmentsSavedBundle(UNIQUE_FRAGMENT_ID)
if (childBundle != null) this.childBundle = childBundle else childBundle = Bundle()
}
}
Im try getView from my fragment instantiated using getFragmentManager.findFragmentbyTag, see my code below:
MyTaskFragment.java:
Initializing my Fragment
DialogFragment newFragment = new newTaskFragment();
newFragment.show(getFragmentManager(), "newTask");
inside MyTaskFragment have the DialogFragment basically this:
public static class newTaskFragment extends DialogFragment {
// ..
builder.setView(inflater.inflate(R.layout.new_task_dialog, null))
// ..
return builder.create();
// ..
}
and also inside MyTaskFragment jave a picker:
public static class TimePickerFragment extends DialogFragment
implements TimePickerDialog.OnTimeSetListener {
and this is the problem(inside picker):
Fragment fragment = getFragmentManager().findFragmentByTag("newTask");
View viewv = fragment.getView();
Button button = (Button) viewv.findViewById(R.id.choice_hour);
the Button above always returns null with error, the id is inside the layout R.layout.new_task_dialog. I don't understood.
Please someone helps me.
You may be receiving this error if:
Your View ID choice_hour has a typo and does not match the View ID
you expect to find in the View hierarchy.
There is more than one Fragment with the "newTask" ID which doesn't
share the same layout.
Your OnTimeSetListener is being called before your Fragment's
onFinishInflate().
Your OnTimeSetListener is being called before your Fragment's
onCreateView() is called.
Your OnTimeSetListener is being called before your Fragment is
attached to your Activity.
See this answer for more details: findViewByID returns null
Objective: To use fragment arguments to pass along the string value from a TextView to a new fragments TextView, BUT while using a ViewPager with different layouts/fragments in the FragmentPagerAdapter.
Problem: The new fragment never receives the fragments arguments from the previous fragment.
My Set up: I have my Activity hosting the ViewPager and FragmentPagerAdapter. I have overridden FragmentPagerAdapters getItem(int position) method to create a new fragment instance depending on the current position of the ViewPager. Right now I only have two Fragments in my adapter, but will be adding more after getting over this obstacle. I am using the newInstance() technique to pass along the Fragment's arguments, but nothing is ever passed.
Pertinent Code:
My FragmentPagerAdapter code:
//this is a variable that I pass in as the newInstanct() methods parameter,
// I update this variable from my fragments constantly
public static String fragmentArgumentsValue = "";
mViewPager.setAdapter(new FragmentPagerAdapter(fm) {
#Override
public int getCount() {
return NUMBER_OF_VIEWS;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
Log.d(TAG, "---In getPosition(), position 0---");
return Frag1
.newInstance(fragmentArgumentsValue);
case 1:
Log.d(TAG, "---In getPosition(), position 1---");
return frag2
.newInstance(fragmentArgumentsValue);
default:
Log.d(TAG, "---In getPosition(), DEFAULT---");
return frag1
.newInstance(fragmentArgumentsValue);
}
}
});
One of the fragments newInstance() method:
public static Fragment newInstance(String fragmentArgumentsValue) {
Frag1 f = new Frag1();
Bundle bun = new Bundle();
bun.putString(KEY_FRAGMENT_ARGUMENTS_STRING, fragmentArgumentsValue);
f.setArguments(bun);
return f;
}
Getting the fragments arguments:
String argString = getArguments().getString(
KEY_FRAGMENT_ARGUMENTS_STRING);
if (argString.length() != 0) {
Log.d(TAG, "Trying to set the frag args to:" + argString);
mWorkingTextView.setText("" + argString);
} else {
Log.d(TAG, "Couldn't set frag args to: " + argString);
}
What I've Tried: I've tried giving the Activity that hosts the ViewPager and FragmentPagerAdapter a static variable that I constantly update in each one of my fragments, I include the static variable in the fragment.newInstance(staticVariableInActivity) method, but this doesn't seem to work.
I've also tried using the ViewPager callback viewPager.setOnPageChangeListener() I had overridden the onPageSelected(int pos) and tried to pass the fragment arguments there, nevertheless it didn't work... so please help me S.O.!!!
My thoughts: I do not have the different fragments and layouts in an ArrayList or any list for that matter, I just instantiate the Fragments via the newInstance() method depending on the positions of the FragementPagerAdapter, could this be a problem? Should I create a singleton list of the layouts/fragments? So I can change the values of the Fragments TextViews via the singleton list? (excuse me if that's a dumb or not possible thing to do).
Note: I am am saving away the TextViews values via public void onSaveInstanceState(Bundle outState) to handle screen rotations. Could this be a problem?
Alright so I this link will help in the communication between fragments: Communication with other Fragments .
You need to define a interface that the Activity will implement and the fragments will use to send data onward to the activity, and the activity will then find the fragment by tag doing something like this:
frag = (Fragment2) getSupportFragmentManager()
.findFragmentByTag(
"android:switcher:" + R.id.viewPager + ":2");
and then update the fragment by calling a public method implemented within the fragment.
The link provided will help greatly, it is from the Android Development website.
Adds the bundle inside your Adapter.
example in the constructor of the adapter :
public ViewPagerAdapter(Context context, FragmentManager fm) {
super(fm);
fragments.add(TileFragments.newInstance());
Bundle bundleFeatures = new Bundle();
bundleFeatures.putString(ContentName.FEATURES.toString(),ContentName.FEATURES.toString());
fragments.get(0).setArguments(bundleFeatures);
}
I need a function in my Activity, that sets fragment to my ViewGroup (FrameLayout in this case). Of course, I can use such construction:
public void setFragment(Fragment fragment){
FragmentManager fm=getFragmentManager();
//etc
}
But with this solution I need to create fragment somewhere else, not in my function. So, if class MyFragment extends Fragment, I need something like this:
setFragment(MyFragment);
Is it possible? Can I pass class as a parameter of function and then create instance of it
And if it's not - is it a bad idea to create fragment behind the function? Like
MyFragment my=new MyFragment();
setFragment(my);
If the two fragments are using the same layout then you can just do something like this
public void setFragment(){
Fragment newFragment;
if(displayFragOne){
newFragment = new MyFragment();
}else if(displayFragTwo){
newFragment = new OtherFragment();
}
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(r.id.the_id_of_layout,fragment).addToBackStack(null).commit();
}
the fragment paramater is passed in from where you initialized it usually in onCreate()
if you need the fragments to display at the same time then you need another FrameLayout to replace.
hopefully that answered your question, if not let me know
EDIT 2:
oh I see now you want to pass a class, sorry. As far as I know you cant do that, passing in an already initialized fragment would be a a better solution like I had before my first edit