I have an Activity with several buttons and text elements. These elements reside in the Activity itself, not inside a fragment.
However, I want to be able to replace all this content with a PreferencesScreen fragment. Is this possible at all without putting the rest of the contents in my Activity that I want removed into a fragment and then call the .replace() function?
Currently I do
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new ProfileFragment())
.commit();
but this simply puts the ProfileFragment on top of my Activity. It overlays it. So I see and can interact with the ProfileFragment, but I also see the rest of the contents of the Acitvity underneath.
Again: I want to be able to replace all the content of my Activity with a PreferencesScreen fragment. Is this possible at all without putting the rest of the contents in my Activity that I want removed into a fragment and then call the .replace() function?
If you want to just hide the activity content, here's how you can set the background color of your Preference Fragment (tested with PreferenceFragmentCompat).
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setBackgroundColor(Color.WHITE);
}
Note that settings are typically opened in a new activity (as in Google apps).
If you're using PreferenceFragmentCompat, these SO answers may help you.
Styling issues: https://stackoverflow.com/a/32108439/2627680
Opening inner preference screens: https://stackoverflow.com/a/32540395/2627680
Related
I have an activity with a number of fragments. In all fragments, I get the views in onViewCreated, like this:
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
tv1 = (TextView)view.findViewById(R.id.tv1);
tv2 = (TextView)view.findViewById(R.id.tv2);
tv3 = (TextView)view.findViewById(R.id.tv3);
}
I'm not storing the textview's text in the state. One fragment extends ListFragment and I change the adapter's items and refresh the listview.
When I go back to previous fragments (I use popBackStack() because I need to use a Back button (app runs in kiosk mode), text in some fragments is lost while in others (and the listview) the changes done stay there.
I'm using replace to replace the current fragment in the frame.
EDIT
The values that are being lost are of TextViews and ImageButtons (image resource). The text in EditText views are not lost.
Why is this happening in just some fragments and how can I solve this data loss?
You have to use
android:freezesText="true"
In your layout textviews whose text you want to be preserved.
I hope it helps.
Let me see if I understand your situation:
You are showing an instance of Fragment1 which contains your TextView.
You replace your instance of Fragment1 on the screen with an instance of Fragment2.
You return to Fragment1, but when you do, the text in the TextView has been lost.
I have seen situations where this occurs because the instance of Fragment1 that contained the original TextView is not the same instance of Fragment1 that is shown the second time. If you are showing two different instances of Fragment1, the second instance will not display the text that the first instance contained in its own text view.
So take a look at the code that is managing your fragment transactions. Are you creating new instances of your fragments before showing them? If so, you'll have to keep references to the fragments that you want to retain instead of recreating them every time.
Another thing to look at would be how the TextView is being populated. Maybe the actual data is being lost somewhere.
Use Instance variables to store data inside each Fragment, if you are using replace E.g.:
String value = "";
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
tv1 = (TextView)view.findViewById(R.id.tv1);
tv1.setText(value);
}
Note: Update value variable as well whenever textview data is being updated.
So I basically have a button in 'DemosFragment' and when I click it, I want it to open another fragment (SettingsFragment), I understand now that I need an activity to fix this issue, as the button currently has an onClick method using intent/startActivity, so how would I go about creating an activity that just holds my fragment? I know that may sound weird they way I wrote it, I just started Android development, but basically I have a fragment and because I want a fragment to have a button to open another fragment, I figure I need an activity for the fragment I am trying to open, so how do I create that activity and what do I need to put in it? Thanks.
You need an activity with the following code:
public class ShowFragmentActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_fragment);
}
}
You also have to create a layout xml file called activity_show_fragment.xml in your res/layout folder:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.example.yourFragmentsClassName"
android:id="#+id/fragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
This should work for just displaying your fragment.
To launch the activity, paste this code in your button's onClick method:
Intent i = new Intent(this, ShowFragmentActivity.class);
startActivity(i);
It's always a good decision to look at the official docs: http://developer.android.com/reference/android/app/Fragment.html.
Hope that helps!
Wow! Your question requires a long answer, however is a good practice (and madatory too) that Fragments cannot communicates between each others, but they can be hosted by an Activity; in that case an Activity can manage the communication flow between them (fragments) and can be developed in several ways, Bundle, Intent and the Handler. Have a look to the ufficial Android documentation here:
http://developer.android.com/training/basics/fragments/index.html
The android docs section on building a flexible UI is a good example of how to start/load a Fragment from an Activity. In the example you will see that a FrameLayout in the Activity XML is used a the fragment container. This will be the View in which all of your fragments are displayed.
When you load your fragment with a FragmentTransaction the contents of your fragments layout will be displayed in the container View. In the above referenced example this takes place with SupportFragmentManager a class included with the android support library, for facilitating fragment transactions in earlier version of the operating system. SupportFramgnetManager requires that you extend FramentActivity and not just Activity. If you're not worried about backwards compatibility and are extending activity, not fragment activity, you can simply use getFragmentManager() instead.
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
After the initial add transaction you can switch between fragments using the replace method for your fragment transaction. Replace does exactly what it sounds like, it swaps one fragment for another. To accomplish this from within your firstframgnet use
SecondFragment secondFragment = new SecondFragment();
getActivity().getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, secondFragment).commit();
Notice that from within the fragment I used getActivity(). This allows you to reference the context of the host activity to access the fragment manager. When you are within the activity you do not need to use getactivity because the fragment manager is already accessible from that context.
In my activity I have several fullscreen fragments, each of them downloads some data from web (using an async task) and shows them to the user. The fragments are showed one at a time.
To be more specific, each of the fragment readings some urls from a sqlite database, and fetch the content before showing them in a list, if that matters. The data loading tasks can be done in the OnCreate() function.
I would like to preload all the fragment (at least starting the downloading), when I show a splash screen. Pretty much like a viewpager preload its fragments.
I am wondering how to achieve this? I tried initialize/create all the fragments in the OnCreate() function of my activity, hoping the OnCreate() of fragments could be called earlier, but the OnCreate() and OnCreateView() function of the fragments are not called until a fragment is about to show to the user.
It sounds like you need to separate your model (the data which is downloaded) from your view (the fragments). One way to do this is to start the downloading AsyncTasks in your activity, rather than starting them in each fragment. Then when the fragments are eventually displayed they can show the data which has been downloaded (or a spinner or some other indication that the download process is still executing).
Fragment's onActivityCreated(Bundle) tells the fragment that its activity has completed its own Activity.onCreate().
So your solution to this problem is initialize or create or do your stuffs which you want to preload before fragments are created, inside your Fragment's onActivityCreated(Bundle)
see documents for fragment's lifecyle
The earliest pace you can start loading is either in a static singleton or in the Application Class
What I end up doing is the following, (1) add all the fragments into the container. So they (and their view) will be created and initialized. (2) hide those not in use and only show the one I would like the user to see. (3) use FragmentTrasaction.show()/FragmentTrasaction.hide() to manipulate the visibility instead of FragmentTrasaction.add() or FragmentTrasaction.replace().
If you following this approach, be warn that all the fragments will be cached in memory. But the benefit is the switch between fragment will be fast and efficient.
I was facing the same problem and then I used this method, suppose we are having an EditText in the fragment, then we can use codes like this
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
//this method allows you to input or instantiate fragments before showing this to an activity conidering id is "editTextEditProfileFirstName"
EditText firstName = (EditText) getActivity().findViewById(R.id.editTextEditProfileFirstName);
firstName.setText("This is my first name", TextView.BufferType.EDITABLE);
super.onViewCreated(view, savedInstanceState);
}
Normally I would use a separate activity for each "screen" I wish to display, using different XML files.
However I'm working with Dynamically loading jar files into an android application, so therefore at runtime, I am not aware of how many activities there will be, or how many screens there will be.
At the moment, using java reflection, I am able to return a list of strings from the dynamically loaded java file, and draw each list item, as a separate button onto the screen. If one of these buttons is clicked, i want to be able to load a different "screen" on the stack. So when I press back from this new screen, it goes to the previous screen that called it.
Is it possible to do this without creating a new activity and passing a new intent to it and of course making relevant changes to the android manifest file?
To use blackberry functionality as an example - Is there an equivalent in android to blackberry's push and pop screens? Where the screen ur pushing/popping, would simply extent MainScreen?
If anyone has questions, or If I've been vague, please comment and I will try my best to explain myself, any help is very much appreciated.
The Android equivalent to BB's push/pop screen is startActivity()/finish(). However, you can manage your own views in a single activity by either using a container view (such as ViewSwitcher, as #hasanghaforian suggests) or by simply calling setContentView() with a new view hierarchy whenever you want to change the screen. Be aware that when you call setContentView, any view references that you obtained by calling findViewById will be stale.
In my opinion you should use Fragment. I assume that you have some piece of code where you iterate over the strings:
for(String def : definitions) {
Fragment f = new CustomFragment();
Bundle b = new Bundle();
b.putString("STRING_DEF",def);
f.setArguments(b);
fragments.add(f);
}
in above piece of code a collection of Framents is just created. Let's look at the CustomFragment implementation:
CustomFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String def = getArguments.getString("STRING_DEF");
//write code to create view
return view;
}
}
Now in your ListActivity you have to implement on click listener more or less like this like this
public void onListItemClick(ListView l, View v, int position, long id) {
FragmentManager fragMgr = getFragmentManager();
FragmentTransaction t = fragMgr.beginTransaction();
t.replace(R.id.id_of_view_place_holder_for_fragment,
fragments.get(position),"FRAGMENT_TAG");
t.commit();
}
you can use ViewSwitcher. ViewSwitcher is a ViewAnimator that switches between two views, and has a factory from which these views are created. You can either use the factory to create the views, or add them yourself. A ViewSwitcher can only have two child views, of which only one is shown at a time.Or you can use fragments.
If you refer to if it is possible to have different layouts in the same activity, the answer is yes.
Activities are independent of layouts, you don't assign the layout for an activity in the manifest, you define what layout to use in the activity calling setContentView() method from Activity class to set a layout.
So if you want to have some layouts (screens) the only thing you have to do is define various layouts and use them when you want calling setContentView(R.layout.the layout), after this call, the layout chosen will be displayed.
If you can't create the layout statically by xml, you can create it dinamically by code as needed by demand each time you want.
In addition you can have a stack of layouts, each time you need a new screen, build it, push it to the stack and call setContentView() method, when you don't need it more, pop off the stack and call setContentView() with the new layout in the top of the stack.
Hope it help you
Consider the sample app from developers.android.com
This describes using Fragments like so:
On a Phone you can use Fragment 1 on Activity A and fragment 2 on Activity B.
On a tablet you have more real estate so you use Fragment 1 and Fragment 2 on Activity A.
Great! ... But... On the first example (the one with a phone) you create an Activity with an xml file containing a single <fragment> and that's all, in the activity you only call setContentView() on that xml? That seems like a lot of redundant code (Activity, XML & Fragment to display a Fragment): Can you set a Fragment as an Activity or is a Wrapper with XML always required?
Ah, found it here
public class MainMenuHolder extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// If not already added to the Fragment manager add it. If you don't do this a new Fragment will be added every time this method is called (Such as on orientation change)
if(savedInstanceState == null)
getSupportFragmentManager().beginTransaction().add(android.R.id.content, new MainMenuFragment()).commit();
}
}
FragmentActivity allow's you to set the Fragment as the content of android.R.id.content which I assume is the android internal ID of the trunk view.
With this method you still end up with an mostly redundant activity (If all you want is the Fragment acting as the Activity). But still, half as much fluff as having an activity and an XML file acting as a container.
Any other answers would be appreciated!
The online example doesn't fill in all the blanks. I'll try to answer your questions directly:
"On the first example (the one with a phone) should you create an Activity with an xml file containing a single and an activity which only calls setContentView() on that xml and that's all?"
You've started in the right place. But there's more to it than that. There's always more than one way to solve a problem in Android but a recommended way of generating the effect of having a dynamic number of fragments based on avail. real-estate is:
Create layout XML files in /layout for the primary (default) targeted orientation/device/form-factor/SDK
Create layout XML files for the smallest-width baseline for other targeted devices. You may also want to target other orientations, SDKs, etc.
Each layout XML file will have it's own set of defined fragments
In the Activity, check to see which fragments are present.
Clearly an analogous strategy can be adopted for programmatic layouts.
In your example in the original question (from Google's docs) you could have:
layout/main.xml :: this layout would only have Fragment 1
layout-sw600dp/main.xml :: this layout would have Fragments 1, 2
Then in MainActivity.java you would check for the existence of each fragment. To do that you could use FragmentManager#findFragmentById() to have a check like: if findFragmentById() returns null for Fragment-2 then MainActivity knows the device has loaded layout/main.xml and only supports one fragment.
Stepping 'back' from the example somewhat reveals that: prior to using Fragments you might have called Activity B from Activity A with startAcitityForResult(int). In the Fragment paradigm you probably only need to have a result from Fragment 2 cause something to happen in Fragment 1, so it's reasonable to have MainActivity be the gatekeeper for that. As you expand on the example you may see that in other apps, MainActivity may need to call other activities - for whatever reason. Perhaps you're targeting a large tablet with enough real estate for 3 fragments but on a handset that needs to be 3 activites. Things can get interesting but the Fragment API is fairly powerful.
"Can you set a Fragment as an Activity or is a Wrapper always required when using fragments?"
A Fragment is not an Activity. Indeed Fragments are loaded by Activities, so yes one might say a wrapper is always required. You're touching on aother subtle aspect of Fragments. Whereas Activities behave like MVC Controllers, Fragments could be called "mini-controllers" due to their lifecycle which both resembles and executes alongside an Activity. Again, the Fragment's lifecycle is contained inside ("wrapped by") the lifecycle of the Activity managing the Fragment. I recommend becoming familiar with the Fragment lifecycle documented at http://developer.android.com/guide/topics/fundamentals/fragments.html#Lifecycle.
More generically you could create a fragment container class:
public class SingleFragmentActivity extends Activity {
public static final String FRAGMENT_NAME = "fragmentName";
public static final String FRAGMENT_ARGUMENTS = "fragmentArguments";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String fragmentName = getIntent().getStringExtra(FRAGMENT_NAME);
Fragment fragment = Fragment.instantiate(this, fragmentName);
Bundle fragmentArguments = getIntent().getBundleExtra(FRAGMENT_ARGUMENTS);
fragment.setArguments(fragmentArguments);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content,fragment, "tag").commit();
}
}
now you use this class to instantiate any fragment as a standalone activity:
public void showFragmentAsActivity() {
Intent intent = new Intent(this, SingleFragmentActivity.class);
intent.putExtra(SingleFragmentActivity.FRAGMENT_NAME, MyFragment.class.getName());
intent.putExtra(SingleFragmentActivity.FRAGMENT_ARGUMENTS,MyFragment.getArgumentsBundle("a string argument"));
startActivity(intent);
}