I need to do something like this. Suppose I have 2 fragments A and B.There is a text which can be clickable in fragment A and when user click this text , he can go to fragment B. This example helped me to do it but I think it does not work for fragment. So please tell me a way to solve this problem.
public class myClaimsFragment extends Fragment {
TextView requestNewClaim;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View newClaimRequest = inflater.inflate(R.layout.activity_my_claims, container, false);
SpannableString ss = new SpannableString("Request");
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View textView) {
Intent intent = new Intent(getActivity(),LoginActivity.class);
startActivity(intent);
}
};
ss.setSpan(clickableSpan , 0,ss.length() , Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
requestNewClaim =(TextView) newClaimRequest.findViewById(R.id.requestHere);
requestNewClaim.setText(ss.toString());
requestNewClaim.setMovementMethod(LinkMovementMethod.getInstance());
return newClaimRequest;
}
}
Layout XML
<LinearLayout
android:id="#+id/view2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/requestHere"
android:clickable="true"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="#000000"
android:layout_marginLeft="20dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</LinearLayout>
If LoginActivity is a fragment class then it would be okay if you use setOnClickListener on textview. But for fragment change you have to change Intent to fragmentTransaction,
Use something like,
textview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getFragmentManager().beginTransaction().replace(R.id.container, new LoginActivity() ).addToBackStack("").commit();
});
But, if you want to use SpannableString then do like this,
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View textView) {
getFragmentManager().beginTransaction().replace(R.id.container, new LoginActivity() ).addToBackStack("").commit();
}
};
Here, R.id.container is the fragment of your main activity layout in which new view will be replaced.
You can not call Fragment via Intent. You need to replace your current fragment with new one.
you have to replace your fragment A to B, use this code
FragmentManager fm = getActivity.getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment fragment = new FragmentB();
ft.replace(R.id.activity_main_content_fragment,fragment);
ft.commit();
In This code replace R.id.youframelayoutid, then it workable
If its code useful so please mark me my answer. :)
As an usual manner you should put a FrameLayout in your Activity's layout XML file. This FrameLayout acts like a placeholder for your Fragments. In other words, Fragment A can be pasted there, so is for Fragment B.
Okay, suppose you've added a FrameLayout in you activity's layout file. Pasting fragments on it and also replacing fragments should be done by the FragmentManager. Hence, you should grab a reference to a FragmentManger in your activity class. For getting this done ...
If you use Android Support Libraries, you should get a reference to FragmentManger by getSupportFragmentManager()
Otherwise, getFragmentManager()
In Android adding fragments and also replacing them are done in the form of a transaction. Thus you should inform the fragment manager that you would like to do a transaction. This could be done via:
FragmentTransaction transaction = fragmentManger.beginTransaction();
Now, you can apply all what you want on this transaction object. For instance, Adding a fragment could be done like this:
transaction.add(R.id.placeholder, new FragmentA() , "tag-frag-A");
For replacing ...
transaction.replace(R.id.placeholder, new FragmentB(), "tag-frag-B");
After you're done, you commit that transaction by calling
transaction.commit();
Notes:
FragmentManager acts like a container for your added fragments. You can search through your added fragments by their tag.
Device rotation does not remove added fragments in the FragmentManager. Thus in your onCreate method take care you've added any fragments only once.
You can add a Transaction to the back stack. This means that whenever user clicks on the Android back button this fragment will be removed from the state stack and also will be rolled back.
Related
I have a MainActivity in which I have Added a Fragment ==> BenefitsFragment
in BenefintsFragment there is a RelativeLayout
<RelativeLayout
android:visibility="gone"
android:background="#android:color/white"
android:id="#+id/benefitContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
</RelativeLayout>
I am adding another fragment like
browseBtn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
RelativeLayout mLayout = (RelativeLayout) view.findViewById(R.id.benefitContainer);
mLayout.setVisibility(View.VISIBLE);
getChildFragmentManager().beginTransaction()
.add(R.id.benefitContainer, new ConfirmPinFragment()).commitNow();
}
});
In my new ConfirmPinFragment I am trying to go back to old BenefitsFragment as
backBtn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
getChildFragmentManager().popBackStack();
}
});
However this popBackStack not working, if i try to remove using
getChildFragmentManager().beginTransaction().remove(ConfirmPinFragment.this).commitNow();
It crashes saying
java.lang.IllegalStateException: FragmentManager is already executing transactions
There are two things you need to do for this to work. But first of all there is really no need for commitNow(). The first thing you need to do is to add the fragment you want to go back to into the back stack during the transaction:
browseBtn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
RelativeLayout mLayout = (RelativeLayout) view.findViewById(R.id.benefitContainer);
mLayout.setVisibility(View.VISIBLE);
getChildFragmentManager().beginTransaction()
.add(R.id.benefitContainer, new ConfirmPinFragment())
.addToBackStack("benefits fragment").commit();
}
});
and when you go back use the getFragmentManager() instead of getChildFragmentManager() because you actually want the fragment manager that holds this fragment. Child fragment manager is the manager that manages the fragments inside this fragment.
backBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getFragmentManager().popBackStack();
}
});
Hope this helps.
You add to the back state from the FragmentTransaction and remove from the backstack using FragmentManager pop methods:
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove(myFrag);
trans.commit();
manager.popBackStack();
Be careful when using commitNow(), the fragment that you add won't be added into backstack. See this answer and this blog post for more info.
If you want to use backstack you should use commit() with addToBackStack() instead of commitNow()
The main reason why commitNow() is not adding into backstack is commitNow() will execute the transaction synchronously, if it'll added into backstack the transaction can broke the backstack order if there are another pending asynchronous transactions.
Using Kotlin and fragment-ktx using this one:
In your gradle:
implementation "androidx.fragment:fragment-ktx:$androidXFragmentKtxVersion"
In your Fragment
childFragmentManager
.findFragmentByTag(YOUR_TAG)?.let { fragment ->
childFragmentManager.commit(allowStateLoss = true) {
remove(fragment)
}
}
If you are not using childFragmentManager use requireActivity().supportFragmentManager instead
requireActivity().supportFragmentManage
.findFragmentByTag(YOUR_TAG)?.let { fragment ->
requireActivity().supportFragmentManage.commit(allowStateLoss = true) {
remove(fragment)
}
}
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 fail to understand that why is it only me facing such a trivial issue . I googled around and couldn't find much .
My case is simple . I have a layout with a fragment .
<fragment
android:id="#+id/tabs_fragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#id/header"
android:layout_above="#+id/footer"
class="com.uae.mopw.fragments.TabsFragment" />
I need to send arguments to this fragments , but I CAN'T SEEM TO FIGURE OUT HOW .
Had I been making a fragment in the code , I would have had a chance to invoke setarguments before the fragment gets attached to the activity .
However now I dont think I can control what happens when this fragment get attached to the activity because it happens during the initialization of the activity itself .
I try randomly overriding onFragmentAttached and setting the arguments there , however I still couldn't get through with it
I get a Fragment already active exception when I try the above .
Help ?
My activity Oncreate
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String moduleName = null;
if (Utils.getLanguage(this).equals("ar")) {
setContentView(R.layout.activity_application_for_work_to_work_ar);
isArabic=true;
moduleName=ModuleNames.DISTANCE_MEASUREMENT_SERVICE_AR;
} else {
setContentView(R.layout.activity_application_for_work_to_work);
isArabic=false;
moduleName=ModuleNames.DISTANCE_MEASUREMENT_SERVICE;
}
/*init header*/
new Header(this,HeaderTypes.HEADER_INTERNAL,moduleName);
/*take out bundle from intent*/
Bundle args = this.getIntent().getBundleExtra(ModuleNames.DISTANCE_MEASUREMENT_SERVICE);
/*Obtain fragment reference*/
fragment=(TabsFragment)getFragmentManager().findFragmentById(R.id.tabs_fragment);
fragment.setArguments(new Bundle());
}
Create a static method in your fragment for instanciating:
public static FooFragment newInstance(final String someValue) {
final FooFragment fooFragment = new FooFragment();
final Bundle bundle = new Bundle();
bundle.putString("key", someValue);
fooFragment.setArguments(bundle);
return fooFragment;
}
Use FragmentTransaction for adding or replacing the fragment:
transaction.replace(R.id.fragment_foo, FooFragment.newInstance("myValue"));
The you can access the data in your fragment by calling
getArguments().getString("key");
As far as I know you can' talk setArguments after the fragment is created. Thomas's answer works fine if you're adding fragments programatically but not if you're using an xml layout like you are. In your position I'd just get the fragment through a FragmentManager during your Activity's onCreate and then call a method on it to set the values you want. It's a different way of doing things, that's why I prefer to start my fragments programatically, not through xml, as the back stack is handled nicely for you too.
I have an Android app where i'm using tabs (with ActionBarSherlock). So my main activity creates the tabs for me and from there i load in the fragment layouts.
In my MainActivity.java i create a tab (this is just a snippet):
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(
bar.newTab().setText("Fragment 1"),
MainMenuFragment.class, null);
My MainMenu.java looks like this:
public class MainMenuFragment extends SherlockFragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.mainmenu_fragment, container, false);
return view;
}
public void showMainMenu(View view)
{
Log.e("app", "olol: button!"); // never called!!
}
}
And this is mainmenu_fragment
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000" >
<Button
android:id="#+id/btnMenu"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="41dp"
android:text="#string/mainmenu"
android:onClick="showMainMenu" />
</RelativeLayout>
Now all i have to do is place the method showMainMenu(View view) somewhere. I thought this would go in the corresponding java file (MainMenuFragment.java in my case). But it only works when i put the method in the MainAvtivity.java file.
That means that all my button actions from all kinds of fragment layouts will go in one (the main) java file. Why can't i simply place it inside the java file that calls the Fragment layout..??
The short answers is (like already pointed out), you can't.
The only way to do this is by creating an onClick even listener. In the MainMenuFragment.java in the onCreate method, do something like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.scan_fragment, container, false);
Button menuButton = (Button)view.findViewById(R.id.btnMenu);
menuButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.e("app", "onclick listener");
}
});
return view;
}
You can remove the onClick attribute from the layout xml.
Now all I have to do is place the method showMainMenu() somewhere - this is wrong. Please, refer to the documentation of android:onClick :
For instance, if you specify android:onClick="sayHello", you must declare a public void sayHello(View v) method of your context (typically, your Activity).
Seems You cannot place your callback somewhere, because framework won't be able to find that callback. If You're defining it inside Activity (which is actually, a Context), it's possible for View to find it back. Actually, View.java contains the following code:
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot " + "be used within a restricted context");
}
final String handlerName = a.getString(attr);
mHandler = getContext().getClass().getMethod(handlerName, View.class);
...
mHandler.invoke(getContext(), View.this);
Seems it's the only possible way to call callback defined in layout file with current android:onClick attribute specification.
I solved this using the following:
Fragment xml contains
android:onClick="myFunction"
Activity Contains
public void myFunction(View v)
{ getSupportFragmentManager().findViewById/Tag(...).myFunction(v); }
Fragment Code can then implement as below to have access to local data
public void myFunction(View v) {...}
Hope this helps.
The gist of the issue is this: I'm trying to Launch a DialogFragment from a FragmentActivity. This DialogFragment's view contains a FrameLayout which I would like to populate with a Fragment. Basically the FragmentActivity launches the DialogFragment, then the DialogFragment populates it's FrameLayout with a Fragment. I've scoured the internet for tutorials and I've pieced together something that (in my mind) should work. However, no matter what I try I continuously get errors. This is what I have so far:
This is my FragmentActivity's layout (file name is "activity_interact"):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/activity" >
<Button
android:id="#+id/btnLaunchDialog"
style="#style/btn" />
This is my DialogFragment's layout (file name is "dialog_overview"):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/dialog" >
<FrameLayout
android:id="#+id/frameDisplay"
style="#style/frame" />
This is my Fragment's layout (file name is "fragment_stats"):
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/table" >
<TableRow style="#style/table" >
<TextView
style="#style/display"
android:gravity="right"
android:text="#string/textStr" />
</TableRow>
Here is the java code for my FragmentActivity:
public class ActivityInteract extends FragmentActivity implements
OnClickListener {
Button btnLaunchDialog;
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_interact);
btnLaunchDialog = (Button) findViewById(R.id.btnLaunchDialog);
btnLaunchDialog.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLaunchDialog:
FragmentManager fm = getSupportFragmentManager();
DialogOverview dialogOverview = new DialogOverview();
dialogOverview.show(fm, "dialog_overview");
break;
}
}
}
Here is my DialogFragment's code:
public class DialogOverview extends DialogFragment implements OnClickListener {
public DialogOverview() {
}
#Override
public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View view = li.inflate(R.layout.dialog_overview, vg);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frameDisplay, new FragmentStats());
ft.commit();
return view;
}
}
Here is my Fragment's code:
public class FragmentStats extends Fragment {
#Override
public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View view = li.inflate(R.layout.fragment_stats, vg);
return view;
}
}
And finally, here is the logcat error:
06-11 10:07:29.382: E/AndroidRuntime(30013): java.lang.IllegalArgumentException: No view found for id 0x7f060003 for fragment FragmentStats{4169c928 #1 id=0x7f060003}
I can see that it's saying that I don't have a view for the Fragment, but I do...(or do I?) I'm lost here, any help would be appreciated. Also, am I going about this the right way? Would it be more efficient to re-use a FragmentManager? (i.e. pass it from the FragmentActivity into the DialogFragment)
Update: I removed the code to load my Fragment into the DialogFragment and the DialogFragment displays without issue now. So obviously (as the logcat error suggests) there is something wrong with my Fragment itself...however, it matches examples that I've seen on the internet. Which is making me wonder: Is there an issue with nesting fragments in this way? A FragmentActivity displaying a DialogFragment that displays a Fragment makes me want to quip that "we can't go any deeper" but I don't know. Could I nest more fragments?
Actually, you CAN add nested fragment to a DialogFragment, BUT it cannot be based on a wrapped Dialog.
Instead of overriding onCreateDialog, inflate the View that contains the ViewGroup that will use the Fragment in onCreateView.
A consequence of this is that you cannot use a DialogFragment that wraps an AlertDialog - so if you want positive and negative buttons you need to manually create them in the content view.
Also, keep in mind that you cannot set the fragment in the XML. You need to declare the container view in XML and perform the fragment transaction programmatically.
public class MyDialogFragment extends DialogFragment {
[...]
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.dialog_layout, container);
if (savedInstanceState == null) {
final ChildFragment fragment = [...];
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
}
return view;
}
}