I'm faced with following problem:
MainActivity is mainly a ViewPager where I can slide through cards (Fragment). This cards have a placeholder ViewGroup where another content (Fragment) is placed in during runtime. The problem is that the content is placed only in one card.
MainActivity
public class MainActivity extend FragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// ...
// init ViewPager
getViewPager().setAdapter(new CardSelectionAdapter(getSupportFragmentManager()));
}
// ...
private class CardSelectionAdapter extends FragmentPagerAdapter {
// ...
public Fragment getItem(int position) {
return new CardFragment(someData);
}
}
}
CardFragment
public class CardFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.card, container, false);
rootView.findViewById(R.id.actionSheet).setVisibility(View.GONE);
return rootView;
}
public void populate() {
// ...
Fragment f;
switch (type) {
case TYPE_A: f = new ActionsAFragment(); break;
case TYPE_B: f = new ActionsBFragment(); break;
default: f = null;
}
if (f != null) {
// Here should be the main problem when adding Fragment to container/placeholder
getFragmentManager().beginTransaction().add(R.id.actionSheet, f).commit();
getView().findViewById(R.id.actionSheet).setVisibility(View.VISIBLE);
}
}
}
I think because all cards belong to MainActivity, FragmentTransaction can only add to one ViewGroup with id R.id.actionSheet.
Is there another way how I can add content to layout with same ids?
I don't see where you are calling populate. You should change populate to accept the parent view (instead of adding the fragment to R.id.actionsheet every time) and call it from onCreateView and pass it the view you just created. That way you don't have a problem with multiple views inside actionSheet with the same ID. However there is so much code missing I can't be exactly sure what to do here.
By the way, when you are dealing with nested fragments - inside of populate, you should use getChildFragmentManager() instead of getFragmentManager().
Related
I'm new Android programming. Earlier I was working with activities, where i could implement onClick on an ImageButton and go to a different activity.
Now I need to do the same but using Fragments. I have a prototype with a menu that always appear on screen and can show different activities to the user. The different lactivities would be inside this container.
Now I want to place an ImageButton inside a fragment and make that the screen shows the next fragment. But I'm confused how to do it.
I have the following components:
Activity_main(java)+activity_main.xml (with menu)
Fragment1(java)+fragment1.xml(working normal)
Inside this layout I have an ImageButton and want to show Fragment2
Fragment2(java)+fragment2.xml
How should look Fragment1 to can call Fragment2?
I will be glad if the answer could be the clearest possible because I'm new on it, and maybe I could forgot an obvious step. Thanks
Simply make a method in your activity which will always change/replace fragment when you invoke it. something like
public void updateFragment(Fragment fragment){
//add fragment replacing code here
}
in your fragment, invoke it some thing like this
((YourActivity)getActivity()).updateFragment(new YourFragment());
since, it is just an idea which works fine but still you can improve the logic.
Actually, going from one fragment to another is almost similar to going from one activity to another. There are just a few extra lines of code.
First, add a new Java class named SingleFragmentActivity which would contain the following code-
public abstract class SingleFragmentActivity extends AppCompatActivity
{
protected abstract Fragment createFragment();
#LayoutRes
protected int getLayoutResId()
{
return R.layout.activity_fragment;
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null)
{
fragment = createFragment();
fm.beginTransaction().add(R.id.fragment_container, fragment).commit();
}
}
}
Make your activities in the following format-
public class SomeActivity extends SingleFragmentActivity
{
#Override
protected Fragment createFragment()
{
return SomeFragment.newInstance();
}
}
And your fragments like this-
public class SomeFragment extends Fragment
{
public static SomeFragment newInstance()
{
return new SomeFragment();
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragment_some, container, false);
return v;
}
}
After this everything has the same code as you have for activities except for one small detail which is your onCreateView(LayoutInflater, ViewGroup, Bundle) class. This is how you would write it-
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragment_some, container, false);
mTextView = (TextView)v.findViewById(R.id.some_text);
mButton = (Button)v.findViewById(R.id.some_button);
mTextView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
check();
}
});
return v;
}
And that is it!
Hi i hope you are already aware about the fragments and their uses but still here is a brief. They are child to an activity and an activity can have more than one fragment so you can update your layout without changing activity just by changing fragments.
You can found more on fragment here : https://developer.android.com/training/basics/fragments/index.html
Back to the original problem, supposed you are in MainActivity.java and you want to load fragment in it, so you do this to load fragment first time.
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame, new Fragment1);
transaction.addToBackStack(null);
transaction.commit();
You will need this method to change fragment from another fragment, so add this in your MainActivity
public void changeFragment(Fragment fragment){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame, new Fragment1);
transaction.addToBackStack(null);
transaction.commit();
}
Now from a button click in this fragment
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((MainActivity)getActivity()).changeFragment(new Fragment2);
}
});
Hope it will help!
I am developing an application where i need to call two fragments on an activity replacing another fragment,one of the fragment will contain a Form and another fragment will contain another Form ,i don't have any idea how to do this,so any suggestion will be cordially appreciated,thanks in advance....
I want to replace Form on Click button. by R&D i come to know at this point i need to use to fragment with two layout each layout will contain Forms.
Main Activity.
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Fragment 1.
public class Fragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment1, container, false);
}
Fragment 2.
public class Fragment2 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment2, container, false);
}
}
You can try like this
FragmentTransaction ft=getSupportFragmentManager().beginTransaction();
ft.add(Home.newInstance(),"First");
ft.add(Second.newInstance(),"Second");
ft.commit();
So here you are added multiple fragment into the stack
If you are trying to replace Fragment1 with Fragment2, you replace the fragment.
https://developer.android.com/reference/android/support/v4/app/FragmentTransaction.html#replace(int,%20android.support.v4.app.Fragment)
I have 3 tabs, and I'm trying to show a different activity and layout each tab.
I am using the tabs as my app navigation, so when a has changed, the whole layout and activity need to be changed.
Actually only the layout is changing.
Since the MainActivity.java + layout has the PageAdapter in it (the PageAdapter is loading the tab's content),
I have moved my first page activity from the MainActivity.java to a new java activity called showUpdates.java. Now I'm trying to access this activity as my Tab #1 content.
If you still didn't understand what I'm asking for, The following will make it clear:
My 3 tabs are:
Java: TabFragment1.java, XML: activity_show_updates.xml
Java: TabFragment2.java, XML: tab_fragment_2.xml
Java: TabFragment3.java, XML: tab_fragment_3.xml
The TabFragment1.java:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/**
* Was trying:
* Intent intent = new Intent(...)
* startActivity(intent);
*/
return inflater.inflate(R.layout.activity_show_updates, container, false);
}
As you can see, it's moving to a new layout, activity_show_updates.xml.
What I'm trying to do is - using my showUpdates.java as the Activity of the tab_fragment_1.
I've been trying to use Intent but my app crashed when I created it at TabFragement1.java.
Anyone knows how I can make showUpdates.java as the activity of this layout?
You cannot nest activities. There are fragments for that. Just create a different fragment for each tab and use an Activity to hold all of them. If you need it, you can also create child fragments (although the support is not so good and you might have some problems, but nothing really hard to handle).
If you have viewpager working, you can do the following:
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
if(position == 0){
//return first fragment
}
else if(position == 1){
//return second fragment
}
...
}
and you can do your onCreate logic in onCreateView:
public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View yourFragment = inflater.inflate (Resource.Layout.fragment_name, container, false);
... //do stuff
return yourFragment;
}
All you need to do is define which fragments to output in your adapter
What I ought to do is change the view/layout of Fragment without creating another class for fragment on click of a button.
For example I have an activity - ContactsActivity and I have a fragment - ContactsFragment.
The Standard way of using Fragments:
From ContactsActivity I call ContactsFragment by -
getFragmentManager().beginTransaction().replace(android.R.id.content, new ContactsFragment())
.commit();
Code for setting View in ContactsFragment class -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
return rootView;
}
**Now comes how I do what I want to do ** (Change the view of fragment)
I change only the view of ContactsFragment by doing a bad kind of hack.
I change the onCreateView() shown above to this -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
//Set the view to R.layout.contacts_primary
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
//Set the view to R.layout.contacts_secondary
if(getActivity().getIntent()!=null && getActivity().getIntent().getBooleanExtra("s", false)) {
rootView = inflater.inflate(R.layout.contacts_secondary, container, false);
Log.e(tag,getActivity().getIntent().getExtras().toString());
return rootView;
}
//This is the onClickListener which again calls the ContactsActivity class,
//this time with an Intent which I used above to change the view from
//R.layout.contacts_primary to R.layout.contacts_secondary
Button button = (Button)rootView.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getActivity(), ContactsActivity.class).putExtra("s",true));
}
});
Now everything works as I want and flawlessly.
But I have a very strong feeling that either all of it is wrong and Fragments aren't supposed to work this way or I am using a hectic hack to achieve what can be done by few lines of code.
So please let me know what is it? And if there is a standard way of doing what I am trying to do.
For me passing additional argument on which base fragment decides wich layout to use seems totally ok. But there is cleaner way of doing what you want to achieve without starting another activity.
First of all pass argument to fragment by making standard static new instance method in fragment (we cannot pass this argument in constructor as android always recreates fragments using empty constructor). Something like this:
public static ContactsFragment newInstance(boolan firstView) {
ContactsFragment fragment = new ContactsFragment();
Bundle args = new Bundle();
args.putBoolean("yourArg", firstView);
fragment.setArguments(args);
return fragment;
}
Every time you have to initiate your fragment do this with this method.
Then declare interface in your fragment to communicate with your activity. Like this
public interface NewViewListener {
public void showNewView(boolen firstView);
}
Than make your activity implement it so your activity han a method where it can place new fragment in container view. In your fragments onAttach and onDetach methodsmake sure your activity implements this interface and hold reference to your activity in private NewViewListener field in your fragment. Like this:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (NewViewListener ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement NewViewListener ");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
Then in on button click method call showNewView method on your activity with whatever argument you want indicating which view you want in new fragment instance. And in your activity method showNewVew fragment in the container. Like this:
#Override
public void showNewView(boolean firstView) {
getFragmentManager().beginTransaction().replace(android.R.id.content, ContactsFragment.newInstance(firstView)
.commit();
}
In your fragments onCreateView you may get passed arguments and decide which view you want to use.
I am trying to implement fragment communication in android like the one in the android guide http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
but my application is crashing as the getSupportFragmentManager().findFragmentById() returns null. What is the issue with my implementation.
The code is given below:
The program is just to send an input text from one fragment to another fragment textView area through a button click from first fragmnet.I have an activity_main.xml and two fragment layout (two separate xml files rather than part of in activity_main.xml)
Frag1.java
public class Frag1 extends Fragment {
public Frag1(){
}
buttonClickListener buttonListener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
buttonListener = (buttonClickListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnButtonPressListener");
}
}
View myFragmentView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
myFragmentView = inflater.inflate(R.layout.frag1, container, false);
//SetValue Button
Button setValueButton = (Button) myFragmentView.findViewById(R.id.setValueButton);
setValueButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
buttonListener.onButtonPressed("Message received");
}
});
return myFragmentView;
}
}
Frag2.java
public class Frag2 extends Fragment {
View myFragmentView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
myFragmentView = inflater.inflate(R.layout.frag2, container, false);
return myFragmentView;
}
void setMessage(String msg){
TextView txt=(TextView)myFragmentView.findViewById(R.id.textView1);
txt.setText(msg);
}
}
buttonClickListener.java
public interface buttonClickListener {
public void onButtonPressed(String msg);
}
MainActivity.java
public class MainActivity extends FragmentActivity implements
ActionBar.TabListener, buttonClickListener {
SectionsPagerAdapter mSectionsPagerAdapter;
#Override
public void onButtonPressed(String msg) {
// TODO Auto-generated method stub
Frag2 fragmentObj=(Frag2) getSupportFragmentManager().findFragmentById(R.layout.frag2);
fragmentObj.setMessage(msg);
}
Please tell me where did I go wrong?
EDIT:
I am using fragment creation using the template generated by Android Plug-in eclipse IDE.
So the fragments are created using android.support.v4.app.Fragment
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch(position)
{
case 0:
return new Frag1();
case 1:
return new Frag2();
}
return fragment;
}
The codebase is kept here for reference
https://skydrive.live.com/redir?resid=D37E0F56FEC9B499!259
Try This it works for me How to put Google Maps V2 on a Fragment Using ViewPager
<fragment
android:id="#+id/map"
android:layout_width="wrap_content"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
GoogleMap mGoogleMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)).getMap();
You should have added the fragment Frag2 by calling
getSupportFragmentManager().beginTransaction().add(R.id.frag2_view, new Frag2(), "tag").commit();
at your MainActivity, where R.id.frag2_view is a layout defined in your main_layout.
To get that Fragment, you should then call
Frag2 obj = (Frag2)getSupportFragmentManager().findFragmentById(R.id.frag2_view);
passing the layout id you used to add the fragment in the main_layout.
Hope it helps.
EDIT:
Since you use a ViewPager, you should use R.id.pager as the ID.
I just tried with your example and it worked.
Frag2 fragmentObj=(Frag2) getSupportFragmentManager().findFragmentById(R.id.pager);
EDIT 2:
Despite it worked, I don't really think this is the correct way, since R.id.pager its from ViewPager and you can't find, let's say, frag4 or frag5.
Ignore my answer please. I'm not sure how to do that with ViewPager, sorry.
I was having a similar problem and here is the solution.
To get the reference to the proper fragment inside the viewpager just call:
getSupportFragmentManager().findFragmentByTag(tag);
The tag param you can build it using the following syntax: "android:switcher:pager_id:index", where pager_id is the id of the ViewPager in the XML layout and the index is the position of your fragment inside the ViewPager starting by zero. See the original response here: https://stackoverflow.com/a/7393477/2423274
Found the right solution for this question.
Hope more people see this.
Frag2 fragmentObj=(Frag2) mSectionsPagerAdapter.getItem(2);
You should use your Adapter (where you populate your fragments) as a source for get the Fragment reference.
I used this to get a child fragment from a fragment adapter...
Fragment parent = MainActivity.this.getSupportFragmentManager().findFragmentById(R.id.main_container);
if (parent instanceof PagerFragment) {
// do something with parent
Fragment child = ((PagerFragment) f).adapter.getItem(childPosition);
}