I have a ListView with several rows. Each row has a button.
I want the button to start a FragmentTransaction to replace the Fragment that the ListView is in.
However, in the getView() method of the Adapter, this line does not work:
FragmentTransaction t = getContext().getSupportFragmentManager().beginTransaction();
It does not like the context.
Can this be done in this way or does the Transaction have to happen elsewhere?
First get the context in your Constructor and then try following code,
FragmentTransaction ft = ((FragmentActivity)context).getSupportFragmentManager().beginTransaction();
getSupportFragmentManager() is only defined for the class FragmentActivity, not for Context. Thus, the compiler can't resolve the call if you try to call it on an object of type Context.
You can use reflection to do this in a safe way. This will always work as long as you pass your FragmentActivity as the Context. Note: FragmentActivity is a subclass of Context, and thus you can pass FragmentActivity wherever you can pass Context.
So use this code instead:
if (getContext() instanceof FragmentActivity) {
// We can get the fragment manager
FragmentActivity activity = (FragmentActivity(getContext()));
FragmentTransaction t = activity.getSupportFragmentManager().beginTransaction();
}
I'd suggest you to pass FragmentManager instance to the Adapter constructor like that:
public class YourAdapter extends...
private FragmentManage mFragmentManager;
public YourAdapter(FragmentManager fm) {
mFragmentManager = fm;
}
And use it explicitly:
FragmentTransaction ft = mFragmentManager.beginTransaction();
That should give you posibility to initialize Adapter with either Fragment.getFragmentManager() or FragmentActivity.getSupportFragmentManager() instance, since they are pointed at the same object
Related
This question already has an answer here:
java.lang.ClassCastException: android.app.Application cannot be cast to androidx.appcompat.app.AppCompatActivity
(1 answer)
Closed 1 year ago.
I use this code in recyclerViewAdapter. I need to open new fragment from fragment.
#Override
public void onClick(View view) {
AppCompatActivity activity = (AppCompatActivity) view.getContext();
CarDetailFragment carDetailFragment = new CarDetailFragment();
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragmentCar, carDetailFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
Please check the below code and you can direct getContext() in a fragment and cast it to your activity of the current fragment.
Context context= getContext();
CarDetailFragment carDetailFragment = new CarDetailFragment();
FragmentManager fragmentManager = ((YOUR_ACTIVITY_NAME) context).getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragmentCar, carDetailFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Declare a public static context variable in your Fragment and assign getActivity to it and then use this variable in Adapter to your getSupportFragmentManager.
public static Context context; //declare the variable in your Fragment
context=getActivity;// assign it in your Fragment before calling Adapter
Then in Adapter use it like this
FragmentManager fragmentManager= ((AppCompatActivity) YourFragmentName.context).getSupportFragmentManager();
EDIT
As static context variable could cause memory leak so you can use this
// while calling the adapter pass getActivity as context
MyAdapter myAdapter = new MyAdapter(getActivity, /* other variables */);
Then in Adapter use it like this
private Context context;
public MyAdapter(Context context, /* Other part */ ) {
this.context = context;
//other part
}
FragmentManager fragmentManager= ((AppCompatActivity) context).getSupportFragmentManager();
Please take the reference from below code ..
public class NewAdapter extends RecyclerView.Adapter<NewAdapter.<Your-View-Holder-Name>> {
private FragmentActivity activity;
public ImageAdapter(FragmentActivity mActivity){
activity = mActivity;
}
Change your getActivity() to activity as mentioned below..
#Override
public void onClick(View view) {
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragmentCar, carDetailFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
When you make instance of Adapter class in Activity please use following syntax..
mAdapter = new NewAdapter(MainActivityHomePage.this);
My guess is your RecyclerView adapter holds a reference to application context and you're using that to create views. The views then hold the context you used to create them, so when you ask them to getContext() they're going to give you the application instead of the activity themed context.
What to keep in mind:
Activity extends Context, so forget about getBaseContext().
When working with views, you want a themed context. If you're inside a View use getContext(). If you're inside an Activity use this. If you're inside Fragment use getContext(). Forget about getApplicationContext() when working with views.
Your adapter doesn't need Context in constructor. The only place you need a context is when creating views and you can get the right Context from the RecyclerView which is going to display the items.
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// parent is the RecyclerView.
Context context = parent.getContext();
// TODO Inflate your view and return view holder.
}
Even if you follow all this you don't get a guarantee that View.getContext() will get you an Activity. It could be a ContextThemeWrapper.
Start thinking about the app architecture. The adapter probably shouldn't know anything about activities or fragments or fragment transactions. The adapter should take data and turn them into views. In the other direction it should take clicks from user and notify some listener. The adapter should take a onClickListener constructor parameter, which you prepared inside the activity (which knows how to do fragment transactions).
I have application which uses several fragments for showing different tabs.
The first fragment is List View with custom cell. Each cell has its own set of buttons. I want to show different fragment when a button is clicked. To know when the desired button is clicked I added an onclick event in the array adapter for my list. Is there a way to show the new fragment from Array Adapter. What I tried so far and it didn`t worked :
final FragmentManager fragmentManager = ((Activity)mContext)getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
PostFragment postFr = new PostFragment();
fragmentTransaction.replace(R.id.detail_container, postFr);
fragmentTransaction.commit();
try this code:
final FragmentManager fragmentManager = ((FragmentActivity)mContext).getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
PostFragment postFr = new PostFragment();
fragmentTransaction.replace(R.id.detail_container, postFr);
fragmentTransaction.commit();
getSupportFragmentManager() is only defined for the class FragmentActivity, not for Activity
what specifically went wrong when you tried to do that?
This is the method I use to go to a new fragment. I put it in an abstract BaseActivity class (extends AppCompatActivity) so that all of my implementing activities have use of the method.
The method takes the fragment you want to go to, and a boolean variable to indicate whether or not to add the transaction to the backstack.
The activity must have a FrameLayout declared in its XML resource file, with an id of "container". This is where the fragments will be placed.
public void gotoFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment);
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.commit();
}
In order to call this method from your ArrayAdapter class, pass an instance of the BaseActivity into the constructor of your Adapter.
For the Context parameter of the adapter, pass getActivity() from the fragment.
If all of your activity classes extend the BaseActivity class, you can safely say in your adapter:
public class CustomArrayAdapter extends ArrayAdapter {
private BaseActivity mActivity;
public CustomArrayAdapter(Context pContext, int pLayoutResource, List<Object> pItems) {
super(pContext, pLayoutResource);
mActivity = (BaseActivity) pContext;
}
#Override
public void onItemClick(int position) {
if (mActivity != null) {
mActivity.goToFragment(new PostFragment());
}
}
I have an Activity with ListView, when clicking on a list item opens a new fragment.
Do I need to create a new fragment every time like this?
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.root_layout,new MyFragment());
Or will be enough to create a fragment once and then use it?
in activity:
MyFragment myFragment = new MyFragment();
......
in onItemClickListener:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.root_layout,myFragment);
It depends on your case. In most cases every list item opens a different fragment (with different data). Then you have to make a static newInstance(Data mySerializableData) method in your fragment, use default constructor inside of it, pass data over Fragment arguments See DetailFragment and use fragmentTransaction.replace() in your activity to add this fragment.
When you dont want your fragment to be changed you can create it only once as you say but there is no need of adding it on every item click. So one creation and only one add.
No, you don't need to create it every time. First, instead of using "add", use "replace". If there is no fragment in fragment manager, your fragment will be added, instead it will be replaced. If you use "add", then you could accidentally add more than one fragment.
You should check for the fragment in the fragment manager and call methods for content updates.
Example:
myFragment = (MyFragment) fragmentManager.findFragmentById(R.id.my_fragment);
if (myFragment == null) {
myFragment = MyFragment.getInstance(someDataIfNeeded);
fragmentManager.beginTransaction().replace(R.id.my_fragment, myFragment).commit();
} else {
myFragment.updateFragmentContent(someData);
}
check instance of that fragment everytime like this-
In your fragment class -
public static final MyFragment newInstance()
{
MyFragment f = new MyFragment();
return f;
}
and in your activity when you want to create fragment -
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.root_layout,MyFragment.newInstance());
this is well n good manner...
I have this Activity which at first shows a Fragment with a list of elements. This works perfectly with this code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.list_act);
if(null == savedInstanceState)
{
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ListFragment glfragment = new ListFragment();
fragmentTransaction.add(R.id.listfrag1, glfragment);
fragmentTransaction.commit();
}
}
Well I have a ListFragment and a DetailFragment. But I don't know how to do the transition when I click an element of the list. I know the fragmentTransaction.replace(), but I don't know WHEN to call it.
I thought I should use the OnListItemClick() inside the ListFragment, but I don't know how to use the FragmentManager inside the Fragment and not in the main Activity... Also I want to "export" some data to the DetailFragment as if it was a Intent, but it's not.
To use the fragment manager inside your Fragment, simply call
getActivity().getFragmentManager() instead of getFragmentManager(). Implementing this in your OnItemClickListener should suffice.
What I would do is:
Define an interface with one method listItemSelected() with as an argument the id of the selected item
Let your activity implement this interface
In the onAttach of your list fragment, take the activity and keep it as a member variable, cast to the interface type. Make sure that in the onDetach you dereference it.
In your onListItemClick, call this method on your activity
In the activity, you can now do a new fragmenttransaction, this time you need to replace instead of add the fragment
To create your detail fragment with the correct argument (the id), use the method described here.
This should normally work fine.
I would like to add a FragmentActivity in the activity layout. In order to make fragment transactions (such as add, remove, or replace a fragment), the api guides say that I first need to get an instance of FragmentTransaction from your Activity and then add a fragment using the add() method specifying the fragment to add and the view in which to insert it. Ok pretty straightforward so far, but what should I do in the FragmentActivity case?
AllEventsFragments events;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if ( savedInstanceState == null )
{
events = new AllEventsFragments();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.content_frame, events,"events");
// Commit the transaction
transaction.commit();
}
}
in which AllEventsFragments is defined as follows:
public class AllEventsFragments extends FragmentActivity implements ActionBar.TabListener
{
...
}
Since the add method accepts a Fragment as second argument the error returned is:
The method add(int, Fragment, String) in the type FragmentTransaction is not applicable for the arguments (int, AllEventsFragments, String)
I would like to add a FragmentActivity in the activity layout.
You are trying to nest activities. That is not supported via fragment transactions, and what little support there ever was for it has been deprecated for ~2.5 years.
However, you can move much of the AllEventsFragments logic into a Fragment, which can then be used from both AllEventsFragments and wherever else you are trying to use it.