In my app the same functionality should be shared between different device-oriented layouts. For handsets, I've got an activity for performing some transactions - simultaneously I've got to implement the same functionality within a popup for tablet version.
So far I've tried putting fragment into dialog both ways: statically and dynamically. When I do it statically it moans about duplicate ID and I guess this means that Fragment is already created. On the other hand when I try to implement it dynamically it says that there's no view for the fragment. I do it like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(_layoutResID);
}
#Override
public void show() {
super.show();
FragmentManager fragmentManager = ((FragmentActivity) _context).getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
TabletLoginFragment loginFragment = new TabletLoginFragment();
fragmentTransaction.add(R.id.login_fragment_holder, loginFragment);
fragmentTransaction.commit();
}
Both ways I can't get it working - what would you recommend to place fragment inside of a Dialog element ?
Related
I have a problem with fragments. In my xml file I have a fragment already set there, I want with the click of a button replace it with another fragment. So with my code I can replace the fragment with the one that I want on the click of the button, but the first fragment wont disappear, so I can still see it under my second fragment, the code it's this:
public class MainActivity extends AppCompatActivity {
FragmentManager fragmentManager;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment,new BlankFragment2());
fragmentTransaction.addToBackStack(null).commit();
}
});
}
}
Set a background color of the root layout of the second fragment and put clickable and focusable true in xml file. It will make disappear of the first fragment and also disable the clicks of first fragment when showing another fragment.
Remove addToBackStack(null).
That stores the Fragment and keeps it attached so that calling popBackStack() will remove the top Fragment and replace it with the previous.
correctly implementing addToBackStack will help in this case and many other
addToBackStack takes an argument which is called task record TAG , To perform a transaction later on , you can use this tag to remove back stack up to a point
For more understanding read
https://medium.com/#bherbst/managing-the-fragment-back-stack-373e87e4ff62
My main application has a blanck FrameLayout the reason for this is so that I can on the go, add and replace fragments to it...
I have had no issue with this until now. I have successfully added the first fragment and the onCreate gets called etc, then in my code using a button I replace the FrameLayout with my second fragment which seems to execute, however not showing? as onActivityCreated() does not get called.
My main application calls FrameLayout xml file known as send_activity
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
sendControlFragment sendControlFragment = new sendControlFragment();
peerItemFragment fragmentList = new peerItemFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.send_activity);
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
sendControlFragment controlFragment = new sendControlFragment();
// Add the fragment to the 'fragment_container' FrameLayout
fragmentTransaction.add(R.id.fragment_container, controlFragment).commit();
}
}
The above works perfectly...
The button which I call the second fragment, I got told to initiate a new FragmentTransaction which I have done, however this does not call the onActivityCreated() and skips to fragmentList.addPeers(mService.peerList);
This is great that it is calling the addPeers() method however without the onActivityCreated() then it crashes!
public void authenticateClick(View v) {
mService.peerSearch();
peerItemFragment peerFragment = new peerItemFragment();
FragmentTransaction fragmentTransactionauthen = fragmentManager.beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
fragmentTransactionauthen.replace(R.id.fragment_container, peerFragment);
fragmentTransactionauthen.addToBackStack(null);
// Commit the transaction
fragmentTransactionauthen.commit();
//pass peer list to fragment to display
fragmentList.addPeers(mService.peerList);
}
Can you please suggest a solution as .replace currently to my knowledge doesnt call the onActivityCreated() method...
Because i was using a listfragment I had to replace my listView id to the following, as this was causing fatal error!
android:id="#android:id/list"
it now works and replaces fragments.
Suppose I am creating an Android application which has a Navigation drawer and set of fragments. When user clicks on an option in the Navigation drawer the corresponding fragment is loaded. The application has only one activity (Main activity) and it has no view.
When the application is first started which fragment gets loaded into the main activity? How does the application know which fragment to be loaded first without user interaction? How to set a custom fragment to be loaded automatically when the application starts?
Thank you
You just perform the same FragmentTransaction you use to replace the fragment on user interaction and in the onCreate() method of your Activity. But you have to check if savedInstanceState is null like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.flFragmentContainer, MainFragment.newInstance());
transaction.commit();
}
}
I'm trying to make a simple reminder app. When creating a new reminder I have the following setup:
MainEditActivity.java: 2 placeholder FrameLayouts for the following fragments:
EditNameFragment.java
custom Action Bar (Cancel, OK button)
EditText for the reminder name
CheckBox to toggle one of the following fragments
EditDateFragment.java OR EditLocationFragment.java (both have a lot of views)
MainEditActivity.java:
private LocationFragment mLocationFragment;
private DateFragment mDateFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_remainer_main); // 2 frame layouts
if (savedInstanceState == null) {
Fragment newFragment = new NewReminderFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.reminder_title_fragment, newFragment).commit();
mDateFragment = new DateFragment();
ft = getFragmentManager().beginTransaction();
ft.add(R.id.date_or_location_fragment, mDateFragment).commit();
}
}
public void onCheckBoxClick(View view)
{
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if(mLocationFragment == null)
mLocationFragment = new LocationFragment();
CheckBox checkBox = (CheckBox)findViewById(R.id.checkBoxID);
if(checkBox != null)
{
if(checkBox.isChecked()) {
fragmentTransaction.replace(R.id.date_or_location_fragment, mLocationFragment);
} else {
fragmentTransaction.replace(R.id.date_or_location_fragment, mDateFragment);
}
fragmentTransaction.commit();
}
#Override
public void handleEvent(EventInfo event) {
// here I get all the data (name, data, time, location, etc)
}
EditNameFragment.java:
private EventHandler mEventHandler;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mEventHandler = (EventHandler)activity;
}
View doneButton = actionBarButtons.findViewById(R.id.doneBtnTextID);
doneButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
createNewReminder();
}
});
private void createNewReminder()
{
// Event info object to store all information (reminder name, data, time, etc)
EventInfo eventInfo = new EventInfo();
// access either Date or Location Fragment (depending on which is visible)
// FIXME: access the fragments and get the data
// let the activity know to display the new reminder
mEventHandler.handleEvent(eventInfo);
}
Question: I started off using only 1 MainActivity with all elements and a ViewFlipper. Then I read that fragments are better to eventually adjust to tablets better. Is this the right way to lay this out?
Question: I know how to get the data from EditNameFragment.java. But how do I get the data that the user entered from EditDataFragment or EditLocationFragment?
Do I need an instance of those two Fragments in the MainActivity to access their Views? Or do I need another callback like I did with EventHandler? If yes, would the rule be to have a callback for each fragment?
I guess I'm struggling a bit with the communication flow between those components. And I know fragment to fragment communication isn't a good design
Fragments are self-contained, modular components that would make sense if presented by themselves independently of the other. The canonical example is a list of newspaper articles and the display for an article...both could be presented either independently or separately and it would make sense. In your case, I think having multiple fragments is probably unnecessary because they do not make sense independently of the other (or you would end up with a half-defined event). It seems as if you have one fragment to edit the date and one fragment to edit the name, but conceptually, the two fragments really edit one "object," which is the event. Thus, this should probably be done within one fragment with a ViewFlipper.
Have you seen this?
http://developer.android.com/training/basics/fragments/communicating.html
You need instances of all three fragments in the main activity, and you need to define interfaces for each fragment and implement them in the main activity.
I am building a one activity-multiple fragments application. I add to the backstack after every transaction. After a couple of hiding and showing fragments and then I rotate the phone, all the fragments added on the container were restored and every fragment is on top of the other.
What can be the problem? Why is my activity showing the fragments I have previously hidden?
I am thinking of hiding all the previously-hidden-now-shown fragments but is there a more 'graceful' way of doing this?
Use setRetainInstance(true) on each fragment and your problem will disappear.
Warning: setting this to true will change the Fragments life-cycle.
While setRetainInstance(true) resolves the issue, there may be cases where you don't want to use it.
To fix that, setup a boolean attribute on the Fragment and restore the visibility:
private boolean mVisible = true;
#Override
public void onCreate(Bundle _savedInstanceState) {
super.onCreate(_savedInstanceState);
if (_savedInstanceState!=null) {
mVisible = _savedInstanceState.getBoolean("mVisible");
}
if (!mVisible) {
getFragmentManager().beginTransaction().hide(this).commit();
}
// Hey! no setRetainInstance(true) used here.
}
#Override
public void onHiddenChanged(boolean _hidden) {
super.onHiddenChanged(_hidden);
mVisible = !_hidden;
}
#Override
public void onSaveInstanceState(Bundle _outState) {
super.onSaveInstanceState(_outState);
if (_outState!=null) {
_outState.putBoolean("mVisible", mVisible);
}
}
Once the configuration changes (e.g. screen orientation), the instance will be destroyed, but the Bundle will be stored and injected to the new Fragment instance.
I had the same problem. you should check source code in the function onCreateView() of your activity.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
if(savedInstanceState == null){//for the first time
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
FragmentExample fragment = new FragmentExample();
fragmentTransaction.add(R.id.layout_main, fragment);
fragmentTransaction.commit();
}else{//savedInstanceState != null
//for configuration change or Activity UI is destroyed by OS to get memory
//no need to add Fragment to container view R.id.layout_main again
//because FragmentManager supported add the existed Fragment to R.id.layout_main if R.id.layout_main is existed.
//here is one different between Fragment and View
}
}
activity_main.xml:
<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:id="#+id/layout_main">
You might want to try to use the replace() function rather than hide and show. I had the same problem when I started using Fragments and using the replace function really helped manage the Fragments better. Here is a quick example:
fragmentManager.replace(R.id.fragmentContainer, desiredFragment, DESIRED_FRAGMENT_TAG)
.addToBackStack(null)
.commit();